Please bookmark this page to avoid losing your image tool!

Nostalgic Photo Filter 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,
    sepiaIntensity = 0.6,       // Number: 0.0 (no sepia) to 1.0 (full sepia)
    saturationReduction = 0.3,  // Number: 0.0 (full color) to 1.0 (grayscale)
    contrastAdjustment = 1.1,   // Number: e.g., 0.5 (low contrast) to 2.0 (high contrast); 1.0 is no change
    brightnessOffset = 5,       // Number: e.g., -50 (darker) to 50 (brighter); 0 is no change
    noiseAmount = 15,           // Number: e.g., 0 (no noise) to 50 (heavy noise)
    vignetteStrength = 0.5,     // Number: 0.0 (no vignette) to 1.0 (fully opaque edges)
    vignetteStartRadiusFactor = 0.5, // Number: 0.0 to 1.0; Center transparent area radius, relative to image diagonal
    vignetteEndRadiusFactor = 0.95   // Number: 0.0 to 1.0; Radius where vignette reaches full strength, relative to image diagonal
) {
    // Ensure image is loaded
    if (!originalImg.complete || originalImg.naturalWidth === 0) {
        try {
            await new Promise((resolve, reject) => {
                const timeoutId = setTimeout(() => reject(new Error("Image loading timed out for nostalgic filter.")), 5000);
                
                originalImg.onload = () => { 
                    clearTimeout(timeoutId); 
                    if (originalImg.naturalWidth === 0) { // Still possible if onload fires for e.g. an empty SVG
                         reject(new Error("Image loaded successfully but has zero dimensions."));
                    } else {
                        resolve();
                    }
                };
                originalImg.onerror = () => { 
                    clearTimeout(timeoutId); 
                    reject(new Error("Image failed to load for nostalgic filter."));
                };

                // If src was set and it already failed, onerror might not fire again.
                // If src was never set, onload/onerror will not fire. Timeout handles this.
                // If image has already completed loading but resulted in 0 dimensions (e.g. broken link)
                if (originalImg.complete && originalImg.naturalWidth === 0 && originalImg.src) {
                     clearTimeout(timeoutId);
                     reject(new Error("Image indicates completion but has zero dimensions (possibly broken link or empty image)."));
                } else if (originalImg.complete && originalImg.naturalWidth > 0) { // Already loaded
                    clearTimeout(timeoutId);
                    resolve();
                }
                // For an image element with no src, it will timeout.
            });
        } catch (e) {
            console.error(e.message);
            const errorCanvas = document.createElement('canvas');
            errorCanvas.width = 200; 
            errorCanvas.height = 100;
            const errorCtx = errorCanvas.getContext('2d');
            errorCtx.fillStyle = '#f0f0f0';
            errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
            errorCtx.fillStyle = 'red';
            errorCtx.font = '12px Arial';
            errorCtx.textAlign = 'center';
            errorCtx.textBaseline = 'middle';
            const lines = e.message.match(/.{1,25}/g) || ["Image error"]; // Wrap text
            lines.forEach((line, index) => {
                 errorCtx.fillText(line, errorCanvas.width / 2, errorCanvas.height / 2 - (lines.length-1)*7 + index*14);
            });
            return errorCanvas;
        }
    }
    
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); 

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

    if (width === 0 || height === 0) { // Should be caught by earlier checks
        console.warn("Nostalgic filter: Image has zero width or height after loading checks.");
        canvas.width = 1; 
        canvas.height = 1;
        return canvas; 
    }
    canvas.width = width;
    canvas.height = height;

    ctx.drawImage(originalImg, 0, 0, width, height);

    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;

    // Clamp input parameters to sensible ranges
    const currentSepiaIntensity = Math.max(0, Math.min(1, sepiaIntensity));
    const currentSaturationReduction = Math.max(0, Math.min(1, saturationReduction));
    const currentContrastAdjustment = Math.max(0, contrastAdjustment); // Allow >1, 0 means black
    const currentNoiseAmount = Math.max(0, noiseAmount);

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

        // 1. Saturation Reduction
        if (currentSaturationReduction > 0) {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Luminosity
            r = r * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
            g = g * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
            b = b * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
        }

        // 2. Sepia Tone
        if (currentSepiaIntensity > 0) {
            const sr = 0.393 * r + 0.769 * g + 0.189 * b;
            const sg = 0.349 * r + 0.686 * g + 0.168 * b;
            const sb = 0.272 * r + 0.534 * g + 0.131 * b;
            
            r = r * (1 - currentSepiaIntensity) + sr * currentSepiaIntensity;
            g = g * (1 - currentSepiaIntensity) + sg * currentSepiaIntensity;
            b = b * (1 - currentSepiaIntensity) + sb * currentSepiaIntensity;
        }
        
        // 3. Brightness Adjustment
        if (brightnessOffset !== 0) {
            r += brightnessOffset;
            g += brightnessOffset;
            b += brightnessOffset;
        }

        // 4. Contrast Adjustment
        if (currentContrastAdjustment !== 1.0) {
            r = (((r / 255.0 - 0.5) * currentContrastAdjustment) + 0.5) * 255.0;
            g = (((g / 255.0 - 0.5) * currentContrastAdjustment) + 0.5) * 255.0;
            b = (((b / 255.0 - 0.5) * currentContrastAdjustment) + 0.5) * 255.0;
        }
        
        // 5. Noise
        if (currentNoiseAmount > 0) {
            const noiseVal = (Math.random() - 0.5) * 2 * currentNoiseAmount; 
            r += noiseVal;
            g += noiseVal;
            b += noiseVal;
        }

        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);

    // 6. Vignette
    const currentVignetteStrength = Math.max(0, Math.min(1, vignetteStrength));
    const currentStartRadiusFactor = Math.max(0, Math.min(1, vignetteStartRadiusFactor));
    const currentEndRadiusFactor = Math.max(0, Math.min(1, vignetteEndRadiusFactor));

    if (currentVignetteStrength > 0.001 && currentEndRadiusFactor > currentStartRadiusFactor) {
        const centerX = width / 2;
        const centerY = height / 2;
        
        const maxRadius = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
        
        const innerR = maxRadius * currentStartRadiusFactor;
        const outerR = maxRadius * currentEndRadiusFactor;

        const gradient = ctx.createRadialGradient(centerX, centerY, innerR, centerX, centerY, outerR);
        gradient.addColorStop(0, 'rgba(0,0,0,0)');
        gradient.addColorStop(1, `rgba(0,0,0,${currentVignetteStrength})`);

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, width, height);
    }

    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 Nostalgic Photo Filter Tool allows users to apply various retro effects to their images, transforming them into nostalgic-looking photos. With customizable parameters, users can adjust sepia intensity, reduce saturation, manipulate contrast and brightness, add noise for a vintage feel, and apply vignette effects. This tool is perfect for enhancing personal photos, creating unique social media content, or giving digital images a classic, timeless appearance.

Leave a Reply

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