You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, intensity = 20, enableScanlines = 1) {
intensity = Number(intensity) || 0;
const drawScanlines = Number(enableScanlines) === 1;
const canvas = document.createElement('canvas');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// willReadFrequently optimizes for getImageData/putImageData operations
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Draw the initial unmodified image
ctx.drawImage(originalImg, 0, 0);
// If intensity is 0 or less, just return the original image on canvas
if (intensity <= 0) return canvas;
const maxOffset = intensity * 2;
// --- 1. Slicing / Block Displacements (Simulates video tearing) ---
const numBlocks = Math.floor(intensity * 1.5);
for (let i = 0; i < numBlocks; i++) {
const y = (Math.random() * height) | 0;
const blockHeight = ((Math.random() * (height / 10)) | 0) + 2;
const xOffset = ((Math.random() - 0.5) * maxOffset * 3) | 0;
const safeY = Math.min(y, height - 1);
const safeHeight = Math.max(1, Math.min(blockHeight, height - safeY));
const blockData = ctx.getImageData(0, safeY, width, safeHeight);
// Random black dropouts (missing video data)
if (Math.random() < 0.1) {
ctx.fillStyle = 'rgba(0,0,0,0.8)';
ctx.fillRect(0, safeY, width, safeHeight);
}
// Draw the shifted image data
ctx.putImageData(blockData, xOffset, safeY);
// Handle horizontal wrap-around so the edge isn't just cut off
if (xOffset > 0) {
ctx.putImageData(blockData, xOffset - width, safeY);
} else if (xOffset < 0) {
ctx.putImageData(blockData, xOffset + width, safeY);
}
}
// --- 2. Chromatic Aberration (RGB Shift) ---
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const outData = new Uint8ClampedArray(data);
const baseRShift = (intensity * 0.5) | 0;
const baseBShift = -(intensity * 0.5) | 0;
for (let y = 0; y < height; y++) {
let rShift = baseRShift;
let bShift = baseBShift;
// Introduce random jitter for a wavy edge effect
if (Math.random() < 0.05) {
rShift += ((Math.random() - 0.5) * intensity) | 0;
bShift += ((Math.random() - 0.5) * intensity) | 0;
}
const yOffset = y * width;
for (let x = 0; x < width; x++) {
const i = (yOffset + x) << 2; // equivalent to * 4
// Calculate shifted X positions with circular wrap-around handling
let rx = (x - rShift) % width;
if (rx < 0) rx += width;
let bx = (x - bShift) % width;
if (bx < 0) bx += width;
const ri = (yOffset + rx) << 2;
const bi = (yOffset + bx) << 2;
outData[i] = data[ri]; // Set Red from shifted position
// Green channel remains from original position `outData[i+1] = data[i+1]`
outData[i+2] = data[bi+2]; // Set Blue from shifted position
}
}
// Apply the RGB shifted data back to the canvas
ctx.putImageData(new ImageData(outData, width, height), 0, 0);
// --- 3. Digital Artifacts / Noise ---
const noiseCount = (intensity * 5) | 0;
const colors = ['#0ff', '#f0f', '#0f0', '#fff']; // Neon Cyan, Magenta, Green, White
for (let i = 0; i < noiseCount; i++) {
ctx.fillStyle = colors[(Math.random() * colors.length) | 0];
ctx.globalAlpha = Math.random() * 0.4 + 0.1;
const nx = (Math.random() * width) | 0;
const ny = (Math.random() * height) | 0;
const nw = ((Math.random() * intensity * 2) | 0) + 1;
const nh = ((Math.random() * 4) | 0) + 1;
ctx.fillRect(nx, ny, nw, nh);
}
// Reset alpha
ctx.globalAlpha = 1.0;
// --- 4. Scanlines (CRT Video Effect) ---
if (drawScanlines) {
// Calculate dynamic scanline transparency based on overall intensity
const scanAlpha = Math.min(0.25, 0.05 + (intensity * 0.005));
ctx.fillStyle = `rgba(0, 0, 0, ${scanAlpha})`;
// Draw recurring horizontal lines starting from top
for (let y = 0; y < height; y += 3) {
ctx.fillRect(0, y, width, 1);
}
}
return canvas;
}
Apply Changes