Please bookmark this page to avoid losing your image tool!

Image Underwater Distortion Filter Effect Tool

(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, strength = 10, frequency = 0.05, tint = 'rgba(0, 80, 150, 0.3)', blurAmount = 2) {
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;

    if (width === 0 || height === 0) {
        console.warn("Image has zero dimensions. It might not be loaded yet or is invalid.");
        const placeholderCanvas = document.createElement('canvas');
        placeholderCanvas.width = Math.max(100, width); // Use original dimensions if >0, else 100
        placeholderCanvas.height = Math.max(100, height);
        const phCtx = placeholderCanvas.getContext('2d');
        phCtx.fillStyle = '#dddddd';
        phCtx.fillRect(0, 0, placeholderCanvas.width, placeholderCanvas.height);
        phCtx.fillStyle = 'red';
        phCtx.font = 'bold 14px Arial';
        phCtx.textAlign = 'center';
        phCtx.textBaseline = 'middle';
        phCtx.fillText("Image Invalid/Not Loaded", placeholderCanvas.width / 2, placeholderCanvas.height / 2);
        return placeholderCanvas;
    }
    
    // 1. Create a source canvas to draw the original image and get its pixel data
    const srcCanvas = document.createElement('canvas');
    srcCanvas.width = width;
    srcCanvas.height = height;
    // Add { willReadFrequently: true } as an optimization hint for getImageData
    const srcCtx = srcCanvas.getContext('2d', { willReadFrequently: true }); 
    srcCtx.drawImage(originalImg, 0, 0, width, height);
    
    let srcImageData;
    try {
        srcImageData = srcCtx.getImageData(0, 0, width, height);
    } catch (e) {
        console.error("Failed to get image data from source canvas.", e);
        // This error often occurs due to CORS policy if the image is from a different origin.
        // Return a canvas with the original image and an error message.
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = width;
        errorCanvas.height = height;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.drawImage(originalImg, 0, 0, width, height); // Draw the original image as a fallback
        
        // Overlay a semi-transparent red box and an error message
        errCtx.fillStyle = 'rgba(255, 0, 0, 0.6)';
        errCtx.fillRect(0, 0, width, height);
        errCtx.fillStyle = 'white';
        errCtx.font = `bold ${Math.min(16, width / 15)}px Arial`; // Adjust font size based on image width
        errCtx.textAlign = 'center';
        errCtx.textBaseline = 'middle';
        errCtx.fillText('Error: Could not process image.', width / 2, height / 2 - 10);
        errCtx.font = `${Math.min(14, width / 20)}px Arial`;
        errCtx.fillText('(Possibly CORS security issue)', width / 2, height / 2 + 10);
        return errorCanvas;
    }
    const srcData = srcImageData.data;

    // 2. Create a destination canvas for the processed image
    const destCanvas = document.createElement('canvas');
    destCanvas.width = width;
    destCanvas.height = height;
    const destCtx = destCanvas.getContext('2d');
    const destImageData = destCtx.createImageData(width, height);
    const destData = destImageData.data;

    // 3. Apply underwater distortion effect (pixel manipulation)
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            // Calculate wave-like displacements for X and Y coordinates
            // The constants (e.g., 0.3, 0.4) and use of sin/cos create irregular ripples.
            const waveXTerm = y * frequency + x * frequency * 0.3; // Adjust 0.3 to change wave shape
            const waveYTerm = x * frequency + y * frequency * 0.4; // Adjust 0.4 to change wave shape
            
            const offsetX = strength * Math.sin(waveXTerm);
            const offsetY = strength * Math.cos(waveYTerm); // Use cosine for Y for a different phase/pattern

            // Calculate the source pixel coordinates after applying displacement
            const srcX = x + offsetX;
            const srcY = y + offsetY;

            // Round to the nearest integer for source pixel (nearest neighbor sampling)
            const iSrcX = Math.round(srcX);
            const iSrcY = Math.round(srcY);

            // Clamp coordinates to ensure they are within the image boundaries
            const clampedX = Math.max(0, Math.min(width - 1, iSrcX));
            const clampedY = Math.max(0, Math.min(height - 1, iSrcY));

            // Calculate array indices for source and destination pixel data
            const srcIndex = (clampedY * width + clampedX) * 4; // 4 bytes per pixel (R,G,B,A)
            const destIndex = (y * width + x) * 4;

            // Copy RGBA values from the source pixel to the destination pixel
            destData[destIndex]     = srcData[srcIndex];     // Red
            destData[destIndex + 1] = srcData[srcIndex + 1]; // Green
            destData[destIndex + 2] = srcData[srcIndex + 2]; // Blue
            destData[destIndex + 3] = srcData[srcIndex + 3]; // Alpha (transparency)
        }
    }
    // Put the distorted pixel data onto the destination canvas
    destCtx.putImageData(destImageData, 0, 0);

    // 4. Apply blur (if blurAmount is greater than 0)
    // Canvas filters apply to drawing operations, not directly to ImageData.
    // So, we draw the current destCanvas (distorted image) to a temporary canvas,
    // then draw it back to destCanvas with the blur filter applied.
    if (blurAmount > 0) {
        const tempCanvasForBlur = document.createElement('canvas');
        tempCanvasForBlur.width = width;
        tempCanvasForBlur.height = height;
        const tempCtxForBlur = tempCanvasForBlur.getContext('2d');
        
        // Draw the current (distorted) content of destCanvas to the temporary canvas
        tempCtxForBlur.drawImage(destCanvas, 0, 0);
        
        // Clear destCanvas, set the blur filter, and draw the image from tempCanvas back to destCanvas
        destCtx.clearRect(0, 0, width, height); // Clear before drawing blurred image
        destCtx.filter = `blur(${blurAmount}px)`;
        destCtx.drawImage(tempCanvasForBlur, 0, 0);
        // Reset the filter to 'none' so it doesn't affect subsequent drawing operations (like tint)
        destCtx.filter = 'none'; 
    }

    // 5. Apply tint overlay
    // Check if the tint parameter is a valid string and not empty or 'none'
    if (tint && typeof tint === 'string' && tint.trim() !== '' && tint.toLowerCase() !== 'none') {
        // The `fillRect` method with an RGBA color string for `fillStyle` will blend the color
        // over the existing content using the default 'source-over' composite operation.
        destCtx.fillStyle = tint;
        destCtx.fillRect(0, 0, width, height);
    }

    // 6. Return the fully processed canvas
    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 Underwater Distortion Filter Effect Tool allows users to apply a creative underwater distortion effect to their images. By adjusting parameters such as distortion strength, frequency, tint, and blur amount, users can achieve various artistic effects that mimic the appearance of being viewed underwater. This tool is useful for graphic designers, content creators, and anyone looking to enhance their images with unique visual styles, or for those who want to create eye-catching social media posts or artwork.

Leave a Reply

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