Please bookmark this page to avoid losing your image tool!

Image Acid Wash 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.
async function processImage(originalImg, bleachLevel = 128, tintColorStr = "224488") {

    // Helper function for hex to RGB conversion (nested for self-containment)
    function _hexToRgb(hexStr) {
        // Remove '#' if present
        let hex = hexStr.startsWith('#') ? hexStr.slice(1) : hexStr;

        // Handle 3-digit hex codes by expanding them
        if (hex.length === 3) {
            hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
        }

        // Check if hex is valid 6-digit format
        if (!/^[0-9A-F]{6}$/i.test(hex)) {
            return null; // Invalid format
        }

        const bigint = parseInt(hex, 16);
        // Check if parsing resulted in a valid number
        if (isNaN(bigint)) {
            return null;
        }
        
        const r = (bigint >> 16) & 255;
        const g = (bigint >> 8) & 255;
        const b = bigint & 255;
        return { r, g, b };
    }

    const canvas = document.createElement('canvas');
    // Use naturalWidth/Height for reliability, fallback to width/height
    const imgWidth = originalImg.naturalWidth > 0 ? originalImg.naturalWidth : originalImg.width;
    const imgHeight = originalImg.naturalHeight > 0 ? originalImg.naturalHeight : originalImg.height;
    
    if (imgWidth === 0 || imgHeight === 0) {
        // Handle cases where image dimensions are not valid
        console.error("Image has zero width or height.");
        return canvas; // Return empty canvas
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;
    
    // Add optimization hint for frequent readback
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        // Handle potential cross-origin/tainted canvas issues
        console.error("Error getting ImageData (canvas may be tainted):", e);
        // Draw an X on the canvas to indicate error and return it
        ctx.strokeStyle = 'red';
        ctx.lineWidth = Math.min(imgWidth, imgHeight) * 0.1; // Responsive line width
        ctx.beginPath();
        ctx.moveTo(0,0);
        ctx.lineTo(imgWidth, imgHeight);
        ctx.moveTo(imgWidth,0);
        ctx.lineTo(0, imgHeight);
        ctx.stroke();
        return canvas; // Return the canvas with the error indicator
    }
    
    const data = imageData.data;

    // Parse user-provided tint color, with fallback to default, then hardcoded default
    let tintRGB = _hexToRgb(tintColorStr);
    if (!tintRGB) {
        console.warn(`Invalid tintColor string "${tintColorStr}", using default parameter #224488.`);
        tintRGB = _hexToRgb("224488"); // Attempt to parse the default parameter string
        if (!tintRGB) { // Absolute fallback if default string itself is somehow bad (should not happen)
             console.warn(`Default tintColor "#224488" also failed parsing, using hardcoded fallback.`);
             tintRGB = {r: 34, g: 68, b: 136}; // {r:0x22, g:0x44, b:0x88}
        }
    }
    const { r: tintR, g: tintG, b: tintB } = tintRGB;

    // Ensure bleachLevel is within [0, 255] range
    bleachLevel = Math.max(0, Math.min(255, bleachLevel));

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

        // Calculate luminance (using Rec. 709 coefficients, common for sRGB)
        const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;

        let finalR, finalG, finalB;

        if (lum > bleachLevel) {
            // This pixel is in the "bleached" range.
            // The denominator (255.0 - bleachLevel) is safe because:
            // - If bleachLevel == 255.0, then (lum > 255.0) is false (since lum <= 255.0), so this branch is not taken.
            // - Thus, bleachLevel < 255.0 here, making (255.0 - bleachLevel) > 0.
            let bleachFactor = (lum - bleachLevel) / (255.0 - bleachLevel);
            
            bleachFactor = Math.max(0, Math.min(1, bleachFactor)); // Clamp, though should be ~[0,1]
            bleachFactor = Math.pow(bleachFactor, 1.5); // Apply curve for bleaching effect

            // Mix tint color with white based on bleachFactor
            finalR = tintR * (1 - bleachFactor) + 255 * bleachFactor;
            finalG = tintG * (1 - bleachFactor) + 255 * bleachFactor;
            finalB = tintB * (1 - bleachFactor) + 255 * bleachFactor;
        } else { // lum <= bleachLevel
            // This pixel is in the "unbleached" or darker range.
            let darkenFactor;
            if (bleachLevel < 1e-9) { // Effectively, bleachLevel is 0.
                                     // Since lum <= bleachLevel, lum must also be ~0.
                darkenFactor = 0.0;  // Make a black pixel stay black.
            } else {
                darkenFactor = lum / bleachLevel; // Scale luminance relative to bleachLevel.
            }
            
            darkenFactor = Math.max(0, Math.min(1, darkenFactor)); // Clamp, though should be ~[0,1]
            darkenFactor = Math.pow(darkenFactor, 0.8); // Apply curve for color intensity

            // Apply tint color scaled by darkenFactor
            finalR = tintR * darkenFactor;
            finalG = tintG * darkenFactor;
            finalB = tintB * darkenFactor;
        }

        // Assign new pixel values. Uint8ClampedArray handles clamping to [0,255].
        data[i]   = Math.floor(finalR);
        data[i+1] = Math.floor(finalG);
        data[i+2] = Math.floor(finalB);
        // Alpha channel (data[i+3]) remains unchanged
    }

    // Put the modified image data back onto the canvas
    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 Acid Wash Filter tool applies a stylistic bleaching effect to images, allowing users to customize the intensity of the bleach and apply a tint color. This tool can transform ordinary photos into artistic visuals that feature a washed-out appearance with a colored hue, making it useful for graphic designers, artists, and social media enthusiasts looking to enhance their uploads with unique filters. Use cases include photo editing for creative projects, social media content, or even designing promotional materials with a vintage or artistic flair.

Leave a Reply

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