You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, textTopLeft = "PLAY \u25BA", textBottomLeft = "HIFI", textBottomRight = "SP 0:00:00", textTopRight = "VHSRip", rgbShift = 5, noiseLevel = 25) {
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0);
// Get image data to apply VHS effects: RGB shift, noise, and scanlines
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
// Create a new Uint8ClampedArray for the result
const resultData = new Uint8ClampedArray(data.length);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const i = (y * w + x) * 4;
// Channel shift (Red shifted left, Blue shifted right, Green remains)
const xR = Math.max(0, x - rgbShift);
const xB = Math.min(w - 1, x + rgbShift);
const iR = (y * w + xR) * 4;
const iB = (y * w + xB) * 4;
let r = data[iR];
let g = data[i + 1];
let b = data[iB + 2];
// Add static noise
const noise = (Math.random() - 0.5) * noiseLevel;
r += noise;
g += noise;
b += noise;
// Simple Scanlines (darken every 3rd line)
if (y % 3 === 0) {
r -= 15;
g -= 15;
b -= 15;
}
resultData[i] = Math.min(255, Math.max(0, r));
resultData[i + 1] = Math.min(255, Math.max(0, g));
resultData[i + 2] = Math.min(255, Math.max(0, b));
resultData[i + 3] = data[i + 3];
}
}
// Add VHS tracking artifact (a horizontal band of distortion and static at the bottom)
const trackingLevel = h - Math.floor(h * 0.15);
const trackingHeight = Math.max(10, Math.floor(h * 0.05));
for (let y = trackingLevel; y < trackingLevel + trackingHeight; y++) {
if (y >= h) break;
// Offset the pixels horizontally for a glitch effect
const offset = Math.floor((Math.random() - 0.5) * w * 0.05);
for (let x = 0; x < w; x++) {
const srcX = Math.min(w - 1, Math.max(0, x + offset));
const i = (y * w + x) * 4;
const srcI = (y * w + srcX) * 4;
// Introduce heavier static in the tracking band
const heavyNoise = (Math.random() - 0.5) * 100;
resultData[i] = Math.min(255, Math.max(0, resultData[srcI] + heavyNoise));
resultData[i + 1] = Math.min(255, Math.max(0, resultData[srcI + 1] + heavyNoise));
resultData[i + 2] = Math.min(255, Math.max(0, resultData[srcI + 2] + heavyNoise));
}
}
imgData.data.set(resultData);
ctx.putImageData(imgData, 0, 0);
// Overlay On-Screen Display (OSD) text resembling VHS metadata
try {
const font = new FontFace('VT323', 'url(https://fonts.gstatic.com/s/vt323/v17/pxiKyp0ihIEF2isQFJXGdg.woff2)');
await font.load();
document.fonts.add(font);
} catch (e) {
console.warn("Font loading failed, falling back to monospace.");
}
// Configure text styling
const fontSize = Math.max(16, Math.floor(h * 0.08));
ctx.font = `${fontSize}px 'VT323', 'Courier New', monospace`;
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.shadowColor = 'black';
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
const marginX = w * 0.05;
const marginY = h * 0.05;
// Top left metadata ("PLAY")
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(textTopLeft, marginX, marginY);
// Bottom left metadata ("HIFI")
ctx.textBaseline = 'bottom';
ctx.fillText(textBottomLeft, marginX, h - marginY);
// Bottom right metadata (timestamp)
ctx.textAlign = 'right';
ctx.fillText(textBottomRight, w - marginX, h - marginY);
// Top right metadata ("VHSRip")
ctx.textBaseline = 'top';
ctx.fillText(textTopRight, w - marginX, marginY);
return canvas;
}
Apply Changes