Please bookmark this page to avoid losing your image tool!

Image Wave Pattern 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.
function processImage(originalImg, amplitude = 10, frequency = 0.05, phase = 0, direction = "horizontal") {
    // Ensure parameters are numbers, provide defaults if conversion fails
    const numAmplitude = Number(amplitude);
    const finalAmplitude = isNaN(numAmplitude) ? 10 : numAmplitude;

    const numFrequency = Number(frequency);
    const finalFrequency = isNaN(numFrequency) ? 0.05 : numFrequency;

    const numPhase = Number(phase);
    const finalPhase = isNaN(numPhase) ? 0 : numPhase;

    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = 0;
        emptyCanvas.height = 0;
        return emptyCanvas;
    }

    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = imgWidth;
    outputCanvas.height = imgHeight;
    // { willReadFrequently: true } can improve performance of getImageData/putImageData in some browsers
    const outputCtx = outputCanvas.getContext('2d', { willReadFrequently: true });

    // Create a temporary source canvas to draw the original image
    // and reliably get its pixel data.
    const sourceCanvas = document.createElement('canvas');
    sourceCanvas.width = imgWidth;
    sourceCanvas.height = imgHeight;
    const sourceCtx = sourceCanvas.getContext('2d', { willReadFrequently: true });
    
    try {
        sourceCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error drawing original image to source canvas:", e);
        // Draw error message on the output canvas
        outputCtx.fillStyle = 'rgba(200,0,0,0.5)'; // Semi-transparent red background
        outputCtx.fillRect(0,0,imgWidth,imgHeight);
        outputCtx.font = "16px Arial";
        outputCtx.fillStyle = 'white';
        outputCtx.textAlign = 'center';
        outputCtx.fillText("Error: Could not draw original image.", imgWidth/2, imgHeight/2 - 10);
        if (e && e.message) {
             outputCtx.fillText(e.message, imgWidth/2, imgHeight/2 + 10);
        }
        return outputCanvas;
    }

    let sourceImageData;
    try {
        sourceImageData = sourceCtx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error getting ImageData from source canvas (e.g., CORS issue):", e);
        // Draw error message on the output canvas
        outputCtx.fillStyle = 'rgba(200,0,0,0.5)';
        outputCtx.fillRect(0,0,imgWidth,imgHeight);
        outputCtx.font = "16px Arial";
        outputCtx.fillStyle = 'white';
        outputCtx.textAlign = 'center';
        outputCtx.fillText("Error: Could not access image pixels.", imgWidth/2, imgHeight/2 - 20);
        outputCtx.fillText("This may be due to CORS restrictions", imgWidth/2, imgHeight/2);
        if (e && e.message) {
            outputCtx.fillText(e.message, imgWidth/2, imgHeight/2 + 20);
        }
        return outputCanvas;
    }
    
    const sourceData = sourceImageData.data;
    const outputImageData = outputCtx.createImageData(imgWidth, imgHeight);
    const outputData = outputImageData.data;

    // Bilinear interpolation helper function.
    // It captures sourceData, imgWidth, imgHeight from the outer scope for efficiency.
    function getBilinearPixel(sourceSampleX, sourceSampleY) {
        const x_floor = Math.floor(sourceSampleX);
        const y_floor = Math.floor(sourceSampleY);
        
        // Calculate fractional parts for interpolation
        const u = sourceSampleX - x_floor; 
        const v = sourceSampleY - y_floor; 

        const resultPixel = [0,0,0,0]; // RGBA array for the interpolated pixel

        // Iterate over R, G, B, A channels
        for (let channel = 0; channel < 4; channel++) {
            let interpolatedValue = 0;
            
            // Get values of the 4 surrounding pixels (p00, p10, p01, p11)
            // P00 is (x_floor, y_floor)
            const px00 = Math.max(0, Math.min(x_floor, imgWidth - 1));
            const py00 = Math.max(0, Math.min(y_floor, imgHeight - 1));
            const val00 = sourceData[(py00 * imgWidth + px00) * 4 + channel];
            interpolatedValue += val00 * (1 - u) * (1 - v);

            // P10 is (x_floor + 1, y_floor)
            const px10 = Math.max(0, Math.min(x_floor + 1, imgWidth - 1));
            const py10 = Math.max(0, Math.min(y_floor, imgHeight - 1)); 
            const val10 = sourceData[(py10 * imgWidth + px10) * 4 + channel];
            interpolatedValue += val10 * u * (1 - v);
            
            // P01 is (x_floor, y_floor + 1)
            const px01 = Math.max(0, Math.min(x_floor, imgWidth - 1)); 
            const py01 = Math.max(0, Math.min(y_floor + 1, imgHeight - 1));
            const val01 = sourceData[(py01 * imgWidth + px01) * 4 + channel];
            interpolatedValue += val01 * (1 - u) * v;

            // P11 is (x_floor + 1, y_floor + 1)
            const px11 = Math.max(0, Math.min(x_floor + 1, imgWidth - 1));
            const py11 = Math.max(0, Math.min(y_floor + 1, imgHeight - 1));
            const val11 = sourceData[(py11 * imgWidth + px11) * 4 + channel];
            interpolatedValue += val11 * u * v;
            
            resultPixel[channel] = Math.round(interpolatedValue);
        }
        return resultPixel;
    }

    // Iterate over each pixel of the destination canvas
    for (let y = 0; y < imgHeight; y++) { // y is the destination y-coordinate
        for (let x = 0; x < imgWidth; x++) { // x is the destination x-coordinate
            let sourcePixelX, sourcePixelY; // Coordinates in the source image to sample from

            if (String(direction).toLowerCase() === "vertical") {
                // Vertical waves: y-coordinate is displaced based on x and wave function
                const displacement = finalAmplitude * Math.sin(x * finalFrequency + finalPhase);
                sourcePixelX = x; 
                sourcePixelY = y - displacement; // Apply inverse displacement
            } else { // Default to horizontal waves
                // Horizontal waves: x-coordinate is displaced based on y and wave function
                const displacement = finalAmplitude * Math.sin(y * finalFrequency + finalPhase);
                sourcePixelX = x - displacement; // Apply inverse displacement
                sourcePixelY = y;
            }
            
            // Get the color from the source image using bilinear interpolation
            const newPixelColor = getBilinearPixel(sourcePixelX, sourcePixelY);
            
            const destIndex = (y * imgWidth + x) * 4;
            outputData[destIndex]     = newPixelColor[0]; // R
            outputData[destIndex + 1] = newPixelColor[1]; // G
            outputData[destIndex + 2] = newPixelColor[2]; // B
            outputData[destIndex + 3] = newPixelColor[3]; // A
        }
    }

    outputCtx.putImageData(outputImageData, 0, 0);
    return outputCanvas;
}

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 Wave Pattern Filter is an online tool that allows users to apply a wave pattern effect to images. By adjusting parameters such as amplitude, frequency, phase, and direction, users can create visually striking wave distortions in their images. This tool can be used in various contexts, such as enhancing graphic designs, creating unique visuals for social media posts, or experimenting with artistic effects for personal projects. The flexibility in parameters provides users with creative control over the intensity and orientation of the wave effect.

Leave a Reply

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