Please bookmark this page to avoid losing your image tool!

Image Thunderstorm Effect Generator

(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, rainDensityParam = "150", rainSpeedParam = "20") {
    // Parse arguments and provide safety bounds
    let rainIntensity = parseInt(rainDensityParam, 10);
    if (isNaN(rainIntensity)) rainIntensity = 150;
    rainIntensity = Math.max(10, Math.min(rainIntensity, 1000)); // clamp between 10 and 1000

    let rainSpeed = parseFloat(rainSpeedParam);
    if (isNaN(rainSpeed)) rainSpeed = 20.0;
    rainSpeed = Math.max(rainSpeed, 1.0);

    // Create and setup the canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width || originalImg.naturalWidth;
    canvas.height = originalImg.height || originalImg.naturalHeight;
    const ctx = canvas.getContext('2d');

    // Adapt number of drops to image resolution (Density per 100,000 pixels)
    const totalPixels = canvas.width * canvas.height;
    const numDrops = Math.floor((rainIntensity * totalPixels) / 100000);
    
    const raindrops = [];
    for (let i = 0; i < numDrops; i++) {
        raindrops.push({
            x: Math.random() * (canvas.width + canvas.height * 0.2), // allow overflow for diagonal path
            y: Math.random() * canvas.height,
            len: Math.random() * 20 + 10,
            speed: Math.random() * 5 + rainSpeed
        });
    }

    // State variables for thunderstorm & lightning
    let flashTimer = 0;
    let flashAlpha = 0;
    let timeToNextLightning = 0; // Trigger an immediate strike on the very first frame
    let lightningPaths = null;

    // Helper functions for lightning generation
    function generateLightning(width, height) {
        let bolts = [];
        let startX = Math.random() * width;
        
        function branch(x, y, endY, isMain, thickness) {
            let path = [];
            let currX = x;
            let currY = y;
            path.push({x, y, thickness});
            
            // Draw path until it reaches target Y
            while (currY < endY) {
                currX += (Math.random() - 0.5) * (isMain ? 80 : 40);
                currY += Math.random() * (isMain ? 50 : 25) + 10;
                path.push({x: currX, y: currY, thickness});
                
                // Randomly spawn smaller branches
                if (Math.random() < (isMain ? 0.2 : 0.05) && thickness > 1.5) {
                    branch(currX, currY, currY + Math.random() * 200 + 50, false, thickness * 0.6);
                }
            }
            bolts.push(path);
        }
        
        branch(startX, 0, height, true, 4 + Math.random() * 2);
        return bolts;
    }

    // Main animation loop
    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // 1. Draw base image
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

        // 2. Logic for generating lightning strikes
        if (timeToNextLightning <= 0) {
            lightningPaths = generateLightning(canvas.width, canvas.height);
            flashTimer = Math.floor(Math.random() * 15 + 10);
            timeToNextLightning = Math.random() * 150 + 100;
        }
        timeToNextLightning--;

        // Compute flicker/flash alpha state
        if (flashTimer > 0) {
            flashTimer--;
            // Flicker effect during the initial strike
            flashAlpha = Math.random() * 0.6 + 0.4;
            if (flashTimer === 0) flashAlpha = 1.0; // lock alpha to full before fadeout starts
        } else {
            // Smooth fade out
            flashAlpha -= 0.05;
            if (flashAlpha < 0) {
                flashAlpha = 0;
                lightningPaths = null;
            }
        }

        // 3. Ambient Dark Overlay (Simulation of a moody storm weather)
        // Lightens up corresponding to the lightning flash intensity
        const overlayAlpha = 0.65 - (flashAlpha * 0.6);
        ctx.fillStyle = `rgba(15, 20, 35, ${Math.max(0, overlayAlpha)})`;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // 4. Render Lightning Bolts
        if (lightningPaths && flashAlpha > 0) {
            ctx.shadowBlur = 20;
            ctx.shadowColor = 'rgba(230, 240, 255, 1)';
            ctx.lineCap = 'round';
            ctx.lineJoin = 'round';
            ctx.strokeStyle = `rgba(255, 255, 255, ${flashAlpha})`;
            
            for (let i = 0; i < lightningPaths.length; i++) {
                const path = lightningPaths[i];
                ctx.beginPath();
                ctx.lineWidth = path[0].thickness;
                for (let j = 0; j < path.length; j++) {
                    if (j === 0) ctx.moveTo(path[j].x, path[j].y);
                    else ctx.lineTo(path[j].x, path[j].y);
                }
                ctx.stroke();
            }
            ctx.shadowBlur = 0; // reset blur for next elements
        }

        // 5. General Overall Screen Flash illuminating the sky
        if (flashAlpha > 0) {
            ctx.globalCompositeOperation = 'screen';
            ctx.fillStyle = `rgba(200, 220, 255, ${flashAlpha * 0.25})`;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.globalCompositeOperation = 'source-over';
        }

        // 6. Process and Draw Rain Drops (Optimized with a single stroke)
        ctx.strokeStyle = `rgba(180, 200, 230, ${0.4 + flashAlpha * 0.3})`;
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        
        for (let i = 0; i < raindrops.length; i++) {
            let drop = raindrops[i];
            
            // Draw slanted rain depending on speed
            ctx.moveTo(drop.x, drop.y);
            ctx.lineTo(drop.x - drop.len * 0.2, drop.y + drop.len);
            
            // Move drops
            drop.y += drop.speed;
            drop.x -= drop.speed * 0.2;
            
            // Reset drop strictly outside the boundaries to seem seamless
            if (drop.y > canvas.height || drop.x < -drop.len) {
                drop.y = -drop.len - Math.random() * 50;
                drop.x = Math.random() * (canvas.width + canvas.height * 0.2);
            }
        }
        ctx.stroke();

        // Queue next frame
        requestAnimationFrame(animate);
    }
    
    // Call animate synchronously once to guarantee the first frame (base + lightning) 
    // is fully rendered to the canvas before returning it.
    animate();
    
    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 Image Thunderstorm Effect Generator is an animation tool that overlays dynamic weather effects onto your uploaded images. It simulates a moody storm atmosphere by adding falling rain drops with adjustable density and speed, accompanied by randomized lightning strikes and flickering light flashes. This tool is ideal for digital artists, content creators, or social media users looking to add dramatic, cinematic, or atmospheric motion effects to static photos and backgrounds.

Leave a Reply

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