Please bookmark this page to avoid losing your image tool!

Expired Film Effect Photo 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, colorShift = 'magenta', intensity = 0.6, grainAmount = 0.15, vignetteAmount = 0.7, addLightLeak = 1) {
    const canvas = document.createElement('canvas');
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    // Draw original image
    ctx.drawImage(originalImg, 0, 0, width, height);

    // Get pixels for manipulation
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;

    // Parse parameters to ensure they are numbers where needed
    intensity = Math.max(0, Math.min(1, Number(intensity)));
    grainAmount = Math.max(0, Math.min(1, Number(grainAmount)));
    vignetteAmount = Math.max(0, Math.min(1, Number(vignetteAmount)));
    const lightLeakFlag = Number(addLightLeak) === 1;

    const shift = colorShift.toLowerCase();

    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i + 1];
        let b = data[i + 2];

        // 1. Faded / Lifted Blacks & Crushed Whites (Characteristic of chemical degradation)
        // Wash out shadows by mapping 0-255 to something like 40-240 based on intensity
        const lift = 45 * intensity;
        const drop = 255 - (20 * intensity);
        
        r = lift + (r / 255) * (drop - lift);
        g = lift + (g / 255) * (drop - lift);
        b = (lift + 15 * intensity) + (b / 255) * (drop - lift); // Shadows naturally skew slightly blue/grey in film

        // 2. Color Shifts (Mimicking expired emulsion layers)
        if (shift === 'magenta') {
            r += 35 * intensity;
            g -= 15 * intensity;
            b += 20 * intensity;
        } else if (shift === 'green') {
            r -= 15 * intensity;
            g += 30 * intensity;
            b -= 15 * intensity;
        } else if (shift === 'yellow') {
            r += 30 * intensity;
            g += 25 * intensity;
            b -= 30 * intensity;
        } else if (shift === 'blue') {
            r -= 20 * intensity;
            g -= 10 * intensity;
            b += 45 * intensity;
        } else {
            // Default warm fade if unknown
            r += 15 * intensity;
            b -= 15 * intensity;
        }

        // 3. Film Grain / Noise
        if (grainAmount > 0) {
            // Random noise per pixel
            const noise = (Math.random() - 0.5) * 255 * grainAmount;
            r += noise;
            g += noise;
            b += noise;
        }

        // Clamp values 0 - 255
        data[i] = Math.min(255, Math.max(0, r));
        data[i + 1] = Math.min(255, Math.max(0, g));
        data[i + 2] = Math.min(255, Math.max(0, b));
    }

    // Apply the manipulated pixels back to canvas
    ctx.putImageData(imageData, 0, 0);

    // 4. Soft Vignette (Lens falloff & dirty edges)
    if (vignetteAmount > 0) {
        const cx = width / 2;
        const cy = height / 2;
        const radius = Math.max(cx, cy) * 1.2;
        
        const gradient = ctx.createRadialGradient(cx, cy, radius * 0.4, cx, cy, radius);
        gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
        // A dirty, slightly warm black/brown for the vignette
        gradient.addColorStop(1, `rgba(20, 10, 0, ${0.8 * vignetteAmount})`);
        
        ctx.globalCompositeOperation = 'multiply';
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, width, height);
    }

    // 5. Light Leaks (Characteristic of light seeping into old analog cameras)
    if (lightLeakFlag && intensity > 0) {
        ctx.globalCompositeOperation = 'screen';
        
        // Primary leak (usually top left or right depending on film advancing)
        const leakGrad1 = ctx.createLinearGradient(0, 0, width * 0.4, height * 0.4);
        leakGrad1.addColorStop(0, `rgba(255, 90, 0, ${0.35 * intensity})`);
        leakGrad1.addColorStop(0.4, `rgba(255, 30, 30, ${0.15 * intensity})`);
        leakGrad1.addColorStop(1, 'rgba(0, 0, 0, 0)');
        ctx.fillStyle = leakGrad1;
        ctx.fillRect(0, 0, width, height);

        // Secondary subtle leak (bottom or opposite edge)
        const leakGrad2 = ctx.createLinearGradient(width, height, width * 0.6, height * 0.7);
        leakGrad2.addColorStop(0, `rgba(255, 180, 50, ${0.2 * intensity})`);
        leakGrad2.addColorStop(1, 'rgba(0, 0, 0, 0)');
        ctx.fillStyle = leakGrad2;
        ctx.fillRect(0, 0, width, height);
    }

    // Reset composite operation to default
    ctx.globalCompositeOperation = 'source-over';

    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

This tool applies a vintage analog aesthetic to your photos by simulating the chemical degradation found in expired film. It allows you to customize the look using features such as color shifts (including magenta, green, yellow, and blue tints), faded shadows, and crushed whites. Additionally, you can adjust the intensity of film grain, apply a soft vignette to mimic lens falloff, and add realistic light leaks to achieve a nostalgic, lo-fi appearance. This is ideal for photographers and content creators looking to give digital images a retro, cinematic, or nostalgic feel.

Leave a Reply

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