Please bookmark this page to avoid losing your image tool!

Image Gritty Filter 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, contrastLevel = 1.5, noiseAmount = 20, sharpenIntensity = 0.5, desaturationLevel = 0.2) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

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

    if (width === 0 || height === 0) {
        console.warn("Image has zero width or height. Returning an empty canvas.");
        canvas.width = 0;
        canvas.height = 0;
        return canvas;
    }

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

    try {
        ctx.drawImage(originalImg, 0, 0, width, height);
    } catch (e) {
        console.error("Error drawing image onto canvas. Returning an empty or partially drawn canvas.", e);
        // Depending on the error, canvas might be blank or partially drawn.
        return canvas;
    }
    
    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, width, height);
    } catch (e) {
        console.error("Could not get ImageData, possibly due to CORS or other security restrictions. Original image drawn on canvas is returned.", e);
        // Canvas has the original image drawn, but processing cannot continue.
        return canvas; 
    }

    const data = imageData.data; // This is a Uint8ClampedArray

    // 1. Desaturation
    // Ensure desaturationLevel is within [0, 1]
    const actualDesaturationLevel = Math.max(0, Math.min(1, desaturationLevel));
    if (actualDesaturationLevel > 0) {
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i + 1];
            const b = data[i + 2];
            // Standard luminance calculation
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            // Interpolate between original color and grayscale
            data[i]     = Math.round(r * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
            data[i + 1] = Math.round(g * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
            data[i + 2] = Math.round(b * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
            // Alpha (data[i+3]) remains unchanged
        }
    }

    // 2. Contrast Adjustment
    // Ensure contrastLevel is non-negative. 1.0 is no change.
    const actualContrastLevel = Math.max(0, contrastLevel);
    if (actualContrastLevel !== 1.0) { 
        // Adjust pixel value: factor * (value - 128) + 128
        // This pushes values away from or towards 128 (mid-gray)
        for (let i = 0; i < data.length; i += 4) {
            data[i]     = Math.round(actualContrastLevel * (data[i] - 128) + 128);
            data[i + 1] = Math.round(actualContrastLevel * (data[i + 1] - 128) + 128);
            data[i + 2] = Math.round(actualContrastLevel * (data[i + 2] - 128) + 128);
        }
    }

    // 3. Sharpening
    // Ensure sharpenIntensity is non-negative. 0 means no sharpening.
    const actualSharpenIntensity = Math.max(0, sharpenIntensity); 
    if (actualSharpenIntensity > 0) {
        // Create a copy of the data array for reading, as convolution needs original neighbor values
        const sourceDataForSharpen = new Uint8ClampedArray(data);

        // Sharpening kernel: enhances edges. Sum of weights is 1.
        const kernel = [
            0,                           -actualSharpenIntensity,    0,
            -actualSharpenIntensity,     1 + 4 * actualSharpenIntensity, -actualSharpenIntensity,
            0,                           -actualSharpenIntensity,    0
        ];
        
        const side = 3; // Kernel is 3x3
        const halfSide = 1; // Math.floor(side / 2)

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                let r_sum = 0, g_sum = 0, b_sum = 0;

                for (let ky = 0; ky < side; ky++) {
                    for (let kx = 0; kx < side; kx++) {
                        // Clamp coordinates to be within image bounds
                        const pixelY = Math.min(height - 1, Math.max(0, y + ky - halfSide));
                        const pixelX = Math.min(width - 1, Math.max(0, x + kx - halfSide));
                        
                        const srcOffset = (pixelY * width + pixelX) * 4;
                        const weight = kernel[ky * side + kx];

                        if (weight === 0) continue; // Optimization for sparse kernels

                        r_sum += sourceDataForSharpen[srcOffset] * weight;
                        g_sum += sourceDataForSharpen[srcOffset + 1] * weight;
                        b_sum += sourceDataForSharpen[srcOffset + 2] * weight;
                    }
                }

                const dstOffset = (y * width + x) * 4;
                data[dstOffset]     = Math.round(r_sum);
                data[dstOffset + 1] = Math.round(g_sum);
                data[dstOffset + 2] = Math.round(b_sum);
                // Alpha (data[dstOffset + 3]) is preserved from before this sharpening step
            }
        }
    }

    // 4. Adding Noise
    // Ensure noiseAmount is non-negative
    const actualNoiseAmount = Math.max(0, noiseAmount);
    if (actualNoiseAmount > 0) {
        for (let i = 0; i < data.length; i += 4) {
            // Generate noise in the range [-actualNoiseAmount, actualNoiseAmount]
            const noise = (Math.random() - 0.5) * 2 * actualNoiseAmount; 
            data[i]     = Math.round(data[i] + noise);
            data[i + 1] = Math.round(data[i + 1] + noise);
            data[i + 2] = Math.round(data[i + 2] + noise);
            // Alpha (data[i+3]) remains unchanged
        }
    }

    // Put the modified pixel data back onto the canvas
    // The Uint8ClampedArray automatically handles clamping values to [0, 255]
    // and rounding upon assignment if Math.round wasn't used. Explicit Math.round is often clearer.
    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 Gritty Filter Applicator is an online tool designed to enhance images by applying a gritty filter effect. Users can adjust various parameters such as contrast, noise, sharpening, and desaturation levels to achieve their desired artistic effect. This tool is particularly useful for photographers, graphic designers, and social media enthusiasts looking to give their images a more textured, dramatic, or vintage appearance. Applications include creating unique artwork, enhancing photos for personal use, or preparing imagery for social media posts.

Leave a Reply

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