Please bookmark this page to avoid losing your image tool!

Image Wormhole Tunnel 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.
async function processImage(originalImg, strength = 2.0, swirl = 0.5) {

    // Helper function for bilinear interpolation
    // Fetches pixel data from (sampleX, sampleY) using bilinear interpolation.
    // Handles boundary conditions by clamping to edge.
    function getBilinearPixel(imgPixelData, imgWidth, imgHeight, sampleX, sampleY) {
        const x0 = Math.floor(sampleX);
        const y0 = Math.floor(sampleY);
        const x1 = x0 + 1;
        const y1 = y0 + 1;

        // Fractional parts for interpolation
        const dx = sampleX - x0;
        const dy = sampleY - y0;
        const invDx = 1 - dx;
        const invDy = 1 - dy;

        const resultRGBA = [0, 0, 0, 0]; // R, G, B, A

        for (let channel = 0; channel < 4; channel++) { // Iterate over R, G, B, A channels
            // Clamp coordinates to be within image bounds for each of the 4 surrounding pixels
            const clamped_y0 = Math.max(0, Math.min(imgHeight - 1, y0));
            const clamped_y1 = Math.max(0, Math.min(imgHeight - 1, y1));
            const clamped_x0 = Math.max(0, Math.min(imgWidth - 1, x0));
            const clamped_x1 = Math.max(0, Math.min(imgWidth - 1, x1));

            // Get pixel values of the four surrounding points
            const P00 = imgPixelData[(clamped_y0 * imgWidth + clamped_x0) * 4 + channel]; // Top-left
            const P10 = imgPixelData[(clamped_y0 * imgWidth + clamped_x1) * 4 + channel]; // Top-right
            const P01 = imgPixelData[(clamped_y1 * imgWidth + clamped_x0) * 4 + channel]; // Bottom-left
            const P11 = imgPixelData[(clamped_y1 * imgWidth + clamped_x1) * 4 + channel]; // Bottom-right
            
            // Bilinear interpolation formula
            const topInterpolation = P00 * invDx + P10 * dx;
            const bottomInterpolation = P01 * invDx + P11 * dx;
            
            resultRGBA[channel] = topInterpolation * invDy + bottomInterpolation * dy;
        }
        return resultRGBA;
    }

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

    if (width === 0 || height === 0) {
        // Return an empty canvas or handle error if image dimensions are invalid
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = 0;
        emptyCanvas.height = 0;
        console.warn("Image has zero width or height.");
        return emptyCanvas;
    }

    // Create an input canvas to draw the original image and get its pixel data
    const inputCanvas = document.createElement('canvas');
    inputCanvas.width = width;
    inputCanvas.height = height;
    const inputCtx = inputCanvas.getContext('2d', { willReadFrequently: true }); // Hint for optimization
    inputCtx.drawImage(originalImg, 0, 0, width, height);
    
    let srcImageData;
    try {
        srcImageData = inputCtx.getImageData(0, 0, width, height);
    } catch (e) {
        // This can happen if the canvas is tainted (e.g., cross-origin image without CORS)
        console.error("Error getting image data from input canvas:", e);
        // As a fallback, return a canvas with the original image drawn (no filter)
        const fallbackCanvas = document.createElement('canvas');
        fallbackCanvas.width = width;
        fallbackCanvas.height = height;
        const fallbackCtx = fallbackCanvas.getContext('2d');
        fallbackCtx.drawImage(originalImg, 0, 0, width, height);
        return fallbackCanvas;
    }
    const srcData = srcImageData.data;

    // Create an output canvas to draw the processed image
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const outputCtx = outputCanvas.getContext('2d');
    const destImageData = outputCtx.createImageData(width, height);
    const destData = destImageData.data;

    // Effect parameters calculation
    const centerX = width / 2;
    const centerY = height / 2;
    
    // maxDist is the distance from the center to a corner, used for normalization.Ensures normalizedDist <= 1.
    const maxDist = Math.hypot(centerX, centerY); 
    if (maxDist === 0) { // Handle 1x1 image case or similar where maxDist could be 0
        outputCtx.putImageData(srcImageData, 0, 0); // No distortion possible, return original
        return outputCanvas;
    }


    // Strength parameter controls the intensity of the radial pinch.
    // Must be >= 1.0. strength < 1.0 would cause a bulge effect instead of a tunnel.
    const effectiveStrength = Math.max(1.0, strength);

    // Iterate over each pixel in the destination image
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const dXcen = x - centerX; // Delta X from center
            const dYcen = y - centerY; // Delta Y from center

            // Convert destination pixel's Cartesian coordinates to polar coordinates
            const distFromCenter = Math.hypot(dXcen, dYcen); // Distance from center for current (x,y)
            const angle = Math.atan2(dYcen, dXcen);         // Angle for current (x,y)

            // Normalize distance: 0 at center, 1 at corners (if maxDist is hypot to corner)
            const normalizedDist = distFromCenter / maxDist;

            // Apply radial distortion (tunnel pinch effect)
            // The exponent `1.0 / effectiveStrength` determines the distortion:
            // - If effectiveStrength = 1.0, exponent = 1.0, no change in radius.
            // - If effectiveStrength > 1.0 (e.g., 2.0), exponent < 1.0 (e.g., 0.5).
            //   For `normalizedDist` between 0 and 1, `pow(normalizedDist, exponent)` will be >= `normalizedDist`.
            //   This means we sample from a larger radius in the source image, creating a pinch/zoom-out effect.
            let newNormalizedDist = Math.pow(normalizedDist, 1.0 / effectiveStrength);
            
            const sourceRadius = newNormalizedDist * maxDist;
            
            // Apply angular distortion (swirl effect)
            // The swirl effect increases with the distance from the center.
            const swirlAngleOffset = swirl * normalizedDist; // Amount of swirl proportional to normalized distance
            const sourceAngle = angle + swirlAngleOffset;

            // Convert the modified polar coordinates (sourceRadius, sourceAngle) back to Cartesian coordinates
            // These are relative to the center.
            const sourceXRel = sourceRadius * Math.cos(sourceAngle);
            const sourceYRel = sourceRadius * Math.sin(sourceAngle);

            // Absolute source coordinates from which to sample the pixel
            const sx = centerX + sourceXRel;
            const sy = centerY + sourceYRel;

            // Get the pixel color from the source image using bilinear interpolation
            const pixelColor = getBilinearPixel(srcData, width, height, sx, sy);
            
            // Set the destination pixel
            const destIdx = (y * width + x) * 4;
            destData[destIdx]     = pixelColor[0]; // R
            destData[destIdx + 1] = pixelColor[1]; // G
            destData[destIdx + 2] = pixelColor[2]; // B
            destData[destIdx + 3] = pixelColor[3]; // A
        }
    }

    // Put the modified pixel data onto the output canvas
    outputCtx.putImageData(destImageData, 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 Wormhole Tunnel Filter Effect Tool allows users to apply a unique visual distortion effect to images. This tool simulates a tunnel or wormhole appearance, which involves a radial pinch and optional swirl effect, enhancing the depth and dimensional feel of the image. Users can adjust the intensity of the effect with parameters for strength and swirl, catering to creative needs for graphic design, art projects, or social media content where stylized images are desired.

Leave a Reply

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