Please bookmark this page to avoid losing your image tool!

Vintage Mood 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.7, brightness = 0, contrast = 20, vignetteStrength = 0.5, noiseAmount = 5) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Ensure image is loaded and dimensions are available
    if (!originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        try {
            await new Promise((resolve, reject) => {
                // Double check in case it completed while instance was created
                if (originalImg.complete && originalImg.naturalWidth !== 0 && originalImg.naturalHeight !== 0) {
                    resolve();
                    return;
                }
                originalImg.onload = () => resolve();
                originalImg.onerror = () => reject(new Error("Image could not be loaded for processing."));
                
                // If originalImg.src is not set or is invalid, onerror should fire.
                // If src is set but image is already errored, this might not work as expected
                // without re-setting src, but typically originalImg instance would be fresh.
            });
        } catch (error) {
            console.error(error.message);
            // Return a small, clearly empty or error-indicating canvas
            const errorCanvas = document.createElement('canvas');
            errorCanvas.width = 100; // Small but visible
            errorCanvas.height = 100;
            const errorCtx = errorCanvas.getContext('2d');
            errorCtx.fillStyle = '#_NEW_LINE_#FDD'; // Light red background
            errorCtx.fillRect(0, 0, 100, 100);
            errorCtx.fillStyle = 'red';
            errorCtx.textAlign = 'center';
            errorCtx.textBaseline = 'middle';
            errorCtx.font = '16px Arial';
            errorCtx.fillText('Error', 50, 50);
            return errorCanvas;
        }
    }
    
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;

    if (width === 0 || height === 0) {
        console.error("Image has zero dimensions after attempting to load.");
        const errorCanvas = document.createElement('canvas'); // As above
        errorCanvas.width = 100;
        errorCanvas.height = 100;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = '#_NEW_LINE_#FDD';
        errorCtx.fillRect(0, 0, 100, 100);
        errorCtx.fillStyle = 'red';
        errorCtx.textAlign = 'center';
        errorCtx.textBaseline = 'middle';
        errorCtx.font = '16px Arial';
        errorCtx.fillText('Error', 50, 50);
        return errorCanvas;
    }

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

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

    // In case drawImage does not throw for 0-area source but getImageData would.
    if (width <= 0 || height <= 0) {
         console.warn("Image dimensions are non-positive, returning canvas as is.");
         return canvas; 
    }

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

    const centerX = width / 2;
    const centerY = height / 2;
    const maxDist = (width === 0 && height === 0) ? 0 : Math.sqrt(centerX * centerX + centerY * centerY);

    function clamp(value, min = 0, max = 255) {
        return Math.max(min, Math.min(max, value));
    }
    
    const contrastFactor = (contrast / 100.0) + 1.0;

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

        // 1. Brightness
        if (brightness !== 0) {
            r = clamp(r + brightness);
            g = clamp(g + brightness);
            b = clamp(b + brightness);
        }

        // 2. Contrast
        if (contrast !== 0) { // contrast = 0 means factor = 1, no change
            r = clamp(128 + contrastFactor * (r - 128));
            g = clamp(128 + contrastFactor * (g - 128));
            b = clamp(128 + contrastFactor * (b - 128));
        }
        
        // 3. Sepia
        if (sepiaIntensity > 0) {
            const currentR = r;
            const currentG = g;
            const currentB = b;

            const sr = clamp(0.393 * currentR + 0.769 * currentG + 0.189 * currentB);
            const sg = clamp(0.349 * currentR + 0.686 * currentG + 0.168 * currentB);
            const sb = clamp(0.272 * currentR + 0.534 * currentG + 0.131 * currentB);

            r = clamp(currentR * (1 - sepiaIntensity) + sr * sepiaIntensity);
            g = clamp(currentG * (1 - sepiaIntensity) + sg * sepiaIntensity);
            b = clamp(currentB * (1 - sepiaIntensity) + sb * sepiaIntensity);
        }

        // 4. Noise
        if (noiseAmount > 0) {
            const noiseVal = (Math.random() - 0.5) * 2 * noiseAmount;
            r = clamp(r + noiseVal);
            g = clamp(g + noiseVal);
            b = clamp(b + noiseVal);
        }
        
        // 5. Vignette
        if (vignetteStrength > 0 && maxDist > 0) {
            const pixelX = (i / 4) % width;
            const pixelY = Math.floor((i / 4) / width);
            
            const dx = pixelX - centerX;
            const dy = pixelY - centerY;
            const dist = Math.sqrt(dx * dx + dy * dy);
            
            const relativeDist = dist / maxDist;
            
            const vignetteFalloffPower = 2.0; // Quadratic falloff
            const vignetteAmount = Math.pow(relativeDist, vignetteFalloffPower);
            
            const reductionMultiplier = vignetteAmount * vignetteStrength;
            const finalMultiplier = clamp(1.0 - reductionMultiplier, 0.0, 1.0); // Clamp multiplier between 0 and 1
            
            r = clamp(r * finalMultiplier);
            g = clamp(g * finalMultiplier);
            b = clamp(b * finalMultiplier);
        }

        data[i] = r;
        data[i + 1] = g;
        data[i + 2] = b;
        // Alpha data[i+3] is preserved
    }

    ctx.putImageData(imageData, 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 Vintage Mood Photo Filter Tool allows users to enhance their images with a vintage aesthetic. It provides adjustable parameters to apply effects like sepia tones, brightness adjustments, contrast enhancements, vignette effects, and noise addition. This versatile tool is ideal for photographers and social media enthusiasts looking to create nostalgic, artistic images or for anyone wanting to give their photos a warm, retro feel for personal projects or digital content.

Leave a Reply

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