Please bookmark this page to avoid losing your image tool!

Rain On Glass Effect Adder

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, density = "100") {
    const canvas = document.createElement('canvas');
    const w = originalImg.width;
    const h = originalImg.height;
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');

    const maxDim = Math.max(w, h);
    
    // Background - wet glass effect
    ctx.filter = `blur(${maxDim * 0.015}px) brightness(0.8) contrast(1.1)`;
    ctx.drawImage(originalImg, 0, 0);
    ctx.filter = 'none';

    // Shades the drop to look 3D and lit
    function addDropShading(cx, cy, r) {
        if (r < maxDim * 0.001) return; // Skip detail on tiny droplets

        ctx.save();
        ctx.beginPath();
        ctx.arc(cx, cy, r, 0, Math.PI * 2);
        ctx.clip();

        // Inner shadow for volume
        const grad = ctx.createRadialGradient(cx - r*0.3, cy - r*0.3, r*0.2, cx, cy, r);
        grad.addColorStop(0, 'rgba(0,0,0,0.0)');
        grad.addColorStop(0.7, 'rgba(0,0,0,0.1)');
        grad.addColorStop(1, 'rgba(0,0,0,0.6)');
        ctx.fillStyle = grad;
        ctx.fill();

        // Top-left Specular highlight
        ctx.beginPath();
        ctx.arc(cx - r*0.35, cy - r*0.35, r*0.4, 0, Math.PI * 2);
        const gradLight = ctx.createRadialGradient(cx - r*0.35, cy - r*0.35, 0, cx - r*0.35, cy - r*0.35, r*0.4);
        gradLight.addColorStop(0, 'rgba(255,255,255,0.7)');
        gradLight.addColorStop(1, 'rgba(255,255,255,0)');
        ctx.fillStyle = gradLight;
        ctx.fill();
        
        // Bottom inner rim light reflection
        ctx.beginPath();
        ctx.arc(cx, cy, r*0.85, 0.15*Math.PI, 0.85*Math.PI);
        ctx.strokeStyle = 'rgba(255,255,255,0.3)';
        ctx.lineWidth = Math.max(1, r*0.1);
        ctx.lineCap = 'round';
        ctx.stroke();

        ctx.restore();
    }

    // Renders the wide-angle, upside-down "lens" of a water drop
    function drawSingleDrop(cx, cy, r) {
        if (r < 1) return;
        ctx.save();
        ctx.beginPath();
        ctx.arc(cx, cy, r, 0, Math.PI * 2);
        ctx.clip();

        // Water droplet refraction scale and flip mapping
        ctx.translate(cx, cy);
        const dropScale = 0.5;
        ctx.scale(-dropScale, -dropScale);
        ctx.translate(-cx, -cy);

        // Optimization: check intersection so we only draw necessary seamless reflection tiles
        const paintMinX = cx - r / dropScale;
        const paintMaxX = cx + r / dropScale;
        const paintMinY = cy - r / dropScale;
        const paintMaxY = cy + r / dropScale;

        // Loop to safely draw original image out of physical bounds if the lens bends far (mirrored tiling)
        for (let i = -1; i <= 1; i++) {
            const tileMinX = i * w;
            const tileMaxX = (i + 1) * w;
            if (paintMaxX < tileMinX || paintMinX > tileMaxX) continue;

            for (let j = -1; j <= 1; j++) {
                const tileMinY = j * h;
                const tileMaxY = (j + 1) * h;
                if (paintMaxY < tileMinY || paintMinY > tileMaxY) continue;

                ctx.save();
                ctx.translate(i * w, j * h);
                
                const flipH = (i !== 0);
                const flipV = (j !== 0);
                
                if (flipH) {
                    ctx.translate(w, 0);
                    ctx.scale(-1, 1);
                }
                if (flipV) {
                    ctx.translate(0, h);
                    ctx.scale(1, -1);
                }
                
                ctx.drawImage(originalImg, 0, 0, w, h);
                ctx.restore();
            }
        }

        ctx.restore();
        addDropShading(cx, cy, r);
    }

    // Draws a falling droplet and its water trail
    function drawDrop(cx, cy, r, baseDropSize) {
        if (r > baseDropSize * 2 && Math.random() > 0.4) {
            let ty = cy;
            let tLen = r * (3 + Math.random() * 4);
            let tr = r * 0.5;
            let tx = cx;
            let waver = (Math.random() - 0.5) * r * 0.5;
            
            // Build the trailing water squiggles
            while (cy - ty < tLen && tr > 0.5) {
                ty -= tr * 1.5;
                tr *= 0.85;
                tx += waver;
                waver = (Math.random() - 0.5) * r * 0.3;
                drawSingleDrop(tx, ty, tr);
            }
        }
        // Base main drop
        drawSingleDrop(cx, cy, r);
    }

    const intensity = Number(density) || 100;
    const effectiveArea = Math.min(w * h, 2000000); 
    const baseCount = Math.floor(effectiveArea / (100000 / intensity));
    const baseDropSize = Math.max(2, maxDim * 0.002);

    const drops = [];

    // Distribute small drops
    for(let i=0; i<baseCount; i++) {
        drops.push({
            x: Math.random() * w,
            y: Math.random() * h,
            r: baseDropSize + Math.random() * baseDropSize,
            type: 'small'
        });
    }

    // Distribute medium drops
    for(let i=0; i<baseCount * 0.2; i++) {
        drops.push({
            x: Math.random() * w,
            y: Math.random() * h,
            r: baseDropSize * 2 + Math.random() * baseDropSize * 2,
            type: 'med'
        });
    }

    // Distribute large drops
    for(let i=0; i<baseCount * 0.05; i++) {
        drops.push({
            x: Math.random() * w,
            y: Math.random() * h,
            r: baseDropSize * 4 + Math.random() * baseDropSize * 4,
            type: 'large'
        });
    }

    // Render smaller drops first so larger cascading drops naturally overlap their view
    drops.sort((a,b) => a.r - b.r);

    for (const d of drops) {
        if (d.type === 'small') {
            drawSingleDrop(d.x, d.y, d.r);
        } else {
            drawDrop(d.x, d.y, d.r, baseDropSize);
        }
    }

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Rain On Glass Effect Adder is an image processing tool that applies a realistic rain-streaked glass overlay to your photos. It works by blurring the background image and adding various sizes of water droplets, complete with realistic light refraction, shadows, and specular highlights. It can even simulate falling droplets and water trails for a more dynamic look. This tool is ideal for photographers, digital artists, and content creators looking to add atmosphere, mood, or a dramatic weather aesthetic to their images for social media, concept art, or cinematic compositions.

Leave a Reply

Your email address will not be published. Required fields are marked *