Please bookmark this page to avoid losing your image tool!

Image Fisheye Filter Effect Creator

(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 = 0.5) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Optimization hint for getImageData heavy operations

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

    canvas.width = width;
    canvas.height = height;

    if (width === 0 || height === 0) {
        // Handles cases like unloaded or zero-size images
        // Draw a (potentially empty) image if dimensions are zero, then return.
        try {
            ctx.drawImage(originalImg, 0, 0, width, height);
        } catch (e) {
            // Log error if originalImg is not drawable (e.g., broken)
            console.error("Error drawing original image:", e);
        }
        return canvas;
    }

    // Draw the original image onto a temporary canvas to reliably get its pixel data.
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = width;
    tempCanvas.height = height;
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    tempCtx.drawImage(originalImg, 0, 0, width, height);
    const sourceImageData = tempCtx.getImageData(0, 0, width, height);
    const sourceData = sourceImageData.data;

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

    const centerX = width / 2;
    const centerY = height / 2;
    
    // The lensRadius defines the radius of the fisheye effect's circular boundary.
    // It's based on the largest inscribed circle in the image.
    const lensRadius = Math.min(centerX, centerY);

    // Clamping strength to be non-negative, as fisheye implies barrel distortion.
    // Negative strength would result in pincushion distortion.
    const effectStrength = Math.max(0, strength);

    // Helper function for bilinear interpolation of pixel colors.
    // This function forms a closure over sourceData, width, and height.
    function getPixelBilinear(x, y) {
        const x0 = Math.floor(x);
        const y0 = Math.floor(y);
        const x1 = x0 + 1;
        const y1 = y0 + 1;

        const xd = x - x0; // Fractional part of x
        const yd = y - y0; // Fractional part of y

        // Safely gets a pixel's RGBA array from sourceData, handling out-of-bounds.
        const getPixel = (px, py) => {
            if (px < 0 || px >= width || py < 0 || py >= height) {
                return [0, 0, 0, 0]; // Transparent black for pixels outside the source image
            }
            const offset = (py * width + px) * 4;
            return [
                sourceData[offset],     // R
                sourceData[offset + 1], // G
                sourceData[offset + 2], // B
                sourceData[offset + 3]  // A
            ];
        };

        const c00 = getPixel(x0, y0); // Color at (x0, y0) - top-left
        const c10 = getPixel(x1, y0); // Color at (x1, y0) - top-right
        const c01 = getPixel(x0, y1); // Color at (x0, y1) - bottom-left
        const c11 = getPixel(x1, y1); // Color at (x1, y1) - bottom-right

        const resultColor = [0, 0, 0, 0];
        for (let i = 0; i < 4; i++) { // Iterate over R, G, B, A channels
            // Interpolate horizontally for top row and bottom row
            const topInterpolation = c00[i] * (1 - xd) + c10[i] * xd;
            const bottomInterpolation = c01[i] * (1 - xd) + c11[i] * xd;
            // Interpolate vertically between the two horizontal results
            resultColor[i] = topInterpolation * (1 - yd) + bottomInterpolation * yd;
        }
        return resultColor;
    }

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const idx = (y * width + x) * 4; // Current pixel's index in outputData

            if (effectStrength === 0) {
                // If strength is zero, no fisheye effect; copy original pixel.
                // Using getPixelBilinear ensures sub-pixel accuracy if (x,y) were not integers,
                // but here (x,y) are loop integers, so it effectively samples sourceData[idx].
                const color = getPixelBilinear(x, y);
                outputData[idx]     = color[0];
                outputData[idx + 1] = color[1];
                outputData[idx + 2] = color[2];
                outputData[idx + 3] = color[3];
                continue;
            }

            // Calculate current pixel's offset from the image center
            const dx_abs = x - centerX;
            const dy_abs = y - centerY;

            // Normalize coordinates based on lensRadius.
            // (norm_dx_circ, norm_dy_circ) are coordinates where the edge of the lens is at radius 1.
            const norm_dx_circ = (lensRadius === 0) ? 0 : dx_abs / lensRadius;
            const norm_dy_circ = (lensRadius === 0) ? 0 : dy_abs / lensRadius;
            
            // Calculate the distance of the destination pixel from the center in normalized lens coordinates.
            const r_dest_norm = Math.sqrt(norm_dx_circ * norm_dx_circ + norm_dy_circ * norm_dy_circ);

            if (r_dest_norm > 1.0) { 
                // Pixel is outside the circular fisheye lens area. Make it transparent black.
                outputData[idx]     = 0; // R
                outputData[idx + 1] = 0; // G
                outputData[idx + 2] = 0; // B
                outputData[idx + 3] = 0; // A (transparent)
                continue;
            }

            let srcX, srcY; // Coordinates of the source pixel to sample from

            if (r_dest_norm === 0) {
                // Center pixel maps to itself.
                srcX = centerX;
                srcY = centerY;
            } else {
                // Fisheye distortion formula:
                // factor = r_src_norm / r_dest_norm
                // This factor modifies the radius:
                // - At center (r_dest_norm=0, handled by else block or r_dest_norm === 0 check): Magnification is (effectStrength + 1)
                // - At lens edge (r_dest_norm=1): Magnification is (0.5 * effectStrength + 1)
                // The center is magnified more than the edge, creating the bulge.
                const factor = (-0.5 * effectStrength * r_dest_norm + effectStrength + 1.0);
                
                // Calculate source normalized coordinates (still relative to lensRadius=1 system)
                const src_norm_dx = norm_dx_circ * factor;
                const src_norm_dy = norm_dy_circ * factor;

                // Convert source normalized coordinates back to absolute pixel coordinates in the source image
                const src_dx_abs = src_norm_dx * lensRadius;
                const src_dy_abs = src_norm_dy * lensRadius;

                srcX = centerX + src_dx_abs;
                srcY = centerY + src_dy_abs;
            }
            
            // Get the color from the calculated source coordinates using bilinear interpolation.
            const color = getPixelBilinear(srcX, srcY);
            outputData[idx]     = color[0];
            outputData[idx + 1] = color[1];
            outputData[idx + 2] = color[2];
            outputData[idx + 3] = color[3];
        }
    }

    // Put the processed pixel data onto the canvas.
    ctx.putImageData(outputImageData, 0, 0);
    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 Fisheye Filter Effect Creator is a tool designed to apply a fisheye distortion effect to images. This filter creates a spherical, bulging effect that can be adjusted for intensity, allowing users to enhance their photos with a unique visual style. Real-world use cases include creating eye-catching graphics for social media, generating fun images for artistic projects, or adding a creative twist to standard photography. This effect is particularly popular in creating immersive visuals, digital art, and altering landscape photos to give them a dramatic appearance.

Leave a Reply

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