You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
glitchIntensity = 5,
localizedSubtitle = "Translated Dub: [Localization Applied]",
vcrDisplay = "PLAY ► SP HIFI STEREO MONO",
searchText = "SEARCHING AUDIO/VIDEO..."
) {
const width = originalImg.width;
const height = originalImg.height;
// Create the main canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw the original image as the base
ctx.drawImage(originalImg, 0, 0, width, height);
// Fetch pixel data to apply VHSRip and Chromatic Aberration effects
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const srcData = new Uint8ClampedArray(data);
// Calculate effect scaling based on image dimensions
const offset = Math.max(1, Math.floor((width * glitchIntensity) / 1000));
const noiseLevel = glitchIntensity * 3;
// Generate random horizontal tracking glitches common in VHS tracking
const trackingBands = [];
for (let i = 0; i < glitchIntensity; i++) {
trackingBands.push({
y: Math.random() * height,
h: Math.random() * 20 + 5,
shift: (Math.random() - 0.5) * offset * 8
});
}
// Apply pixel manipulations
for (let y = 0; y < height; y++) {
let shift = 0;
// Calculate tracking glitch for this row if applicable
for (let i = 0; i < trackingBands.length; i++) {
const band = trackingBands[i];
if (y >= band.y && y <= band.y + band.h) {
shift += band.shift;
}
}
// Add random slight jitter to some scanlines
if (Math.random() < 0.05) {
shift += (Math.random() - 0.5) * offset * 2;
}
shift = Math.floor(shift);
for (let x = 0; x < width; x++) {
const i = (y * width + x) * 4;
// Chromatic aberration (RGB channel shifting)
const rx = Math.min(width - 1, Math.max(0, x + offset + shift));
const gx = Math.min(width - 1, Math.max(0, x + shift));
const bx = Math.min(width - 1, Math.max(0, x - offset + shift));
const ri = (y * width + rx) * 4;
const gi = (y * width + gx) * 4;
const bi = (y * width + bx) * 4;
// Apply static noise
const noise = (Math.random() - 0.5) * noiseLevel;
// Write transformed values
data[i] = Math.min(255, Math.max(0, srcData[ri] + noise)); // Red
data[i + 1] = Math.min(255, Math.max(0, srcData[gi + 1] + noise)); // Green
data[i + 2] = Math.min(255, Math.max(0, srcData[bi + 2] + noise)); // Blue
data[i + 3] = srcData[i + 3]; // Alpha
}
}
// Put modified pixels back
ctx.putImageData(imgData, 0, 0);
// Apply Interlacing / VHS Scanlines overlay
ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
const scanlineThickness = Math.max(1, Math.floor(height / 1000));
for (let y = 0; y < height; y += scanlineThickness * 3) {
ctx.fillRect(0, y, width, scanlineThickness);
}
// Apply CRT Vignette (darken edges)
const gradient = ctx.createRadialGradient(width / 2, height / 2, height / 3, width / 2, height / 2, height);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.6)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Helper function for rendering text elements
const drawText = (text, posX, posY, font, fillStyle, align = 'left', withStroke = false) => {
ctx.font = font;
ctx.textAlign = align;
ctx.textBaseline = 'top';
ctx.fillStyle = fillStyle;
ctx.shadowColor = 'black';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
if (withStroke) {
ctx.lineWidth = Math.max(3, Math.floor(height / 200));
ctx.strokeStyle = 'black';
ctx.strokeText(text, posX, posY);
}
ctx.fillText(text, posX, posY);
};
// Draw VCR OSD (On-Screen Display) text at top left
const osdFontSize = Math.max(16, Math.floor(height / 22));
const osdFont = `bold ${osdFontSize}px "Courier New", Courier, monospace`;
const paddingX = width * 0.05;
const paddingY = height * 0.05;
drawText(vcrDisplay, paddingX, paddingY, osdFont, '#00ff00', 'left', false);
drawText(`> ${searchText}`, paddingX, paddingY + osdFontSize * 1.5, osdFont, '#ffffff', 'left', false);
// Blinking REC Indicator Simulation
const recRadius = Math.max(4, height / 120);
ctx.beginPath();
ctx.arc(paddingX + recRadius, paddingY + osdFontSize * 3 + recRadius, recRadius, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
drawText('REC', paddingX + recRadius * 2 + 10, paddingY + osdFontSize * 3, osdFont, 'red', 'left', false);
// Draw Localization/Dub Translator Subtitles (Bottom Center)
const subFontSize = Math.max(22, Math.floor(height / 15));
const subFont = `italic bold ${subFontSize}px Arial, sans-serif`;
// Support basic multiline via literal \n or logical \n
const subLines = localizedSubtitle.split(/\\n|\n/);
const lineHeight = subFontSize * 1.2;
const startPosY = height - (subLines.length * lineHeight) - (height * 0.08);
for(let i = 0; i < subLines.length; i++) {
drawText(subLines[i], width / 2, startPosY + (i * lineHeight), subFont, '#ffea00', 'center', true);
}
return canvas;
}
Apply Changes