Please bookmark this page to avoid losing your image tool!

Photo Haunting 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.8, contrast = 130, brightness = -30, noiseAmount = 20, tintColor = "rgba(20, 40, 80, 0.2)", vignetteStrength = 0.7, blurRadius = 0.5) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    const w = originalImg.naturalWidth;
    const h = originalImg.naturalHeight;

    if (w === 0 || h === 0) {
        // Handle cases where image might not be loaded or is invalid
        canvas.width = 1; // avoid 0x0 canvas
        canvas.height = 1;
        console.warn("Photo Haunting Filter: Original image has zero width or height.");
        return canvas;
    }

    canvas.width = w;
    canvas.height = h;

    // 1. Apply blur (if any) and draw image
    // The filter applies to subsequent drawing operations.
    if (blurRadius > 0) {
        ctx.filter = `blur(${blurRadius}px)`;
    }
    ctx.drawImage(originalImg, 0, 0, w, h);
    // Reset filter so it doesn't affect subsequent tint/vignette drawing
    if (blurRadius > 0) {
        ctx.filter = 'none';
    }

    // 2. Pixel manipulations (desaturation, brightness, contrast, noise)
    // Only get/put image data if necessary
    if (desaturationLevel > 0 || contrast !== 100 || brightness !== 0 || noiseAmount > 0) {
        const imageData = ctx.getImageData(0, 0, w, h);
        const data = imageData.data;
        const contrastFactor = contrast / 100.0;

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

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

            // Contrast
            if (contrastFactor !== 1.0) { // contrast = 100 means factor is 1.0
                r = (r - 128) * contrastFactor + 128;
                g = (g - 128) * contrastFactor + 128;
                b = (b - 128) * contrastFactor + 128;
            }
            
            // Brightness
            if (brightness !== 0) {
                r += brightness;
                g += brightness;
                b += brightness;
            }

            // Noise
            if (noiseAmount > 0) {
                const noiseValue = (Math.random() - 0.5) * noiseAmount;
                r += noiseValue;
                g += noiseValue;
                b += noiseValue;
            }

            // Clamp values
            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);
    }

    // 3. Apply tint layer
    // Ensure tintColor is a non-empty string and not "none" (case-insensitive for "none" might be good, but string equality is ok for now)
    if (tintColor && typeof tintColor === 'string' && tintColor.toLowerCase() !== "none" && tintColor.trim() !== "") {
        try {
            // The browser's fillStyle will parse the color string.
            // If it's an RGBA string, its alpha component will be used for blending with source-over.
            ctx.globalCompositeOperation = 'source-over'; // Default blending mode
            ctx.fillStyle = tintColor;
            ctx.fillRect(0, 0, w, h);
        } catch (e) {
            // This catch block might not be effective for invalid fillStyle strings,
            // as browsers often silently ignore them.
            console.warn("Photo Haunting Filter: Could not apply tintColor. Ensure it's a valid CSS color string.", e);
        }
    }

    // 4. Apply vignette layer
    if (vignetteStrength > 0) {
        const centerX = w / 2;
        const centerY = h / 2;
        const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);

        // Calculate inner radius based on vignetteStrength to control the size of the clear area
        // innerRadiusFactor decreases as vignetteStrength increases, making the vignette larger.
        // Range from 0.7 (for strength 0) down to 0.1 (for strength 1.0)
        const innerRadiusFactor = 0.7 - vignetteStrength * 0.6;
        const actualInnerRadius = maxRadius * Math.max(0, innerRadiusFactor); // Ensure non-negative

        const gradient = ctx.createRadialGradient(centerX, centerY, actualInnerRadius, centerX, centerY, maxRadius);
        gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Transparent center
        gradient.addColorStop(1, `rgba(0,0,0,${vignetteStrength})`); // Dark edges with opacity based on strength

        ctx.globalCompositeOperation = 'source-over'; // Apply on top
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, w, h);
    }
    
    // Reset composite operation just in case, though subsequent ops are not expected in this function.
    ctx.globalCompositeOperation = 'source-over';

    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 Haunting Filter is an online tool designed to enhance and transform your images with a haunting aesthetic. By adjusting parameters such as desaturation, contrast, brightness, noise levels, and applying effects like tint and vignette, users can create unique and atmospheric photos. This tool is ideal for photographers, artists, or social media users looking to add a dramatic flair to their images or create a moody, vintage-like atmosphere for artistic projects.

Leave a Reply

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