You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, noiseLevel = 25, colorBleed = 3, scanlineIntensity = 0.2, interlacingAmount = 1.5) {
// Ensure parameters are parsed as numbers
noiseLevel = Number(noiseLevel);
colorBleed = Number(colorBleed);
scanlineIntensity = Number(scanlineIntensity);
interlacingAmount = Number(interlacingAmount);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Apply basic soft/washed-out filter characteristic of TVRips or old VHS captures
ctx.filter = 'contrast(0.85) saturate(0.8) blur(0.8px)';
ctx.drawImage(originalImg, 0, 0);
ctx.filter = 'none';
// Pixel manipulation for Color Bleed (Chromatic Aberration), Interlacing, and Noise
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// Fallback if cross-origin image taints the canvas
return canvas;
}
const data = imageData.data;
const copyData = new Uint8ClampedArray(data);
for (let y = 0; y < canvas.height; y++) {
// Simulate interlacing comb artifacts by shifting alternate lines in opposite directions
const interlaceShift = (y % 2 === 0) ? Math.floor(interlacingAmount) : -Math.floor(interlacingAmount);
for (let x = 0; x < canvas.width; x++) {
const i = (y * canvas.width + x) * 4;
// Combine color bleed (RGB separation) and interlacing shifts
const redX = Math.min(canvas.width - 1, Math.max(0, x - colorBleed + interlaceShift));
const redI = (y * canvas.width + redX) * 4;
const greenX = Math.min(canvas.width - 1, Math.max(0, x + interlaceShift));
const greenI = (y * canvas.width + greenX) * 4;
const blueX = Math.min(canvas.width - 1, Math.max(0, x + colorBleed + interlaceShift));
const blueI = (y * canvas.width + blueX) * 4;
data[i] = copyData[redI]; // Red channel
data[i + 1] = copyData[greenI + 1]; // Green channel
data[i + 2] = copyData[blueI + 2]; // Blue channel
// Add TV static noise (Luma overlay)
const noise = (Math.random() - 0.5) * noiseLevel;
data[i] = Math.min(255, Math.max(0, data[i] + noise));
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + noise));
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + noise));
}
}
ctx.putImageData(imageData, 0, 0);
// Apply Horizontal Scanlines (CRT Effect)
ctx.fillStyle = `rgba(0, 0, 0, ${scanlineIntensity})`;
for (let y = 0; y < canvas.height; y += 2) {
ctx.fillRect(0, y, canvas.width, 1);
}
// Apply subtle Vertical RGB Shadow Mask (CRT phosphor effect)
ctx.fillStyle = `rgba(255, 0, 0, ${scanlineIntensity * 0.15})`;
for (let x = 0; x < canvas.width; x += 3) {
ctx.fillRect(x, 0, 1, canvas.height);
}
ctx.fillStyle = `rgba(0, 255, 0, ${scanlineIntensity * 0.15})`;
for (let x = 1; x < canvas.width; x += 3) {
ctx.fillRect(x, 0, 1, canvas.height);
}
ctx.fillStyle = `rgba(0, 0, 255, ${scanlineIntensity * 0.15})`;
for (let x = 2; x < canvas.width; x += 3) {
ctx.fillRect(x, 0, 1, canvas.height);
}
// Apply Tube TV Vignette (Darkening corners on the screen)
const gradient = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, canvas.height * 0.3,
canvas.width / 2, canvas.height / 2, Math.max(canvas.width, canvas.height) * 0.6
);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0.7)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas;
}
Apply Changes