Please bookmark this page to avoid losing your image tool!

Image Lens Whacking 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, aberration = 10, centerExposure = 1.2, exposureRadius = 0.3, vignetteStrength = 0.7, vignetteFalloff = 2.0) {
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } can be an optimization hint for browsers
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); 
    
    const w = originalImg.naturalWidth || originalImg.width;
    const h = originalImg.naturalHeight || originalImg.height;
    canvas.width = w;
    canvas.height = h;

    ctx.drawImage(originalImg, 0, 0, w, h);
    
    let sourceImageData;
    try {
        sourceImageData = ctx.getImageData(0, 0, w, h);
    } catch (e) {
        // This can happen if the image is cross-origin and the canvas becomes tainted.
        console.error("Error getting ImageData: ", e);
        // Depending on requirements, you might throw an error or return the original image drawn on canvas.
        // For this exercise, we'll throw an error as processing isn't possible.
        throw new Error("Could not get ImageData from canvas. Ensure the image is CORS-enabled if from a different origin.");
    }
    
    const sourceData = sourceImageData.data;
    const outputImageData = ctx.createImageData(w, h);
    const outputData = outputImageData.data;

    const centerX = w / 2;
    const centerY = h / 2;
    // maxDist is the distance from the center to a corner, used for normalization
    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY); 

    // Bilinear interpolation helper function
    // Samples a color from imageData at fractional coordinates (x, y)
    function getPixelBilinear(data, imgWidth, imgHeight, x, y) {
        // Clamp coordinates to be within the image bounds [0, width-1] and [0, height-1]
        const x_clamped = Math.max(0, Math.min(x, imgWidth - 1));
        const y_clamped = Math.max(0, Math.min(y, imgHeight - 1));

        const x_floor = Math.floor(x_clamped);
        const y_floor = Math.floor(y_clamped);
        
        // Determine ceil coordinates, ensuring they don't exceed image boundaries
        const x_ceil = Math.min(imgWidth - 1, x_floor + 1);
        const y_ceil = Math.min(imgHeight - 1, y_floor + 1);
        
        const fx = x_clamped - x_floor; // Fractional part of x
        const fy = y_clamped - y_floor; // Fractional part of y
        const fx1 = 1 - fx;
        const fy1 = 1 - fy;

        // Weights for the four neighboring pixels
        const w1 = fx1 * fy1; // Weight for (x_floor, y_floor)
        const w2 = fx  * fy1; // Weight for (x_ceil, y_floor)
        const w3 = fx1 * fy;  // Weight for (x_floor, y_ceil)
        const w4 = fx  * fy;  // Weight for (x_ceil, y_ceil)

        // Indices of the four neighboring pixels in the imageData array
        const idx1 = (y_floor * imgWidth + x_floor) * 4;
        const idx2 = (y_floor * imgWidth + x_ceil) * 4;
        const idx3 = (y_ceil  * imgWidth + x_floor) * 4;
        const idx4 = (y_ceil  * imgWidth + x_ceil) * 4;

        // Interpolate R, G, B, A channels
        const r = data[idx1] * w1 + data[idx2] * w2 + data[idx3] * w3 + data[idx4] * w4;
        const g = data[idx1+1] * w1 + data[idx2+1] * w2 + data[idx3+1] * w3 + data[idx4+1] * w4;
        const b = data[idx1+2] * w1 + data[idx2+2] * w2 + data[idx3+2] * w3 + data[idx4+2] * w4;
        const a = data[idx1+3] * w1 + data[idx2+3] * w2 + data[idx3+3] * w3 + data[idx4+3] * w4;

        return [r, g, b, a];
    }
    
    for (let y_coord = 0; y_coord < h; y_coord++) {
        for (let x_coord = 0; x_coord < w; x_coord++) {
            const currentPixelIndex = (y_coord * w + x_coord) * 4; 

            const dx = x_coord - centerX;
            const dy = y_coord - centerY;
            const dist = Math.sqrt(dx * dx + dy * dy);
            
            // Normalized distance from center (0 = center, 1 = corner)
            // Avoid division by zero for extremely small images (e.g., 1x1 pixel)
            const normDist = (maxDist === 0) ? 0 : (dist / maxDist); 

            // 1. Chromatic Aberration
            // The amount of channel separation scales with distance from the center
            const currentAberrationOffset = aberration * normDist;
            
            let R_ab, G_ab, B_ab;
            
            // Apply aberration if the effect is strong_ab enough and offset is noticeable
            if (aberration > 0 && currentAberrationOffset > 0.01) { 
                const angle = Math.atan2(dy, dx); // Angle from center to current pixel
                const cosAngle = Math.cos(angle);
                const sinAngle = Math.sin(angle);

                // Red channel shifted radially outwards
                const rSampleX = x_coord + currentAberrationOffset * cosAngle;
                const rSampleY = y_coord + currentAberrationOffset * sinAngle;
                
                // Blue channel shifted radially inwards
                const bSampleX = x_coord - currentAberrationOffset * cosAngle;
                const bSampleY = y_coord - currentAberrationOffset * sinAngle;
                
                // Sample R, G, B channels from their respective (potentially shifted) locations
                // Green channel is sampled from the original pixel location for relative sharpness
                const [rVal] = getPixelBilinear(sourceData, w, h, rSampleX, rSampleY);
                const [gValFromSource] = getPixelBilinear(sourceData, w, h, x_coord, y_coord); // Get G from original position
                const [_, __, bActualVal] = getPixelBilinear(sourceData, w, h, bSampleX, bSampleY); // Get B from its shifted position
                
                R_ab = rVal;
                G_ab = gValFromSource; 
                B_ab = bActualVal;
            } else { // No significant aberration, use original pixel colors
                R_ab = sourceData[currentPixelIndex];
                G_ab = sourceData[currentPixelIndex + 1];
                B_ab = sourceData[currentPixelIndex + 2];
            }
            const A_orig = sourceData[currentPixelIndex + 3]; // Preserve original alpha


            // 2. Center Exposure Boost
            let exposureFactor = 1.0;
            // Apply brightness boost if centerExposure is not 1 (no change) and within exposureRadius
            if (centerExposure !== 1.0 && exposureRadius > 0 && normDist < exposureRadius) {
                // Linear falloff of exposure from `centerExposure` at the very center 
                // down to 1.0 (no change) at `exposureRadius`
                exposureFactor = centerExposure - (centerExposure - 1.0) * (normDist / exposureRadius);
            }
            let R_exp = R_ab * exposureFactor;
            let G_exp = G_ab * exposureFactor;
            let B_exp = B_ab * exposureFactor;

            // 3. Vignette
            let vignetteMultiplier = 1.0;
            if (vignetteStrength > 0) {
                 // `vignetteStrength` (0-1): 0 = no vignette, 1 = black edges.
                 // `vignetteFalloff`: controls how quickly the vignette effect transitions. Higher values mean sharper/faster falloff.
                const vignetteEffectAmount = vignetteStrength * Math.pow(normDist, vignetteFalloff);
                vignetteMultiplier = 1.0 - vignetteEffectAmount; // Factor to multiply color by
            }

            let R_final = R_exp * vignetteMultiplier;
            let G_final = G_exp * vignetteMultiplier;
            let B_final = B_exp * vignetteMultiplier;

            // Write final clamped RGB values and original Alpha to output
            outputData[currentPixelIndex]     = Math.max(0, Math.min(255, R_final));
            outputData[currentPixelIndex + 1] = Math.max(0, Math.min(255, G_final));
            outputData[currentPixelIndex + 2] = Math.max(0, Math.min(255, B_final));
            outputData[currentPixelIndex + 3] = A_orig;
        }
    }

    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 Lens Whacking Filter Effect Tool allows users to apply a creative lens whacking effect to their images, which combines chromatic aberration, exposure adjustments, and vignette effects. This tool can enhance photographs by creating a unique aesthetic, making it suitable for artistic photography, social media posts, or any application where a distinctive visual style is desired. Users can customize the level of aberration, center exposure, exposure radius, vignette strength, and falloff to achieve the desired look.

