Please bookmark this page to avoid losing your image tool!

Photo Automatic Saturation Fixer

(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, targetFloorSaturation = 0.25, targetAimSaturation = 0.5, maxBoostFactor = 2.0) {
    const canvas = document.createElement('canvas');
    // Use { willReadFrequently: true } for potential performance optimization if available/needed.
    // Some browsers might issue warnings if it's used excessively or not optimally.
    // For simplicity, and broad compatibility, we can omit it if not strictly necessary or causing issues.
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    const width = originalImg.naturalWidth || originalImg.width || 0;
    const height = originalImg.naturalHeight || originalImg.height || 0;

    if (width === 0 || height === 0) {
        canvas.width = 0;
        canvas.height = 0;
        return canvas; // Return empty canvas for empty image
    }

    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // Helper: RGB to HSL conversion
    // r, g, b are in [0, 255]. Output h, s, l are in [0, 1].
    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;
        const 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;
            }
            if (h === undefined) h = 0; // Should not happen with an actual color
            h /= 6;
        }
        return [h, s, l];
    }

    // Helper: HSL to RGB conversion
    // Input h, s, l are in [0, 1]. Output r, g, b are in [0, 255].
    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)];
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        // This can happen if the image is from a different origin and canvas is tainted (CORS)
        console.error("Error getting image data for saturation adjustment:", e);
        // In case of error (e.g. CORS), return the canvas with the original image drawn.
        // The user might see the original image instead of an error/blank.
        return canvas; 
    }
    
    const data = imageData.data;
    let totalSaturation = 0;
    const numPixels = canvas.width * canvas.height;

    if (numPixels === 0) { // Should be caught by width/height check earlier
        return canvas;
    }

    // First pass: Calculate average saturation for ALL pixels
    for (let i = 0; i < data.length; i += 4) {
        const r_val = data[i];
        const g_val = data[i+1];
        const b_val = data[i+2];
        // Alpha data[i+3] is not directly used in saturation calculation, but preserved.
        // We consider all pixels, including transparent ones, for average.
        // Their color (often black {0,0,0} if fully transparent) contributes to average.
        const [, s_val, ] = rgbToHsl(r_val, g_val, b_val); // Only need saturation 's'
        totalSaturation += s_val;
    }

    const currentAvgSat = totalSaturation / numPixels;
    
    let saturationMultiplier = 1.0;

    // Validate and use parameters
    const finalTargetFloor = (typeof targetFloorSaturation === 'number' && targetFloorSaturation >= 0 && targetFloorSaturation <= 1) ? targetFloorSaturation : 0.25;
    const finalTargetAim = (typeof targetAimSaturation === 'number' && targetAimSaturation >= 0 && targetAimSaturation <= 1) ? targetAimSaturation : 0.5;
    const finalMaxBoost = (typeof maxBoostFactor === 'number' && maxBoostFactor >= 1) ? maxBoostFactor : 2.0;

    if (currentAvgSat > 0 && currentAvgSat < finalTargetFloor) { 
        // currentAvgSat > 0: handles pure grayscale images where avg sat is 0, implies no color to boost.
        let boost = finalTargetAim / currentAvgSat;
        saturationMultiplier = Math.min(boost, finalMaxBoost);
        saturationMultiplier = Math.max(saturationMultiplier, 1.0); // Ensure it's an actual boost, not desaturation
    }

    if (saturationMultiplier !== 1.0) {
        // Second pass: Apply saturation adjustment
        for (let i = 0; i < data.length; i += 4) {
            const r_orig = data[i];
            const g_orig = data[i+1];
            const b_orig = data[i+2];
            
            const [h, s, l] = rgbToHsl(r_orig, g_orig, b_orig);

            if (s > 0) { // Only apply to pixels that have some saturation (non-gray)
                        // This also prevents issues if H is undefined for S=0.
                let newS = s * saturationMultiplier;
                newS = Math.max(0, Math.min(1, newS)); // Clamp s_new to [0, 1]
                
                const [nr, ng, nb] = hslToRgb(h, newS, l);
                data[i] = nr;
                data[i+1] = ng;
                data[i+2] = nb;
            }
            // 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 Photo Automatic Saturation Fixer is a tool designed to enhance the color saturation of images. It analyzes the average saturation of an input image and adjusts it to ensure that it meets specified saturation targets. This tool is ideal for photographers and designers looking to improve the visual vibrancy of their images by automatically boosting colors, making images more engaging and aesthetically pleasing. Users can customize parameters to control the desired saturation levels, making it suitable for a variety of images, including photographs and graphics that may appear dull or washed out.

Leave a Reply

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