You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg,
pixelSize = 6, // Size of the "virtual" pixels. E.g., 6 means a 6x6 block of original pixels becomes one color. Min: 1.
scanlineOpacity = 0.2, // Opacity of scanlines (0.0 to 1.0).
scanlineThickness = 1, // Thickness of scanlines in pixels. Min: 1.
colorQuantization = 64, // Step for color quantization (e.g., 64 means R,G,B values become multiples of 64). Range: 1 to 255. Values <=1 mean no quantization applied here.
vignetteIntensity = 0.3, // Intensity of vignette darkening corners (0.0 to 1.0). 0 means no vignette.
noiseIntensity = 15 // Max random R,G,B channel offset (+/-). Range: 0 to 255.
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { alpha: originalImg.src.endsWith('.png') }); // Preserve alpha if PNG
const w = originalImg.width;
const h = originalImg.height;
if (w === 0 || h === 0) {
// Handle cases where image might not be loaded or is invalid
canvas.width = 1;
canvas.height = 1;
ctx.fillStyle = 'red';
ctx.fillRect(0,0,1,1); // Draw a red pixel indicating error or empty image
console.error("Image Vintage Arcade Cabinet Art Creator: Invalid image dimensions.");
return canvas;
}
canvas.width = w;
canvas.height = h;
// Parameter validation/clamping
pixelSize = Math.max(1, Math.floor(pixelSize));
scanlineOpacity = Math.max(0, Math.min(1, scanlineOpacity));
scanlineThickness = Math.max(1, Math.floor(scanlineThickness));
// colorQuantization: validated inline before use.
vignetteIntensity = Math.max(0, Math.min(1, vignetteIntensity));
noiseIntensity = Math.max(0, Math.min(255, noiseIntensity / 2)); // User provides peak, we use +/- half of it. Halved for +/- effect.
// 1. Pixelation (Low-res effect)
if (pixelSize > 1) {
const tempCanvasSmall = document.createElement('canvas');
const tempCtxSmall = tempCanvasSmall.getContext('2d');
const pixelW = Math.max(1, Math.floor(w / pixelSize));
const pixelH = Math.max(1, Math.floor(h / pixelSize));
tempCanvasSmall.width = pixelW;
tempCanvasSmall.height = pixelH;
tempCtxSmall.drawImage(originalImg, 0, 0, pixelW, pixelH);
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false; // Firefox
ctx.webkitImageSmoothingEnabled = false; // Chrome, Safari
ctx.msImageSmoothingEnabled = false; // IE
ctx.drawImage(tempCanvasSmall, 0, 0, pixelW, pixelH, 0, 0, w, h);
ctx.imageSmoothingEnabled = true; // Reset for other operations
} else {
ctx.drawImage(originalImg, 0, 0, w, h);
}
// Get image data for subsequent pixel manipulations
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
// 2. Color Reduction (Quantization)
const safeColorQuantization = Math.max(1, Math.floor(colorQuantization));
if (safeColorQuantization > 1 && safeColorQuantization <= 255) {
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, Math.max(0, Math.round(data[i] / safeColorQuantization) * safeColorQuantization)); // Red
data[i + 1] = Math.min(255, Math.max(0, Math.round(data[i + 1] / safeColorQuantization) * safeColorQuantization)); // Green
data[i + 2] = Math.min(255, Math.max(0, Math.round(data[i + 2] / safeColorQuantization) * safeColorQuantization)); // Blue
}
}
// 3. Add Noise (subtle grain)
if (noiseIntensity > 0) {
for (let i = 0; i < data.length; i += 4) {
const randVal = (Math.random() - 0.5) * noiseIntensity * 2; // Generates noise between -noiseIntensity and +noiseIntensity
data[i] = Math.min(255, Math.max(0, data[i] + randVal));
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + randVal));
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + randVal));
}
}
// Put modified image data back
ctx.putImageData(imageData, 0, 0);
// 4. Scanlines (drawn on top)
if (scanlineOpacity > 0 && scanlineThickness > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${scanlineOpacity})`;
for (let y = 0; y < h; y += (scanlineThickness * 2)) { // Draw lines, then gap of same thickness
ctx.fillRect(0, y, w, scanlineThickness);
}
}
// 5. Vignette (drawn on top as final overlay)
if (vignetteIntensity > 0) {
const centerX = w / 2;
const centerY = h / 2;
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY); // Distance from center to a corner
// Inner radius: where the vignette effect starts becoming visible.
// Controls how large the "clear" Narea in the center is.
// Example: 0 means vignette starts from center, 0.5 means clear up to 50% of outerRadius.
const innerRadius = outerRadius * (1 - vignetteIntensity * 1.5); // Make central clear area larger for subtle vignettes
const gradient = ctx.createRadialGradient(
centerX, centerY, Math.max(0, innerRadius), // Inner circle (fully transparent part)
centerX, centerY, outerRadius // Outer circle (where vignette is darkest)
);
gradient.addColorStop(0, `rgba(0,0,0,0)`);
// The full vignetteIntensity is applied at the very edge (outerRadius)
gradient.addColorStop(1, `rgba(0,0,0,${vignetteIntensity})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
}
return canvas;
}
Apply Changes