Please bookmark this page to avoid losing your image tool!

Image Film Grain 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.
function processImage(originalImg, grainAmount = 20, monochromeStr = "false") {
    // Validate and parse grainAmount.
    // It converts the input to a number. If it's NaN (e.g., from "abc") or negative,
    // it defaults to 20. Allows 0 for no grain.
    let parsedGrainAmount = Number(grainAmount);
    if (isNaN(parsedGrainAmount) || parsedGrainAmount < 0) {
        parsedGrainAmount = 20; // Default value if invalid input
    }

    // Parse monochromeStr to a boolean.
    // String(monochromeStr) handles cases like null or undefined safely.
    // Only "true" (case-insensitive) results in true, others (e.g. "false", "0", any other string) become false.
    const monochrome = String(monochromeStr).toLowerCase() === "true";

    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } as a hint to the browser can optimize repeated getImageData/putImageData calls.
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    const imgWidth = originalImg.naturalWidth;
    const imgHeight = originalImg.naturalHeight;

    // If the image has no dimensions (e.g., not loaded or broken),
    // set canvas to 0x0 and return it.
    if (imgWidth === 0 || imgHeight === 0) {
        canvas.width = 0;
        canvas.height = 0;
        // console.warn("Image has zero dimensions. Returning empty canvas.");
        return canvas;
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;

    // Draw the original image onto the canvas.
    try {
        ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    } catch (e) {
        // console.error("Error drawing image to canvas:", e);
        // If drawing fails (e.g. image is corrupt or format not supported), return the empty canvas.
        return canvas;
    }

    // Try to get image data. This can fail due to CORS/tainted canvas issues.
    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        // console.error("Error getting image data (e.g., CORS issue):", e);
        // If getImageData fails, the canvas is tainted. Return the canvas with the original image drawn (no grain).
        return canvas;
    }

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

    // Only apply noise if strength is greater than 0.
    if (noiseStrength > 0) {
        for (let i = 0; i < data.length; i += 4) {
            // RGB components are at i, i+1, i+2. Alpha is at i+3.
            // Alpha channel (data[i+3]) is preserved.

            // Read original pixel values
            let r = data[i];
            let g = data[i+1];
            let b = data[i+2];

            let noiseVal;
            if (monochrome) {
                // Generate a single noise value for R, G, B to make monochrome (grayscale) grain.
                // Noise is centered around 0, ranging from -noiseStrength to +noiseStrength.
                noiseVal = (Math.random() - 0.5) * noiseStrength * 2;
                r += noiseVal;
                g += noiseVal;
                b += noiseVal;
            } else {
                // Generate separate noise values for R, G, B for colored grain.
                r += (Math.random() - 0.5) * noiseStrength * 2;
                g += (Math.random() - 0.5) * noiseStrength * 2;
                b += (Math.random() - 0.5) * noiseStrength * 2;
            }

            // Assign modified RGB values back to the ImageData.
            // Uint8ClampedArray automatically clamps values to the 0-255 range
            // and rounds floating-point numbers to the nearest integer (.5 rounds to nearest even).
            data[i] = r;
            data[i+1] = g;
            data[i+2] = b;
        }
    }

    // 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 Film Grain Filter tool allows users to apply a grain effect to their images, simulating the texture of film photography. Users can control the amount of grain added and choose between a monochrome or colored grain effect. This tool is ideal for photographers and designers looking to enhance their images with a classic film look, adding depth and character to digital photos.

Leave a Reply

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