Please bookmark this page to avoid losing your image tool!

Image Victorian Filter Effect Application

(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, contrastAmount = 1.1, noiseAmount = 15, vignetteStart = 0.3, vignetteEnd = 0.85, vignetteColor = "rgba(40,30,20,0.5)") {

    // Helper function to parse a color string (e.g., "red", "#FF0000", "rgb(255,0,0)", "rgba(255,0,0,0.5)")
    // and return its RGBA components.
    function getRgbaFromColorString(colorStr) {
        // Create a temporary 1x1 canvas.
        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = 1;
        tempCanvas.height = 1;
        const tempCtx = tempCanvas.getContext('2d');
        
        // Set the fillStyle to the provided color string.
        // This makes the browser parse the color.
        tempCtx.fillStyle = colorStr;
        
        // Draw a 1x1 pixel rectangle. This forces the fillStyle to be evaluated.
        tempCtx.fillRect(0, 0, 1, 1);
        
        // Get the [R, G, B, A] values of the drawn pixel. A is in the 0-255 range.
        const [r, g, b, a] = tempCtx.getImageData(0, 0, 1, 1).data;
        
        // Return RGB components and the alpha value (normalized to 0-1 range).
        return { r, g, b, alpha: a / 255 };
    }

    const canvas = document.createElement('canvas');
    // Add willReadFrequently hint for potential performance improvements with getImageData/putImageData.
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Check if the originalImg is a valid, fully loaded image.
    if (!originalImg || typeof originalImg.naturalWidth === 'undefined' || !originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        console.warn("Image is not valid or not fully loaded. Returning a placeholder canvas.");
        // Create a placeholder canvas with an error message.
        canvas.width = 300; 
        canvas.height = 150;
        ctx.fillStyle = '#f0f0f0'; // Light gray background
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#777'; // Dark gray text color
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font = '16px Arial';
        ctx.fillText('Image not loaded or invalid', canvas.width / 2, canvas.height / 2);
        return canvas;
    }
    
    // Set canvas dimensions to match the original image.
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

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

    // Apply pixel-level effects (Sepia, Contrast, Noise) if their respective parameters indicate an operation.
    if (sepiaAmount > 0 || contrastAmount !== 1.0 || noiseAmount > 0) {
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data; // This is a Uint8ClampedArray: [R,G,B,A, R,G,B,A, ...]

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

            // Store original pixel color components for sepia interpolation if needed.
            const rOrig = r;
            const gOrig = g;
            const bOrig = b;

            // 1. Apply Sepia effect
            if (sepiaAmount > 0) {
                // Standard sepia RGB transformation values
                const tr = 0.393 * rOrig + 0.769 * gOrig + 0.189 * bOrig;
                const tg = 0.349 * rOrig + 0.686 * gOrig + 0.168 * bOrig;
                const tb = 0.272 * rOrig + 0.534 * gOrig + 0.131 * bOrig;

                // Interpolate between original color and sepia color based on sepiaAmount (0 to 1).
                r = (1 - sepiaAmount) * rOrig + sepiaAmount * tr;
                g = (1 - sepiaAmount) * gOrig + sepiaAmount * tg;
                b = (1 - sepiaAmount) * bOrig + sepiaAmount * tb;
                
                // Clamp resulting values to the valid 0-255 range.
                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. Apply Contrast adjustment
            if (contrastAmount !== 1.0) {
                // Normalize color component to [-0.5, 0.5] range, apply contrast factor, then revert to [0, 255].
                r = (((r / 255) - 0.5) * contrastAmount + 0.5) * 255;
                g = (((g / 255) - 0.5) * contrastAmount + 0.5) * 255;
                b = (((b / 255) - 0.5) * contrastAmount + 0.5) * 255;

                // Clamp values.
                r = Math.max(0, Math.min(255, r));
                g = Math.max(0, Math.min(255, g));
                b = Math.max(0, Math.min(255, b));
            }
            
            // 3. Apply Noise
            if (noiseAmount > 0) {
                // Generate a random noise value between -noiseAmount/2 and +noiseAmount/2.
                const noise = (Math.random() - 0.5) * noiseAmount;
                r += noise;
                g += noise;
                b += noise;

                // Clamp values.
                r = Math.max(0, Math.min(255, r));
                g = Math.max(0, Math.min(255, g));
                b = Math.max(0, Math.min(255, b));
            }
            
            // Update pixel data in the ImageData object.
            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);
    }

    // 4. Apply Vignette effect
    // Parse the vignetteColor to access its RGB components and original alpha.
    const parsedVignetteColor = getRgbaFromColorString(vignetteColor);

    // Proceed with vignette if its parameters define a visible effect.
    // vignetteStart must be less than vignetteEnd for a valid gradient.
    // vignetteEnd > 0 means the vignette has some size.
    // parsedVignetteColor.alpha > 0 means the vignette color is not fully transparent.
    if (vignetteStart < vignetteEnd && vignetteEnd > 0 && parsedVignetteColor.alpha > 0) {
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        
        // Calculate the maximum distance from the center to a corner. This forms the gradient scale.
        const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);
        
        // Calculate actual pixel radii for gradient stops based on relative vignetteStart/End values.
        // Clamp vignetteStart/End to [0,1] range to ensure they are valid proportions of maxDist.
        const startRadius = maxDist * Math.max(0, Math.min(1, vignetteStart));
        const endRadius = maxDist * Math.max(0, Math.min(1, vignetteEnd));

        // Ensure startRadius is actually less than endRadius to prevent gradient errors.
        if (startRadius < endRadius) {
            const gradient = ctx.createRadialGradient(centerX, centerY, startRadius, centerX, centerY, endRadius);
            
            // Inner gradient stop: Use the RGB from vignetteColor but make it fully transparent.
            const transparentInnerStop = `rgba(${parsedVignetteColor.r}, ${parsedVignetteColor.g}, ${parsedVignetteColor.b}, 0)`;
            gradient.addColorStop(0, transparentInnerStop); // Color at startRadius
            
            // Outer gradient stop: Use the user-provided vignetteColor (which includes its alpha).
            gradient.addColorStop(1, vignetteColor); // Color at endRadius

            ctx.fillStyle = gradient;
            // Default 'source-over' globalCompositeOperation is suitable as vignetteColor already contains alpha.
            ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw the gradient overlay.
        }
    }
    
    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 Victorian Filter Effect Application is a versatile online tool designed to add a vintage Victorian-style effect to your images. By adjusting parameters such as sepia tone, contrast, noise, and vignette, users can easily transform their photos into nostalgic-looking artworks. This tool is particularly useful for photographers, artists, and social media enthusiasts looking to create unique and aesthetically pleasing visuals for personal or professional use. Whether you’re looking to enhance a historical theme or simply want to add a touch of classic elegance to your images, this application provides a user-friendly interface to achieve those effects.

Leave a Reply

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