Please bookmark this page to avoid losing your image tool!

Haunted Mansion Portrait Image Creator

(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,
    desaturation = 0.8,         // 0.0 (original color) to 1.0 (full grayscale)
    contrast = 1.3,             // 1.0 is no change. Recommend range: 0.5 (less contrast) to 2.5 (more contrast)
    brightness = -30,           // Adjusts brightness. E.g., -100 (darker) to 100 (brighter)
    noise = 20,                 // Amount of noise. E.g., 0 (none) to 50 (heavy noise)
    vignetteDarkness = 0.7,     // Darkness of vignette edges. 0.0 (none) to 1.0 (fully black)
    vignetteSpread = 0.3,       // How far from center (as a fraction of max radius) the vignette effect starts. 0.0 (starts at center) to 1.0 (no vignette)
    tintColorStr = "40,50,70",  // RGB color for tint as a string "R,G,B". E.g., "40,50,70" (cool dark gray). Set to "" or null for no tint.
    tintOpacity = 0.2           // Opacity of the tint. 0.0 (no tint) to 1.0 (full tint color)
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); // willReadFrequently for performance with getImageData/putImageData

    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    canvas.width = imgWidth;
    canvas.height = imgHeight;

    // If the image has no dimensions, return an empty canvas
    if (imgWidth === 0 || imgHeight === 0) {
        console.warn("Original image has zero width or height.");
        return canvas;
    }

    // Draw the original image onto the canvas
    try {
        ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error drawing image to canvas:", e);
        return canvas; // Return empty canvas on error
    }
    
    const imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
    const data = imageData.data;

    let parsedTintColor = null;
    if (tintColorStr && typeof tintColorStr === 'string' && tintColorStr.trim() !== "" && tintOpacity > 0) {
        const parts = tintColorStr.split(',').map(s => parseFloat(s.trim()));
        if (parts.length === 3 && parts.every(p => !isNaN(p) && p >= 0 && p <= 255)) {
            parsedTintColor = { r: parts[0], g: parts[1], b: parts[2] };
        } else {
            console.warn("Invalid tintColorStr format. Expected 'R,G,B' with numbers 0-255. Tint disabled.", tintColorStr);
        }
    }

    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
        if (desaturation > 0) {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Luminosity method
            const desat = Math.max(0, Math.min(1, desaturation)); // Clamp desaturation 0-1
            r = r * (1 - desat) + gray * desat;
            g = g * (1 - desat) + gray * desat;
            b = b * (1 - desat) + gray * desat;
        }

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

        // 3. Contrast
        if (contrast !== 1.0) {
            const clampedContrast = Math.max(0, contrast); // Ensure contrast factor is not negative
            r = (r - 128) * clampedContrast + 128;
            g = (g - 128) * clampedContrast + 128;
            b = (b - 128) * clampedContrast + 128;
        }
        
        // 4. Tint
        if (parsedTintColor && tintOpacity > 0) {
            const tOp = Math.max(0, Math.min(1, tintOpacity)); // Clamp opacity 0-1
            r = r * (1 - tOp) + parsedTintColor.r * tOp;
            g = g * (1 - tOp) + parsedTintColor.g * tOp;
            b = b * (1 - tOp) + parsedTintColor.b * tOp;
        }

        // 5. Noise
        if (noise > 0) {
            const noiseVal = Math.max(0, noise); // Ensure noise intensity is not negative
            const randomNoise = (Math.random() - 0.5) * 2 * noiseVal; // Noise in range [-noiseVal, noiseVal]
            r += randomNoise;
            g += randomNoise;
            b += randomNoise;
        }

        // Clamp final pixel values to [0, 255]
        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));
        // Alpha channel (data[i+3]) remains unchanged
    }

    // Put the modified image data back onto the canvas
    ctx.putImageData(imageData, 0, 0);

    // 6. Vignette
    const cVignetteDarkness = Math.max(0, Math.min(1, vignetteDarkness));
    const cVignetteSpread = Math.max(0, Math.min(1, vignetteSpread));

    if (cVignetteDarkness > 0 && cVignetteSpread < 1.0) { // vignetteSpread = 1 means no vignette visible
        const centerX = imgWidth / 2;
        const centerY = imgHeight / 2;
        
        const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY); // Radius to the furthest corner

        // innerRadius: where vignette is still fully transparent
        // outerRadius: where vignette reaches its specified darkness
        const innerRadius = maxRadius * cVignetteSpread; 
        const outerRadius = maxRadius;
        
        if (outerRadius > innerRadius) { // Ensure gradient is valid (outer must be > inner)
            const gradient = ctx.createRadialGradient(
                centerX, centerY, innerRadius,
                centerX, centerY, outerRadius
            );

            gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Transparent towards the center
            gradient.addColorStop(1, `rgba(0,0,0,${cVignetteDarkness})`); // Darker towards the edges

            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, imgWidth, imgHeight);
        }
    }

    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 Haunted Mansion Portrait Image Creator allows users to transform their images into haunted-style portraits. This tool enables customization by adjusting various parameters such as desaturation, contrast, brightness, and noise levels. It can also apply a vignette effect and a tint color to enhance the spooky aesthetic. This makes it ideal for creating atmospheric artwork for Halloween, themed parties, or for use in creative projects that require a dark, eerie flair.

Leave a Reply

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