Please bookmark this page to avoid losing your image tool!

Photo 8-Bit Filter Effect Tool

(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, levels = 6) {
    // 1. Parameter preparation
    // Ensure 'levels' is a valid number, round it, and ensure it's at least 2.
    // 'levels' determines the number of distinct values each color channel (R, G, B) can have.
    let parsedLevels = Number(levels);
    if (isNaN(parsedLevels) || !isFinite(parsedLevels)) {
        // If 'levels' is not a valid number (e.g., "abc", NaN, Infinity),
        // fall back to the default value (6, as defined in the function signature).
        // This ensures robustness if a non-numeric string or invalid number is passed.
        parsedLevels = 6; 
    }
    // Ensure at least 2 levels for meaningful quantization, and round to nearest integer.
    const numLevels = Math.max(2, Math.round(parsedLevels));

    // 2. Create canvas
    const canvas = document.createElement('canvas');
    
    // Determine image dimensions.
    // Use naturalWidth/naturalHeight for HTMLImageElement to get its intrinsic dimensions.
    // Fallback to width/height for other CanvasImageSource types (e.g., another canvas, video)
    // or if naturalWidth/naturalHeight are not available (e.g., image not fully loaded, though
    // it's assumed originalImg is a loaded image object).
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

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

    // If image dimensions are zero (e.g., image not loaded or invalid),
    // no processing can occur. Return an empty (0x0 or default sized) canvas.
    if (imgWidth === 0 || imgHeight === 0) {
        console.warn("Image has zero width or height. Returning an empty canvas.");
        return canvas;
    }

    // 3. Get 2D rendering context
    const ctx = canvas.getContext('2d');
    if (!ctx) {
        // This should ideally not happen in modern browsers unless canvas is explicitly disabled
        // or not supported (e.g., very old browser or non-browser environment).
        console.error("Canvas 2D context is not available. Returning an empty canvas.");
        return canvas;
    }

    // 4. Draw the original image onto the canvas
    try {
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    } catch (e) {
        // This might happen if originalImg is not a valid CanvasImageSource
        // or is in a state that prevents drawing (e.g., certain errors during loading).
        console.error("Error drawing image to canvas:", e);
        // Return the canvas, which might be empty or partially drawn depending on when the error occurred.
        return canvas;
    }
    
    // 5. Get pixel data from the canvas
    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        // This error commonly occurs due to Cross-Origin Resource Sharing (CORS) restrictions.
        // If the image is loaded from a different domain without appropriate CORS headers,
        // the canvas becomes "tainted", and operations like getImageData are disallowed for security reasons.
        console.error("Could not get image data from canvas (often a CORS issue):", e);
        // In this situation, the filter effect cannot be applied.
        // The canvas currently holds the original image (drawn in step 4).
        // Return this canvas with the original image as a fallback.
        return canvas;
    }
    
    const data = imageData.data; // This is a Uint8ClampedArray representing RGBA pixels

    // 6. Apply the color quantization (8-bit like effect)
    // Calculate the 'step' value used for quantizing color channels.
    // With `numLevels` distinct values, there are `numLevels - 1` intervals spanning 0-255.
    // Example: numLevels = 6 (default) -> step = 255 / (6-1) = 51.
    // Possible channel values: 0, 51, 102, 153, 204, 255.
    const step = 255 / (numLevels - 1);

    for (let i = 0; i < data.length; i += 4) {
        // Iterate through each pixel (R, G, B, A components).
        // Quantize the Red, Green, and Blue color channels.
        // The Alpha channel (data[i+3]) is typically left unchanged for this type of filter.
        data[i]   = Math.round(data[i] / step) * step;   // Red component
        data[i+1] = Math.round(data[i+1] / step) * step; // Green component
        data[i+2] = Math.round(data[i+2] / step) * step; // Blue component

        // Note on clamping: The `data` array is a Uint8ClampedArray. When values are assigned
        // to it, they are automatically clamped to the 0-255 range.
        // For example, if `Math.round(data[i] / step) * step` resulted in 255.0000001,
        // it would be clamped to 255. If it resulted in -0.0000001, it would be clamped to 0.
        // The formula `Math.round(value / step) * step` for `value` in [0, 255] should inherently
        // produce results within [0, 255] if floating point precision were perfect.
        // Small deviations might occur, but Uint8ClampedArray handles them.
    }

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

    // 8. Return the canvas element with the applied 8-bit filter effect
    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 8-Bit Filter Effect Tool allows users to apply an 8-bit filter effect to images, reducing the number of distinct colors used to render the image. This is achieved by quantizing the color values of the image to a specified number of levels, enhancing the retro pixel art aesthetic reminiscent of classic video games. Suitable use cases for this tool include creating unique artwork, designing visuals for retro-themed projects, or generating stylized graphics for websites and social media.

Leave a Reply

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