Please bookmark this page to avoid losing your image tool!

Image 35mm Film 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.
function processImage(originalImg, sepiaIntensity = 0.3, grainAmount = 0.1, vignetteStrength = 0.5, vignetteStart = 0.3, vignetteEnd = 0.95) {
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } can be a performance hint for some browsers/operations.
    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;

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, w, h);

    // Clamp input parameters to sensible ranges
    const effectiveSepiaIntensity = Math.max(0, Math.min(1, sepiaIntensity));
    const effectiveGrainAmount = Math.max(0, Math.min(0.5, grainAmount)); // Cap grain amount
    const effectiveVignetteStrength = Math.max(0, Math.min(1, vignetteStrength));

    // Apply Sepia and Grain pixel by pixel
    if (effectiveSepiaIntensity > 0 || effectiveGrainAmount > 0) {
        const imageData = ctx.getImageData(0, 0, w, h);
        const data = imageData.data;

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

            // Apply Sepia
            if (effectiveSepiaIntensity > 0) {
                const tr = 0.393 * r + 0.769 * g + 0.189 * b;
                const tg = 0.349 * r + 0.686 * g + 0.168 * b;
                const tb = 0.272 * r + 0.534 * g + 0.131 * b;

                r = r * (1 - effectiveSepiaIntensity) + tr * effectiveSepiaIntensity;
                g = g * (1 - effectiveSepiaIntensity) + tg * effectiveSepiaIntensity;
                b = b * (1 - effectiveSepiaIntensity) + tb * effectiveSepiaIntensity;
            }

            // Apply Grain
            if (effectiveGrainAmount > 0) {
                // Monochrome grain: add the same random value to R, G, B
                // The range of Math.random() - 0.5 is [-0.5, 0.5].
                // Scaled by a factor (e.g., 100) and effectiveGrainAmount.
                // Max grain magnitude is 50 at effectiveGrainAmount = 0.5.
                const grain = (Math.random() - 0.5) * (100 * effectiveGrainAmount);
                r += grain;
                g += grain;
                b += grain;
            }

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

    // Apply Vignette
    if (effectiveVignetteStrength > 0) {
        ctx.save();
        // Using source-over to draw the vignette on top
        ctx.globalCompositeOperation = 'source-over';

        const centerX = w / 2;
        const centerY = h / 2;
        
        // The maximum radius is from the center to a corner
        const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);

        // Validate and normalize vignetteStart and vignetteEnd
        let normVignetteStart = Math.max(0, Math.min(1, vignetteStart));
        let normVignetteEnd = Math.max(0, Math.min(1, vignetteEnd));

        // Ensure vignetteEnd is greater than vignetteStart
        if (normVignetteEnd <= normVignetteStart) {
            // If start is at or near 1, push end to 1. Otherwise, make a small gap.
            if (normVignetteStart >= 0.99) {
                normVignetteEnd = 1;
                normVignetteStart = Math.min(normVignetteStart, 0.99); // Ensure start is not 1 if end becomes 1
            } else {
                 normVignetteEnd = normVignetteStart + 0.01;
            }
        }
         // Ensure normVignetteEnd itself does not exceed 1 after adjustment
        normVignetteEnd = Math.min(1, normVignetteEnd);
        // Ensure start is less than end after all adjustments
        if (normVignetteStart >= normVignetteEnd) {
            normVignetteStart = Math.max(0, normVignetteEnd-0.01);
        }


        const startRadius = maxRadius * normVignetteStart;
        const endRadius = maxRadius * normVignetteEnd;
        
        if (startRadius >= endRadius) { // Failsafe if radii are invalid for gradient
             if (effectiveVignetteStrength > 0) { // Still draw a full overlay if strength is there
                ctx.fillStyle = `rgba(0,0,0,${effectiveVignetteStrength * 0.1})`; // very subtle full overlay as error state
                ctx.fillRect(0,0,w,h);
             }
        } else {
            const gradient = ctx.createRadialGradient(
                centerX, centerY, startRadius, // Inner circle (for color stop 0)
                centerX, centerY, endRadius    // Outer circle (for color stop 1)
            );

            // Color stop 0: transparent black at startRadius
            gradient.addColorStop(0, 'rgba(0,0,0,0)');
            // Color stop 1: semi-transparent black at endRadius, strength controlled by vignetteStrength
            gradient.addColorStop(1, `rgba(0,0,0,${effectiveVignetteStrength})`);

            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, w, h); // Apply gradient over the entire canvas
        }
        
        ctx.restore();
    }

    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 35mm Film Filter Effect Tool allows users to apply a vintage look to their images, reminiscent of 35mm film photography. By adjusting parameters such as sepia intensity, grain amount, and vignette strength, users can create images with rich, warm tones, subtle noise, and a gradient darkening around the edges. This tool is ideal for photographers, graphic designers, and social media enthusiasts looking to enhance their images with a nostalgic aesthetic.

Leave a Reply

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