Please bookmark this page to avoid losing your image tool!

Photo Vibrant Pop 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, saturationBoostParam = 1.4, contrastBoostParam = 1.2) {

    // Ensure parameters are numbers and non-negative
    const saturationBoost = Number(saturationBoostParam);
    const contrastBoost = Number(contrastBoostParam);

    if (isNaN(saturationBoost) || isNaN(contrastBoost) || saturationBoost < 0 || contrastBoost < 0) {
        console.error("Invalid parameters: saturationBoost and contrastBoost must be non-negative numbers.");
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 450; 
        errorCanvas.height = 50;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.font = '14px Arial';
        errCtx.fillStyle = 'red';
        errCtx.fillText('Error: Invalid filter parameters. Boosts must be non-negative numbers.', 10, 30);
        return errorCanvas;
    }

    // Helper function: Clamp value between min and max
    function clamp(value, min, max) {
        return Math.max(min, Math.min(value, max));
    }

    // Helper function: Convert RGB to HSL
    // Inputs r,g,b are 0-255. Outputs h (0-360), s (0-1), l (0-1).
    function rgbToHsl(r, g, b) {
        r /= 255;
        g /= 255;
        b /= 255;

        const max = Math.max(r, g, b);
        const min = Math.min(r, g, b);
        let h, s;
        const l = (max + min) / 2;

        if (max === min) {
            h = s = 0; // Achromatic (grayscale)
        } 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 * 360, s, l];
    }

    // Helper function: Convert HSL to RGB
    // Inputs h (0-360), s (0-1), l (0-1). Outputs r,g,b are 0-255.
    function hslToRgb(h, s, l) {
        let r_out, g_out, b_out;

        if (s === 0) {
            r_out = g_out = b_out = l; // Achromatic
        } else {
            const hueToRgbComponent = (p, q, t_in) => {
                let t = t_in;
                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_channel = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p_channel = 2 * l - q_channel;
            const h_normalized = h / 360; // Normalize h to 0-1 range

            r_out = hueToRgbComponent(p_channel, q_channel, h_normalized + 1 / 3);
            g_out = hueToRgbComponent(p_channel, q_channel, h_normalized);
            b_out = hueToRgbComponent(p_channel, q_channel, h_normalized - 1 / 3);
        }

        return [Math.round(r_out * 255), Math.round(g_out * 255), Math.round(b_out * 255)];
    }

    try {
        // Validate the image object
        if (!originalImg || typeof originalImg.naturalWidth === 'undefined') {
             throw new Error('The provided input is not a valid Image object.');
        }

        // Wait for the image to load if it's not already loaded
        if (!originalImg.complete || originalImg.naturalWidth === 0) {
            await new Promise((resolve, reject) => {
                // Double-check in case it completed synchronously
                if (originalImg.complete && originalImg.naturalWidth !== 0) {
                    resolve();
                    return;
                }
                // If src is not set, it might never load.
                if (!originalImg.src) {
                    reject(new Error("Image source is not set. Cannot load."));
                    return;
                }
                originalImg.onload = resolve;
                originalImg.onerror = () => reject(new Error('Image failed to load (onerror event).'));
            });
        }

        // Final check on dimensions after attempting to load
        if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
            throw new Error('Image has invalid dimensions (0x0) after loading. Cannot process.');
        }

        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Performance hint for frequent getImageData/putImageData

        canvas.width = originalImg.naturalWidth;
        canvas.height = originalImg.naturalHeight;

        // Draw the original image onto the canvas
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

        // Get pixel data
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data; // This is a Uint8ClampedArray

        // Process each pixel
        for (let i = 0; i < data.length; i += 4) {
            let r_val = data[i];
            let g_val = data[i + 1];
            let b_val = data[i + 2];
            // Alpha channel (data[i+3]) is preserved

            // 1. Adjust Saturation using HSL color space
            let [h, s, l] = rgbToHsl(r_val, g_val, b_val);
            
            s *= saturationBoost;
            s = clamp(s, 0, 1); // Ensure saturation stays within [0, 1]
            
            [r_val, g_val, b_val] = hslToRgb(h, s, l);
            
            // 2. Adjust Contrast
            // Formula: NewValue = Factor * (OldValue - MidPoint) + MidPoint
            // MidPoint for 0-255 range is 128.
            r_val = clamp(contrastBoost * (r_val - 128) + 128, 0, 255);
            g_val = clamp(contrastBoost * (g_val - 128) + 128, 0, 255);
            b_val = clamp(contrastBoost * (b_val - 128) + 128, 0, 255);

            // Update pixel data
            data[i] = r_val;
            data[i + 1] = g_val;
            data[i + 2] = b_val;
        }

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

        return canvas;

    } catch (error) {
        console.error("Error processing image:", error.message);
        const errorCanvas = document.createElement('canvas');
        // Adjust width based on message length slightly, or use a fixed sensible width
        const estimatedCharWidth = 8; // Approximate width of a character in 14px Arial
        errorCanvas.width = Math.max(400, error.message.length * estimatedCharWidth);
        errorCanvas.height = 50;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.font = '14px Arial';
        errCtx.fillStyle = 'red';
        errCtx.fillText(error.message, 10, 30);
        return errorCanvas;
    }
}

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 Vibrant Pop Filter is an online image enhancement tool that allows users to boost the saturation and contrast of their images, making colors more vivid and striking. This tool is ideal for photographers, graphic designers, and social media enthusiasts looking to enhance the visual appeal of their images. Users can easily adjust the intensity of saturation and contrast to achieve the desired vibrant effects, making it suitable for personal or professional projects where image enhancement is required.

Leave a Reply

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