Please bookmark this page to avoid losing your image tool!

Image Brutalist Filter Effect Applicator

(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, posterizeLevels = 4, saturation = 1.2, pixelSize = 1, palette = "") {
    // Ensure parameters are numbers and properly initialized
    posterizeLevels = Number(posterizeLevels);
    saturation = Number(saturation);
    pixelSize = Number(pixelSize);

    // Validate and adjust parameters
    let pLevels = Math.floor(posterizeLevels); // Used for posterization logic
    pixelSize = Math.max(1, Math.floor(pixelSize)); // pixelSize must be an integer >= 1

    const outputCanvas = document.createElement('canvas');
    const ctx = outputCanvas.getContext('2d');

    const w = originalImg.naturalWidth || originalImg.width;
    const h = originalImg.naturalHeight || originalImg.height;

    outputCanvas.width = w;
    outputCanvas.height = h;

    // --- Helper Function: Hex to RGB ---
    function hexToRgb(hex) {
        if (!hex) return null;
        hex = hex.replace(/^#/, '');
        if (!/^(?:[0-9a-fA-F]{3}){1,2}$/.test(hex)) {
            // console.warn(`Invalid hex format: ${hex}`); // Optional: for debugging
            return null;
        }

        let r, g, b;
        if (hex.length === 3) {
            r = parseInt(hex[0] + hex[0], 16);
            g = parseInt(hex[1] + hex[1], 16);
            b = parseInt(hex[2] + hex[2], 16);
        } else { // length is 6
            r = parseInt(hex.substring(0, 2), 16);
            g = parseInt(hex.substring(2, 4), 16);
            b = parseInt(hex.substring(4, 6), 16);
        }
        return [r, g, b];
    }

    // --- Helper Function: RGB to HSL ---
    function rgbToHsl(r, g, b) {
        r /= 255; g /= 255; b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;

        if (max === min) {
            h = s = 0; // achromatic
        } else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return [h, s, l];
    }

    // --- Helper Function: HSL to RGB ---
    function hslToRgb(h, s, l) {
        let r, g, b;
        if (s === 0) {
            r = g = b = l; // achromatic
        } else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1 / 6) return p + (q - p) * 6 * t;
                if (t < 1 / 2) return q;
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
                return p;
            };
            const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p = 2 * l - q;
            r = hue2rgb(p, q, h + 1 / 3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1 / 3);
        }
        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }

    // --- Helper Function: Color Distance (squared for efficiency) ---
    function colorDistanceSquared(rgb1, rgb2) {
        const dr = rgb1[0] - rgb2[0];
        const dg = rgb1[1] - rgb2[1];
        const db = rgb1[2] - rgb2[2];
        return dr * dr + dg * dg + db * db;
    }

    // --- Parse custom palette ---
    let parsedPalette = [];
    if (typeof palette === 'string' && palette.trim() !== "") {
        const colors = palette.split(',');
        for (const colorStr of colors) {
            const rgb = hexToRgb(colorStr.trim());
            if (rgb) {
                parsedPalette.push(rgb);
            } else {
                // console.warn(`Invalid color in palette string: ${colorStr}`); // Optional: for debugging
            }
        }
    }

    // --- 1. Prepare initial image on canvas (with pixelation if needed) ---
    if (w > 0 && h > 0) { // Only proceed if image has dimensions
        if (pixelSize > 1) {
            const tempPixelCanvas = document.createElement('canvas');
            const tempPixelCtx = tempPixelCanvas.getContext('2d');
            
            const smallWidth = Math.max(1, Math.floor(w / pixelSize));
            const smallHeight = Math.max(1, Math.floor(h / pixelSize));

            tempPixelCanvas.width = smallWidth;
            tempPixelCanvas.height = smallHeight;

            // Draw original image downscaled into the temporary canvas
            tempPixelCtx.drawImage(originalImg, 0, 0, smallWidth, smallHeight);

            // Now draw from the small temporary canvas to the main outputCanvas, scaled up
            ctx.imageSmoothingEnabled = false;
            ctx.mozImageSmoothingEnabled = false;    // Firefox
            ctx.webkitImageSmoothingEnabled = false; // Chrome, Safari, Opera
            ctx.msImageSmoothingEnabled = false;     // IE, Edge

            ctx.drawImage(tempPixelCanvas, 0, 0, smallWidth, smallHeight, 0, 0, w, h);
        } else {
            // No pixelation, draw original image directly onto outputCanvas
            ctx.drawImage(originalImg, 0, 0, w, h);
        }
    } else {
        // Image has no dimensions, return the empty (possibly 0x0) canvas
        return outputCanvas;
    }
    
    const imageData = ctx.getImageData(0, 0, w, h);
    const data = imageData.data;

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

        // --- 2a. Saturation ---
        if (saturation !== 1.0 && saturation >= 0) { // Allow desaturation (saturation=0)
            const hsl = rgbToHsl(r, g, b);
            hsl[1] *= saturation;
            hsl[1] = Math.max(0, Math.min(1, hsl[1])); // Clamp Saturation to [0, 1]
            const saturatedRgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
            r = saturatedRgb[0];
            g = saturatedRgb[1];
            b = saturatedRgb[2];
        }

        // --- 2b. Posterization / Palette Quantization ---
        if (parsedPalette.length > 0) {
            // Quantize to custom palette
            let minDistSq = Infinity;
            let closestColor = parsedPalette[0]; // Default to first color in palette
            for (const paletteRgb of parsedPalette) {
                const distSq = colorDistanceSquared([r, g, b], paletteRgb);
                if (distSq < minDistSq) {
                    minDistSq = distSq;
                    closestColor = paletteRgb;
                }
                if (minDistSq === 0) break; // Exact match found, no need to check further
            }
            r = closestColor[0];
            g = closestColor[1];
            b = closestColor[2];
        } else if (pLevels >= 2) {
            // Apply posterization if no custom palette and pLevels is valid
            const step = 255 / (pLevels - 1);
            r = Math.round(r / step) * step;
            g = Math.round(g / step) * step;
            b = Math.round(b / step) * step;
        }
        
        // Assign processed and clamped color values
        data[i] = Math.min(255, Math.max(0, Math.round(r)));
        data[i + 1] = Math.min(255, Math.max(0, Math.round(g)));
        data[i + 2] = Math.min(255, Math.max(0, Math.round(b)));
        // Alpha channel (data[i+3]) remains unchanged
    }

    // --- 3. Put modified ImageData back to canvas ---
    ctx.putImageData(imageData, 0, 0);

    return outputCanvas;
}

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 Brutalist Filter Effect Applicator is a versatile online tool designed to transform your images with a unique blend of posterization, increased saturation, and pixelation effects. Users can customize the number of posterization levels, adjust color saturation, specify pixel size, and even utilize custom color palettes to achieve a variety of artistic effects suitable for graphic design, social media content, or creative projects. Whether you’re looking to create bold, graphic-style images or to explore new aesthetics in your visuals, this tool provides a straightforward way to experiment with and apply a distinctive brutalist style to your photographs.

Leave a Reply

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