Please bookmark this page to avoid losing your image tool!

Photo Urban Mood Filter

(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, desaturationLevel = 0.7, coolness = 20, contrastFactor = 1.3, vignetteStrength = 0.5, grainAmount = 8) {
    const canvas = document.createElement('canvas');
    const naturalWidth = originalImg.naturalWidth || originalImg.width;
    const naturalHeight = originalImg.naturalHeight || originalImg.height;
    
    if (naturalWidth === 0 || naturalHeight === 0) {
        console.error("Image has zero dimensions.");
        // Return an empty or minimal canvas if image is invalid
        canvas.width = 1;
        canvas.height = 1;
        return canvas;
    }

    canvas.width = naturalWidth;
    canvas.height = naturalHeight;

    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    ctx.drawImage(originalImg, 0, 0, naturalWidth, naturalHeight);

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Error getting ImageData (cross-origin issue?):", e);
        // If we can't get ImageData (e.g., tainted canvas due to cross-origin image),
        // we return the canvas with the original image drawn.
        // The caller can decide how to handle this (e.g., display as is, or show an error).
        return canvas;
    }
    
    const data = imageData.data;
    const width = canvas.width;
    const height = canvas.height;

    const centerX = width / 2.0;
    const centerY = height / 2.0;
    // Maximum distance from center to a corner, used for vignette normalization
    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);

    // Clamp parameters to sensible ranges to prevent unexpected behavior or errors
    desaturationLevel = Math.max(0, Math.min(1, desaturationLevel)); // 0 to 1
    coolness = Math.max(0, coolness); // 0 or positive
    contrastFactor = Math.max(0, contrastFactor); // 0 or positive (1 is no change)
    vignetteStrength = Math.max(0, Math.min(1, vignetteStrength)); // 0 to 1
    grainAmount = Math.max(0, grainAmount); // 0 or positive

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

        // 1. Desaturation (Luminance-preserving method)
        if (desaturationLevel > 0) {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            r = r * (1 - desaturationLevel) + gray * desaturationLevel;
            g = g * (1 - desaturationLevel) + gray * desaturationLevel;
            b = b * (1 - desaturationLevel) + gray * desaturationLevel;
        }

        // 2. Coolness adjustment (adds a blue/cyan tint)
        if (coolness > 0) {
            r -= coolness * 0.6; // Reduce red component
            g -= coolness * 0.3; // Slightly reduce green component
            b += coolness * 1.0; // Boost blue component
        }

        // 3. Contrast adjustment
        // Formula: NewColor = Factor * (OldColor - 128) + 128
        // This pushes colors away from (or towards if Factor < 1) mid-gray.
        if (contrastFactor !== 1.0) {
            r = contrastFactor * (r - 128) + 128;
            g = contrastFactor * (g - 128) + 128;
            b = contrastFactor * (b - 128) + 128;
        }
        
        // 4. Vignette effect (darkens corners)
        if (vignetteStrength > 0 && maxDist > 0) { // maxDist check for 1x1 or 0x0 images although naturalWidth/Height check covers 0x0
            const pixelIndex = i / 4;
            const currentX = pixelIndex % width;
            const currentY = Math.floor(pixelIndex / width);

            const dx = centerX - currentX;
            const dy = centerY - currentY;
            const dist = Math.sqrt(dx * dx + dy * dy);
            
            const vignetteSoftness = 2.0; // Quadratic falloff for a smooth transition
            const relativeDist = dist / maxDist; // Normalized distance from center (0 to 1)
            
            // Darkness increases from 0 at center to vignetteStrength at corners
            const darkness = Math.pow(relativeDist, vignetteSoftness) * vignetteStrength;
            const vignetteFactor = 1.0 - darkness; // Multiplier for color channels
            
            r *= vignetteFactor;
            g *= vignetteFactor;
            b *= vignetteFactor;
        }

        // 5. Grain/Noise addition
        if (grainAmount > 0) {
            // Generate monochromatic noise: random value between -grainAmount and +grainAmount
            const noise = (Math.random() - 0.5) * grainAmount * 2; 
            r += noise;
            g += noise;
            b += noise;
        }

        // Final Clamping: ensure R, G, B values are within the valid [0, 255] range
        data[i] = Math.max(0, Math.min(255, Math.round(r)));
        data[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
        data[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
        // Alpha channel (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 Photo Urban Mood Filter is an online tool that allows users to enhance their images by applying various aesthetic effects. This tool can desaturate colors to create a muted look, adjust coolness for a more bluish tone, increase contrast for more vivid images, and add a vignette effect that darkens the corners of the photo. Additionally, users can add grain for a textured appearance. It is ideal for photographers, graphic designers, and social media enthusiasts looking to give their photos an urban artistic vibe.

Leave a Reply

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