Please bookmark this page to avoid losing your image tool!

Image Vintage Effect Applicator

(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, 
    sepiaAmount = 1.0, 
    noiseIntensity = 20, 
    vignetteOpacity = 0.6,
    vignetteColorStr = "0,0,0", // Comma-separated R,G,B string e.g., "0,0,0" for black
    vignetteExtent = 0.3    // Defines the proportion of the image's diagonal radius for the clear central area (0-1). 
                            // 0 means vignette starts darkening from the very center.
                            // 1 means the entire image is clear (no dark vignette from this gradient setting).
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Determine image dimensions. naturalWidth/Height are for <img> elements after they've loaded.
    // Fallback to width/height for other canvas elements or if naturalWidth/Height aren't available.
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Handle cases where the image might not be fully loaded or is invalid.
    if (width === 0 || height === 0) {
        // console.error("Image not loaded or dimensions are zero.");
        // Return a small canvas with an error message.
        canvas.width = 200; // Increased size for better message visibility
        canvas.height = 50;
        ctx.fillStyle = "rgb(200, 200, 200)"; // Light gray background
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "red";
        ctx.font = "16px Arial";
        ctx.textAlign = "center";
        ctx.fillText("Error: Invalid Image", canvas.width / 2, canvas.height / 2 + 6); // Centered text
        return canvas;
    }

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

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

    // Get image pixel data
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;

    // Sanitize and clamp input parameters to their expected ranges and convert to numbers
    const effectiveSepiaAmount = Math.max(0, Math.min(1, Number(sepiaAmount)));
    const effectiveNoiseIntensity = Math.max(0, Number(noiseIntensity)); // Can be > 255, but effect saturates
    const effectiveVignetteOpacity = Math.max(0, Math.min(1, Number(vignetteOpacity)));
    const effectiveVignetteExtent = Math.max(0, Math.min(1, Number(vignetteExtent)));

    // Parse the vignetteColorStr (e.g., "R,G,B") into usable RGB values
    const colorParts = vignetteColorStr.split(',').map(s => parseInt(s.trim(), 10));
    const V_R = (colorParts.length === 3 && !isNaN(colorParts[0])) ? Math.max(0, Math.min(255, colorParts[0])) : 0;
    const V_G = (colorParts.length === 3 && !isNaN(colorParts[1])) ? Math.max(0, Math.min(255, colorParts[1])) : 0;
    const V_B = (colorParts.length === 3 && !isNaN(colorParts[2])) ? Math.max(0, Math.min(255, colorParts[2])) : 0;
    const vignetteColorRgbaBase = `rgba(${V_R},${V_G},${V_B},`; // Base for "rgba(R,G,B," string

    // Iterate over each pixel (RGBA)
    for (let i = 0; i < data.length; i += 4) {
        let r_orig = data[i];
        let g_orig = data[i + 1];
        let b_orig = data[i + 2];

        let r = r_orig;
        let g = g_orig;
        let b = b_orig;

        // 1. Apply Sepia effect
        // Mixes original color with sepia-toned color based on effectiveSepiaAmount
        if (effectiveSepiaAmount > 0) {
            // Standard sepia transformation coefficients
            const tr = 0.393 * r_orig + 0.769 * g_orig + 0.189 * b_orig;
            const tg = 0.349 * r_orig + 0.686 * g_orig + 0.168 * b_orig;
            const tb = 0.272 * r_orig + 0.534 * g_orig + 0.131 * b_orig;

            // Blend original with sepia version
            r = (tr * effectiveSepiaAmount) + (r_orig * (1 - effectiveSepiaAmount));
            g = (tg * effectiveSepiaAmount) + (g_orig * (1 - effectiveSepiaAmount));
            b = (tb * effectiveSepiaAmount) + (b_orig * (1 - effectiveSepiaAmount));
        }

        // Clamp R, G, B values to [0, 255] after sepia and blending
        r = Math.max(0, Math.min(255, r));
        g = Math.max(0, Math.min(255, g));
        b = Math.max(0, Math.min(255, b));
        
        // 2. Add Noise
        // Adds random variation to R,G,B channels to simulate film grain
        if (effectiveNoiseIntensity > 0) {
            const noiseR_val = (Math.random() - 0.5) * effectiveNoiseIntensity;
            const noiseG_val = (Math.random() - 0.5) * effectiveNoiseIntensity;
            const noiseB_val = (Math.random() - 0.5) * effectiveNoiseIntensity;

            r = Math.max(0, Math.min(255, r + noiseR_val));
            g = Math.max(0, Math.min(255, g + noiseG_val));
            b = Math.max(0, Math.min(255, b + noiseB_val));
        }
        
        // Update pixel data
        data[i] = r;
        data[i + 1] = g;
        data[i + 2] = b;
        // Alpha channel (data[i+3]) is preserved from the original image
    }

    // Write the modified pixel data back to the canvas
    ctx.putImageData(imageData, 0, 0);

    // 3. Apply Vignette effect
    // Overlays a radial gradient to darken the edges/corners of the image
    if (effectiveVignetteOpacity > 0) {
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        
        // The outer radius of the gradient extends to the furthest corner of the canvas
        const outerRadius = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
        // The inner radius defines the start of the vignette fade; effectively, the "clear" central area
        const innerRadius = outerRadius * effectiveVignetteExtent; 

        const gradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
        
        // The gradient starts transparent at innerRadius (or from center if innerRadius is 0)
        gradient.addColorStop(0, 'rgba(0,0,0,0)'); 
        // It fades to the specified vignette color and opacity at outerRadius
        gradient.addColorStop(1, vignetteColorRgbaBase + effectiveVignetteOpacity + ')');

        ctx.fillStyle = gradient;
        // Ensure vignette is drawn using default composition mode (source-over)
        ctx.globalCompositeOperation = 'source-over'; 
        ctx.fillRect(0, 0, canvas.width, canvas.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 Image Vintage Effect Applicator is a tool designed to transform your images with a vintage aesthetic. It allows users to apply a sepia tone for a warm, nostalgic look, introduce noise to simulate film grain, and create a vignette effect that darkens the edges of the image. This tool can be used for enhancing personal photos, creating art projects, or adding a retro feel to images for social media. Whether you’re looking to evoke a sense of nostalgia or to artistically modify images, this tool provides a straightforward approach to achieve that vintage flair.

Leave a Reply

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