You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, overlayTexts = "UNFRIENDED,THE CONJURING,2027,NO ESCAPE,COMING SOON", animationLoopSeconds = 15) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Process text parameters
let textArr = overlayTexts.split(',');
while(textArr.length < 5) textArr.push("");
let startTime = null;
// Base timestamp set to year 2027 to match the trailer theme
let baseTime = new Date("2027-10-31T23:58:45Z").getTime();
function draw(time) {
if (!startTime) startTime = time;
let elapsed = (time - startTime) / 1000;
let t = elapsed % animationLoopSeconds;
let w = canvas.width;
let h = canvas.height;
// Normalize time to a 15-second cinematic progression
let nt = (t / animationLoopSeconds) * 15;
let brightness = 100;
let invert = 0;
let glitching = false;
let glitchAmount = 0;
let showText = "";
let redOverlay = 0;
let vignette = 0.65;
let grayscale = 40;
let blurAmount = 0;
// Timings for different trailer phases
if (nt < 2) {
// Fade in and first text
brightness = 20 + (nt / 2) * 60;
showText = textArr[0];
if (Math.random() > 0.95) glitching = true;
glitchAmount = w * 0.02;
} else if (nt < 4) {
brightness = 60 + Math.random() * 10;
showText = textArr[1];
if (Math.random() > 0.8) glitching = true;
glitchAmount = w * 0.03;
} else if (nt < 5) {
// Tension drop
brightness = 25;
showText = "";
if (Math.random() > 0.9) glitching = true;
} else if (nt < 7) {
// Red tint & 2027 reveal
brightness = 40 + Math.random() * 20;
redOverlay = 0.3;
showText = textArr[2];
if (Math.random() > 0.6) glitching = true;
glitchAmount = w * 0.05;
} else if (nt < 9) {
brightness = 35;
showText = textArr[3];
if (Math.random() > 0.8) glitching = true;
glitchAmount = w * 0.02;
} else if (nt < 11) {
// Intense Jumpscare phase
if (Math.random() > 0.5) {
invert = 100;
brightness = 150 + Math.random() * 50;
glitching = true;
glitchAmount = w * 0.1;
grayscale = 0;
redOverlay = 0.5;
} else {
invert = 0;
brightness = 30;
redOverlay = 0.2;
}
} else if (nt < 13) {
// Final rapid flash
brightness = 30 + Math.random() * 10;
showText = textArr[4];
if (Math.random() > 0.6) glitching = true;
glitchAmount = w * 0.04;
} else {
// Fade out to black
brightness = Math.max(0, 30 - ((nt - 13) / 2) * 30);
blurAmount = (nt - 13) * 2;
}
// Draw base image with css filters
ctx.filter = `brightness(${brightness}%) invert(${invert}%) grayscale(${grayscale}%) blur(${blurAmount}px)`;
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Apply visual glitch distortion
if (glitching) {
let slices = 5 + Math.random() * 10;
for(let i = 0; i < slices; i++) {
let sy = Math.random() * h;
let sh = Math.random() * (h / 10);
let dx = (Math.random() - 0.5) * glitchAmount;
ctx.drawImage(canvas, 0, sy, w, sh, dx, sy, w, sh);
}
}
// Apply dark red tint
if (redOverlay > 0) {
ctx.fillStyle = `rgba(200, 0, 0, ${redOverlay})`;
ctx.fillRect(0, 0, w, h);
}
// Apply Vignette for scary mood
let gradient = ctx.createRadialGradient(w/2, h/2, Math.min(w,h) * 0.2, w/2, h/2, Math.max(w,h) * 0.8);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, `rgba(0,0,0,${vignette})`);
ctx.fillStyle = gradient;
ctx.fillRect(0,0,w,h);
// Render CRT interlaced scanlines
let scanlineWidth = Math.max(1, Math.floor(h / 300));
ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
for (let y = 0; y < h; y += scanlineWidth * 2) {
ctx.fillRect(0, y, w, scanlineWidth);
}
// Render "Unfriended" style Camcorder UI
let recBlink = (Math.floor(time / 500) % 2 === 0);
let fontSize = Math.max(14, h * 0.04);
ctx.font = `bold ${fontSize}px "Courier New", Courier, monospace`;
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
let pad = Math.max(10, h * 0.05);
// REC indicator
if (recBlink) {
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(pad + fontSize*0.5, pad, fontSize*0.4, 0, Math.PI*2);
ctx.fill();
}
ctx.fillStyle = '#ffffff';
ctx.fillText("REC", pad + fontSize*1.2, pad);
// Timestamp
let dt = new Date(baseTime + elapsed * 1000);
let timeStr = dt.toISOString().substring(11, 19);
ctx.textAlign = 'right';
ctx.fillText(timeStr, w - pad, pad);
// Low Battery Icon
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = Math.max(1, fontSize * 0.1);
let bw = fontSize * 2;
let bh = fontSize;
let bx = w - pad - bw;
let by = h - pad - bh;
ctx.strokeRect(bx, by, bw, bh);
ctx.fillRect(bx + bw, by + bh*0.25, bw*0.1, bh*0.5); // battery tip
// Make battery blink red when recording dot blinks
ctx.fillStyle = recBlink ? '#ff0000' : '#ffffff';
ctx.fillRect(bx + bw*0.1, by + bh*0.1, bw*0.2, bh*0.8);
// Render Cinematic Trailer Text
if (showText && showText.trim() !== "") {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
let titleSize = Math.max(30, h*0.12);
ctx.font = `400 ${titleSize}px Impact, sans-serif`;
let tx = w/2;
let ty = h/2;
if (glitching) {
// Jitter
tx += (Math.random() - 0.5) * (w*0.03);
ty += (Math.random() - 0.5) * (h*0.03);
ctx.fillStyle = (Math.random() > 0.5) ? '#ff0000' : '#ffffff';
} else {
ctx.fillStyle = '#ffffff';
}
ctx.lineWidth = Math.max(1, titleSize*0.02);
ctx.strokeStyle = '#000000';
ctx.shadowColor = '#000000';
ctx.shadowBlur = 15;
ctx.strokeText(showText, tx, ty);
ctx.fillText(showText, tx, ty);
ctx.shadowBlur = 0; // reset
}
// Add TV Grain / Static noise
ctx.fillStyle = 'rgba(255,255,255,0.07)';
let noiseCount = Math.floor(w * h * 0.0002);
for(let i = 0; i < noiseCount; i++) {
let nx = Math.random() * w;
let ny = Math.random() * h;
let ns = Math.random() * 3 + 1;
ctx.fillRect(nx, ny, ns, ns);
}
requestAnimationFrame(draw);
}
// Start animation loop
requestAnimationFrame(draw);
return canvas;
}
Apply Changes