Please bookmark this page to avoid losing your image tool!

Image Lava Lamp Filter

(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.
async function processImage(originalImg, numBlobs = 5, distortionStrength = 30, blobSize = 60, complexity = 0.05) {
    const width = originalImg.width;
    const height = originalImg.height;

    const destCanvas = document.createElement('canvas');
    destCanvas.width = width;
    destCanvas.height = height;
    const destCtx = destCanvas.getContext('2d');

    // Draw original image to a source canvas to easily access its pixels
    const srcCanvas = document.createElement('canvas');
    srcCanvas.width = width;
    srcCanvas.height = height;
    const srcCtx = srcCanvas.getContext('2d');
    srcCtx.drawImage(originalImg, 0, 0, width, height);
    const srcImageData = srcCtx.getImageData(0, 0, width, height);
    const srcData = srcImageData.data;

    const outputImageData = destCtx.createImageData(width, height);
    const outputData = outputImageData.data;

    const blobs = [];
    const safeNumBlobs = Math.max(0, numBlobs); // Ensure numBlobs is not negative
    const effectiveBlobSize = Math.max(1, blobSize); // Ensure blobSize is at least 1 for radius calculation

    for (let i = 0; i < safeNumBlobs; i++) {
        blobs.push({
            x: Math.random() * width,
            y: Math.random() * height,
            radius: effectiveBlobSize * (0.75 + Math.random() * 0.5), // Add some variation, ensure positive
            phaseX: Math.random() * Math.PI * 2,
            phaseY: Math.random() * Math.PI * 2,
        });
    }

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            let totalDX = 0;
            let totalDY = 0;

            for (const blob of blobs) {
                const distX = x - blob.x;
                const distY = y - blob.y;
                const distanceSquared = distX * distX + distY * distY;

                // If blob.radius is very small, distanceSquared could be much larger, making falloff tiny.
                // Avoid division by zero if radius somehow became zero (already protected by effectiveBlobSize).
                // Gaussian falloff: exp(-d^2 / (2 * sigma^2)). Let sigma be blob.radius.
                const falloff = Math.exp(-distanceSquared / (2 * blob.radius * blob.radius));

                const angle = Math.atan2(distY, distX); // Angle from blob center to pixel
                const dist = Math.sqrt(distanceSquared); // Distance from blob center to pixel

                // Swirling displacement.
                // (dist * complexity) creates ripple patterns.
                // blob.phaseX/Y gives each blob a unique swirl character.
                totalDX += falloff * distortionStrength * Math.sin(angle + blob.phaseX + dist * complexity);
                totalDY += falloff * distortionStrength * Math.cos(angle + blob.phaseY + dist * complexity);
            }

            const srcX = x + totalDX;
            const srcY = y + totalDY;

            const idx = (y * width + x) * 4; // Index for the destination pixel

            // Bilinear interpolation for smoother results
            const x_floor = Math.floor(srcX);
            const y_floor = Math.floor(srcY);
            const x_frac = srcX - x_floor;
            const y_frac = srcY - y_floor;

            // Check if the 4 pixels needed for interpolation are within bounds
            if (x_floor >= 0 && x_floor < width - 1 && y_floor >= 0 && y_floor < height - 1) {
                const p00_base_idx = (y_floor * width + x_floor) * 4;
                const p10_base_idx = (y_floor * width + (x_floor + 1)) * 4;
                const p01_base_idx = ((y_floor + 1) * width + x_floor) * 4;
                const p11_base_idx = ((y_floor + 1) * width + (x_floor + 1)) * 4;

                for (let channel = 0; channel < 4; channel++) { // R, G, B, A
                    const c00 = srcData[p00_base_idx + channel];
                    const c10 = srcData[p10_base_idx + channel];
                    const c01 = srcData[p01_base_idx + channel];
                    const c11 = srcData[p11_base_idx + channel];

                    // Interpolate top row
                    const top_val = c00 * (1 - x_frac) + c10 * x_frac;
                    // Interpolate bottom row
                    const bottom_val = c01 * (1 - x_frac) + c11 * x_frac;
                    // Interpolate vertically
                    const final_val = top_val * (1 - y_frac) + bottom_val * y_frac;
                    
                    outputData[idx + channel] = final_val;
                }
            } else {
                // Fallback for out-of-bounds or edge pixels: use nearest neighbor clamping
                const clampedSrcX = Math.max(0, Math.min(width - 1, srcX));
                const clampedSrcY = Math.max(0, Math.min(height - 1, srcY));
                
                const nearestX = Math.floor(clampedSrcX);
                const nearestY = Math.floor(clampedSrcY);
                
                const src_pixel_idx = (nearestY * width + nearestX) * 4;
                
                outputData[idx + 0] = srcData[src_pixel_idx + 0]; // R
                outputData[idx + 1] = srcData[src_pixel_idx + 1]; // G
                outputData[idx + 2] = srcData[src_pixel_idx + 2]; // B
                outputData[idx + 3] = srcData[src_pixel_idx + 3]; // A
            }
        }
    }

    destCtx.putImageData(outputImageData, 0, 0);
    return destCanvas;
}

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 Lava Lamp Filter is a creative online tool that transforms your images by applying a visually striking lava lamp effect. Users can create unique designs by adjusting parameters such as the number and size of swirling blobs, distortion strength, and complexity of the wave patterns. This tool is perfect for artists, designers, and anyone looking to add a fun, psychedelic flair to their visuals, making it suitable for social media posts, graphic design projects, and personal artwork.

Leave a Reply

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