Leave a Reply

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

Other Image Tools:

Image Black and White Red Filter Effect Tool

Image Lee Medium Stopper 6-Stop ND Filter Effect Tool

Image Nikon F3 Film Camera Render Effect Tool

Image Polaroid Spectra Filter Effect Tool

Image Contax T2/T3 Filter Effect Application

Image Bronica ETRS Medium Format Filter Effect Application

Image Soap Bubble Bokeh Effect Generator

Image Center Graduated ND Filter Effect Tool

Image Breakthrough Photography X4 ND Filter Effect

Photo Filter Effect Creator for Yashica T4 Point-and-Shoot

Image AGFA APX 25 Film Filter Effect Tool

Image Singh-Ray Gold-N-Blue Polarizer Effect Tool

Image Black and White Blue Filter Effect Tool

Image Pinhole Solargraphy Effect Creator

Image Kodak Vision3 500T Motion Picture Film Effect Simulator

Image Soft Focus Filter Effect for Nikon Nikkor

Image Bergger Pancro 400 Film Filter Effect Tool

Image Agfa Optima Filter Effect Application

Image Technicolor 3-Strip Process Filter Effect

Image Cyanotype Process Filter Effect

Image Black and White with Orange #21 Filter Effect Tool

Image Bleach Bypass Effect Filter

Image IMAX Camera Filter Effect Tool

Image Super 8 Film Filter Effect Tool

Image Anamorphic Lens Flare Filter Effect Tool

Image Prism Photography Filter Effect Tool

Image Freelensing Effect Creator

Image Tiffen Glimmerglass Filter Effect Tool

Image Mamiya RZ67 Medium Format Filter Effect Tool

Image Wet Plate Collodion Filter Effect Tool

Image Ilford Pan F Plus 50 Filter Effect Tool

Image X-ray Photography Filter Effect Tool

Image Radial Graduated Filter Effect Tool

Image Lee 80A Cooling Filter Effect Application

Image Autochrome Lumière Filter Effect Tool

Photo Infrared 720nm Filter Effect Tool

See All →