You can edit the below JavaScript code to customize the image tool.
function processImage(
originalImg,
targetWidth = 256,
targetHeight = 224,
colorDivisor = 64,
addScanlinesStr = "true",
scanlineOpacity = 0.2,
scanlineThickness = 1
) {
// 1. Parameter Sanity Checks and Parsing
// Use 'parsed' prefix for internal variables to distinguish from input parameters.
// Ensure target dimensions are at least 1x1.
const parsedTargetWidth = Math.max(1, parseInt(String(targetWidth), 10) || 256);
const parsedTargetHeight = Math.max(1, parseInt(String(targetHeight), 10) || 224);
// Ensure colorDivisor is at least 1 (1 means no color change).
const parsedColorDivisor = Math.max(1, parseInt(String(colorDivisor), 10) || 64);
// Convert addScanlinesStr to boolean.
const parsedAddScanlines = String(addScanlinesStr).toLowerCase() === 'true';
// Clamp scanlineOpacity between 0 and 1.
const parsedScanlineOpacity = Math.max(0, Math.min(1, parseFloat(String(scanlineOpacity)) || 0.2));
// Ensure scanlineThickness is at least 1.
const parsedScanlineThickness = Math.max(1, parseInt(String(scanlineThickness), 10) || 1);
// Ensure originalImg is a loaded image with valid dimensions.
if (!originalImg || typeof originalImg.width !== 'number' || typeof originalImg.height !== 'number' || originalImg.width === 0 || originalImg.height === 0) {
console.error("processImage: originalImg is invalid, not loaded, or has zero dimensions.");
// Create a placeholder canvas indicating an error.
const errorCanvas = document.createElement('canvas');
// Use parsed target dimensions for error canvas, or fallback to a default size.
errorCanvas.width = parsedTargetWidth > 0 ? parsedTargetWidth : 100;
errorCanvas.height = parsedTargetHeight > 0 ? parsedTargetHeight : 100;
const errorCtx = errorCanvas.getContext('2d');
if (errorCtx) {
errorCtx.fillStyle = '#7f7f7f'; // Gray background
errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
errorCtx.fillStyle = '#ffffff'; // White text
errorCtx.font = '16px Arial';
errorCtx.textAlign = 'center';
errorCtx.textBaseline = 'middle';
errorCtx.fillText('Image Error', errorCanvas.width / 2, errorCanvas.height / 2);
}
return errorCanvas;
}
// 2. Create Low-Resolution Canvas (tempCanvas)
// This canvas will hold the downscaled and color-reduced version of the image.
const tempCanvas = document.createElement('canvas');
tempCanvas.width = parsedTargetWidth;
tempCanvas.height = parsedTargetHeight;
const tempCtx = tempCanvas.getContext('2d');
if (!tempCtx) {
console.error("processImage: Could not get 2D context for temporary canvas.");
// Fallback: return an empty canvas matching original image size if context creation fails.
const fallbackCanvas = document.createElement('canvas');
fallbackCanvas.width = originalImg.width;
fallbackCanvas.height = originalImg.height;
return fallbackCanvas;
}
// 3. Draw Original Image to tempCanvas
// This step performs the initial downscaling to the target SNES-like resolution.
tempCtx.drawImage(originalImg, 0, 0, parsedTargetWidth, parsedTargetHeight);
// 4. Color Reduction
// This simplifies the color palette, characteristic of older game consoles.
if (parsedColorDivisor > 1) { // Only apply if divisor suggests a change.
const imageData = tempCtx.getImageData(0, 0, parsedTargetWidth, parsedTargetHeight);
const data = imageData.data; // This is a Uint8ClampedArray: [R,G,B,A, R,G,B,A, ...]
for (let i = 0; i < data.length; i += 4) {
// Reduce precision of R, G, B components.
data[i] = Math.floor(data[i] / parsedColorDivisor) * parsedColorDivisor; // Red
data[i + 1] = Math.floor(data[i + 1] / parsedColorDivisor) * parsedColorDivisor; // Green
data[i + 2] = Math.floor(data[i + 2] / parsedColorDivisor) * parsedColorDivisor; // Blue
// Alpha channel (data[i+3]) is left untouched to preserve transparency.
}
tempCtx.putImageData(imageData, 0, 0);
}
// 5. Create Output Canvas
// This canvas will display the final, upscaled image.
const outputCanvas = document.createElement('canvas');
outputCanvas.width = originalImg.width; // Match original image dimensions for output.
outputCanvas.height = originalImg.height;
const outputCtx = outputCanvas.getContext('2d');
if (!outputCtx) {
console.error("processImage: Could not get 2D context for output canvas.");
const fallbackCanvas = document.createElement('canvas');
fallbackCanvas.width = originalImg.width;
fallbackCanvas.height = originalImg.height;
return fallbackCanvas;
}
// 6. Disable Image Smoothing
// Crucial for achieving a sharp, pixelated look when upscaling.
outputCtx.imageSmoothingEnabled = false;
// Vendor-specific prefixes (mozImageSmoothingEnabled, webkitImageSmoothingEnabled, msImageSmoothingEnabled)
// are generally not needed for modern browsers but could be added for very old browser support.
// 7. Draw tempCanvas to outputCanvas
// This upscales the low-resolution, processed image to the final output size,
// using nearest-neighbor interpolation due to imageSmoothingEnabled = false.
outputCtx.drawImage(tempCanvas, 0, 0, outputCanvas.width, outputCanvas.height);
// 8. Add Scanlines (Optional)
// Simulates the horizontal lines often visible on CRT displays.
if (parsedAddScanlines && parsedScanlineThickness > 0 && parsedScanlineOpacity > 0) {
outputCtx.fillStyle = `rgba(0, 0, 0, ${parsedScanlineOpacity})`;
// Draw thin, semi-transparent black lines across the image.
// The loop steps by twice the thickness to create a line and then a gap.
for (let y = 0; y < outputCanvas.height; y += (parsedScanlineThickness * 2)) {
outputCtx.fillRect(0, y, outputCanvas.width, parsedScanlineThickness);
}
}
// 9. Return the processed canvas
return outputCanvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image SNES Filter Effect Application allows users to transform standard images into a retro 16-bit video game style reminiscent of the Super Nintendo Entertainment System (SNES). Users can specify target dimensions, color reduction options, and optional scanline effects to emulate the aesthetic of classic video games. This tool is beneficial for creating nostalgic graphics for game projects, digital art, or personal use, with adjustable parameters to customize the intensity of the effect and achieve the desired visual style.