Please bookmark this page to avoid losing your image tool!

Image Brooding 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, saturation = 0.2, contrast = 50, brightness = -25, coolShift = 30) {
    // Parameters:
    // originalImg: JavaScript Image object (HTMLImageElement)
    // saturation: number (0.0 to 1.0). 0.0 for grayscale, 1.0 for original color intensity. Default: 0.2 (low saturation for brooding effect)
    // contrast: number (0 to 100). Strength of contrast. 0 means no change. Default: 50 (results in a 1.5x contrast factor)
    // brightness: number (-100 to 100). Brightness adjustment. Negative values darken, positive values lighten. Default: -25 (darker)
    // coolShift: number (0 to 50). Amount of cool blue/cyan tint. Increases blue, slightly reduces red. Default: 30

    const canvas = document.createElement('canvas');
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    if (width === 0 || height === 0) {
        console.error("Original image has zero width or height.");
        // Return a tiny, empty canvas as a fallback.
        canvas.width = 1;
        canvas.height = 1;
        const tinyCtx = canvas.getContext('2d');
        if (tinyCtx) {
            tinyCtx.clearRect(0,0,1,1); // Make it transparent
        }
        return canvas;
    }

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

    const ctx = canvas.getContext('2d');
    if (!ctx) {
        console.error("Could not get 2D rendering context.");
        // Fallback: return an empty canvas (it's already created and sized)
        return canvas;
    }

    try {
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Error drawing image onto canvas:", e);
        // If drawing fails, return canvas with an error message.
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#DDDDDD'; // Light gray background for message
        ctx.fillRect(0,0,canvas.width, canvas.height);
        ctx.fillStyle = 'black';
        const baseFontSize = Math.min(canvas.width, canvas.height) / 15;
        const fontSize = Math.max(12, Math.floor(baseFontSize));
        ctx.font = `bold ${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Error: Could not draw input image.', canvas.width / 2, canvas.height / 2);
        return canvas;
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Error getting ImageData (possibly from a tainted canvas due to cross-origin image restrictions):", e);
        // Image is drawn, but pixel manipulation is not possible.
        // Display a message on top of the drawn image.
        ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'; // Semi-transparent overlay
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        
        ctx.fillStyle = 'white';
        const baseFontSize = Math.min(canvas.width, canvas.height) / 20;
        const fontSize = Math.max(12, Math.floor(baseFontSize));
        ctx.font = `bold ${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        
        const msgLine1 = "Filter not applied:";
        const msgLine2 = "Image data inaccessible.";
        const textLineHeight = fontSize * 1.2;

        ctx.fillText(msgLine1, canvas.width / 2, canvas.height / 2 - textLineHeight / 2);
        ctx.fillText(msgLine2, canvas.width / 2, canvas.height / 2 + textLineHeight / 2);
        return canvas;
    }

    const data = imageData.data;

    // Convert parameters to numbers, provides robustness if string representations of numbers are passed.
    const numSaturation = Number(saturation);
    const numContrast = Number(contrast);
    const numBrightness = Number(brightness);
    const numCoolShift = Number(coolShift);
    
    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i+1];
        let b = data[i+2];

        // 1. Brightness adjustment
        r += numBrightness;
        g += numBrightness;
        b += numBrightness;
        // Clamp to 0-255
        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. Contrast adjustment
        // `numContrast` (0 to 100) determines factor.
        // 0 maps to 1.0 (no change), 50 maps to 1.5, 100 maps to 2.0.
        // The midpoint for contrast is 128.
        const contrastFactor = 1.0 + (numContrast / 100.0);
        r = contrastFactor * (r - 128) + 128;
        g = contrastFactor * (g - 128) + 128;
        b = contrastFactor * (b - 128) + 128;
        // Clamp to 0-255
        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. Saturation adjustment
        // `numSaturation` (0.0-1.0): 0.0 for grayscale, 1.0 for original color intensity.
        // Standard luminosity coefficients for grayscale conversion.
        const gray = 0.299 * r + 0.587 * g + 0.114 * b; 
        // Lerp (linear interpolation) between grayscale and original color component
        r = gray * (1.0 - numSaturation) + r * numSaturation;
        g = gray * (1.0 - numSaturation) + g * numSaturation;
        b = gray * (1.0 - numSaturation) + b * numSaturation;
        // Clamping here is good practice, though if inputs r,g,b are 0-255 and numSaturation is 0-1,
        // results should remain within range. Floating point inaccuracies might slightly exceed.
        r = Math.max(0, Math.min(255, r));
        g = Math.max(0, Math.min(255, g));
        b = Math.max(0, Math.min(255, b));

        // 4. Cool Tint (Brooding filter)
        // `numCoolShift` (0-50) determines intensity of the tint.
        r -= numCoolShift * 0.5; // Reduce red component to make it cooler
        // g -= numCoolShift * 0.1; // Optional: slightly reduce green if a more cyan tint is desired
        b += numCoolShift;       // Boost blue component

        // Final clamp for pixel component values and round to nearest integer
        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 Image Brooding Filter tool allows users to apply a distinctive brooding aesthetic to their images through adjustments in saturation, contrast, brightness, and color tint. This tool can be particularly useful for photographers and graphic designers looking to create a moody atmosphere in their visuals, suitable for artistic projects, social media posts, or any creative endeavor that emphasizes a darker, cooler tone. By modifying the image characteristics, users can easily transform their photos to evoke specific emotions or styles.

Leave a Reply

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