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!
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.