You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, textTop = "20TH", textMid = "CENTURY", textBot = "YTP COLLAB") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Movie studio intro parodies are typically cinematic. HD resolution.
const width = 1280;
const height = 720;
canvas.width = width;
canvas.height = height;
// 1. Cinematic Sky Background (Dark space to sunset orange)
let skyGrad = ctx.createLinearGradient(0, 0, 0, height);
skyGrad.addColorStop(0, '#000018'); // Deep space
skyGrad.addColorStop(0.3, '#1d1233'); // Purple-ish night
skyGrad.addColorStop(0.7, '#8a3311'); // Sunset orange
skyGrad.addColorStop(1, '#e68a00'); // Bright warm horizon
ctx.fillStyle = skyGrad;
ctx.fillRect(0, 0, width, height);
// 2. Stars
ctx.fillStyle = '#ffffff';
for (let i = 0; i < 200; i++) {
let sx = Math.random() * width;
let sy = Math.random() * height * 0.7; // mostly top 70%
let sr = Math.random() * 1.5;
ctx.globalAlpha = Math.random() * 0.8 + 0.2;
ctx.beginPath();
ctx.arc(sx, sy, sr, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1.0;
// 3. Far City Backdrop (Silhouettes to give scale)
ctx.fillStyle = '#0f0514';
for(let i = 0; i <= width; i += (20 + Math.random() * 30)) {
let hLine = 50 + Math.random() * 250;
let wLine = 20 + Math.random() * 50;
ctx.fillRect(i, height - hLine, wLine, hLine);
}
// 4. Background Searchlights
const drawSearchlight = (x, y, angle, size, opacity) => {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
let grad = ctx.createLinearGradient(0, 0, 0, -1200);
grad.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);
grad.addColorStop(1, 'rgba(255, 255, 255, 0.0)');
ctx.fillStyle = grad;
ctx.beginPath();
ctx.moveTo(-size, 0);
ctx.lineTo(-size * 6, -1200);
ctx.lineTo(size * 6, -1200);
ctx.lineTo(size, 0);
ctx.fill();
ctx.restore();
};
drawSearchlight(350, height, 0.25, 18, 0.5);
drawSearchlight(width - 350, height, -0.35, 18, 0.5);
// 5. Classic Monolith Side Columns
const drawColumn = (x, y, w, h) => {
let grad = ctx.createLinearGradient(x, y, x + w, y);
grad.addColorStop(0, '#331a00');
grad.addColorStop(0.5, '#cc8800');
grad.addColorStop(1, '#331a00');
ctx.fillStyle = grad;
ctx.fillRect(x, y, w, h);
// Capital / Base trim
ctx.fillStyle = '#ffcc00';
ctx.fillRect(x - 5, y - 10, w + 10, 20);
}
// Left and right pillars
drawColumn(200, 420, 70, 300);
drawColumn(330, 470, 70, 250);
drawColumn(width - 270, 420, 70, 300);
drawColumn(width - 400, 470, 70, 250);
// 6. Placed Billboard / Original Image (Centered on top of structure)
// We draw this before tiers so it sits inside/behind the perspective of the text
let pWidth = 380;
let pHeight = 240;
let imgAspect = originalImg.width / originalImg.height;
if (pWidth / imgAspect > pHeight) {
pWidth = pHeight * imgAspect;
} else {
pHeight = pWidth / imgAspect;
}
let py = 400 - pHeight;
let px = (width - pWidth) / 2;
// Faux 3D frame extrusion for image
let baseDepth = 25;
for(let i = baseDepth; i >= 0; i--) {
let c = 120 - i * 2; // Darker at back
ctx.fillStyle = i === 0 ? '#ffcc00' : `rgb(${c + 50}, ${c}, 0)`;
ctx.fillRect(px - 10 + i * 0.4, py - 10 + i, pWidth + 20, pHeight + 20);
}
// Embed the original image onto the sign
ctx.drawImage(originalImg, px, py, pWidth, pHeight);
// Spotlights resting directly on the billboard
ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
ctx.beginPath();
ctx.arc(px + pWidth * 0.8, py + pHeight * 0.3, pWidth * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(px + pWidth * 0.2, py + pHeight * 0.7, pWidth * 0.3, 0, Math.PI * 2);
ctx.fill();
// 7. Render 3D Gold Text
const draw3DText = (text, cx, cy, size, maxW) => {
ctx.save();
ctx.translate(cx, cy);
ctx.font = `bold ${size}px "Arial Black", Impact, sans-serif`;
// Auto-scale to fit inside tier bounds
let textWidth = ctx.measureText(text).width;
if (textWidth > maxW) {
let scale = maxW / textWidth;
ctx.scale(scale, Math.max(0.7, scale)); // Scale X but limit Y squash
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// 3D Extrusion
let depth = size * 0.45;
for (let i = Math.floor(depth); i > 0; i -= 1) {
let c = 220 - (i / depth) * 110;
ctx.fillStyle = `rgb(${Math.floor(c)}, ${Math.floor(c * 0.75)}, 0)`;
ctx.fillText(text, i * 0.3, i);
}
// Front face (Bright Gold)
ctx.fillStyle = '#ffea55';
ctx.fillText(text, 0, 0);
// Front thin stroke for crispness
ctx.lineWidth = size * 0.015;
ctx.strokeStyle = '#fffefe';
ctx.strokeText(text, 0, 0);
ctx.restore();
}
// Stack tiers downwards (Top surface to bottom floor)
const tiers = [
{ y: 400, h: 90, tw: 450, bw: 600, text: textTop, textSize: 60, maxW: 400 },
{ y: 490, h: 100, tw: 600, bw: 750, text: textMid, textSize: 80, maxW: 550 },
{ y: 590, h: 130, tw: 750, bw: 1050, text: textBot, textSize: 100, maxW: 700 }, // Extends to bottom
];
for (let i = 0; i < tiers.length; i++) {
let t = tiers[i];
let cx = width / 2;
// Main structural block
ctx.fillStyle = '#d98c00';
ctx.beginPath();
ctx.moveTo(cx - t.tw/2, t.y);
ctx.lineTo(cx + t.tw/2, t.y);
ctx.lineTo(cx + t.bw/2, t.y + t.h);
ctx.lineTo(cx - t.bw/2, t.y + t.h);
ctx.fill();
// 3D Right Side highlight (Lighting effect)
ctx.fillStyle = 'rgba(255, 255, 255, 0.12)';
ctx.beginPath();
ctx.moveTo(cx, t.y);
ctx.lineTo(cx + t.tw/2, t.y);
ctx.lineTo(cx + t.bw/2, t.y + t.h);
ctx.lineTo(cx, t.y + t.h);
ctx.fill();
// Architectural details (edges)
ctx.lineWidth = 4;
ctx.strokeStyle = '#ffcc00';
ctx.stroke();
// Horizontal ridge details
for(let j = 1; j < 3; j++) {
let fw = t.tw + (t.bw - t.tw) * (j / 3);
let fy = t.y + t.h * (j / 3);
ctx.beginPath();
ctx.moveTo(cx - fw/2, fy);
ctx.lineTo(cx + fw/2, fy);
ctx.strokeStyle = 'rgba(100, 50, 0, 0.4)';
ctx.lineWidth = i === 2 ? 3 : 2;
ctx.stroke();
}
// Drop the 3D Text on exactly this tier
let textCy = t.y + t.h * 0.45; // Center text roughly in the block segment
draw3DText(t.text, cx, textCy, t.textSize, t.maxW);
}
// 8. Front Crossing Searchlights
drawSearchlight(width/2 - 450, height, 0.45, 22, 0.35);
drawSearchlight(width/2 + 450, height, -0.6, 22, 0.35);
// 9. Classic Lens Flare / Bloom Effect (signature meme collab style)
let flareCx = width * 0.7;
let flareCy = height * 0.25;
// Burst core
let flareGrad = ctx.createRadialGradient(flareCx, flareCy, 0, flareCx, flareCy, 250);
flareGrad.addColorStop(0, 'rgba(255, 255, 255, 0.85)');
flareGrad.addColorStop(0.1, 'rgba(255, 220, 100, 0.5)');
flareGrad.addColorStop(1, 'rgba(255, 200, 100, 0.0)');
ctx.fillStyle = flareGrad;
ctx.beginPath();
ctx.arc(flareCx, flareCy, 250, 0, Math.PI * 2);
ctx.fill();
// Secondary lens reflections
ctx.fillStyle = 'rgba(255, 120, 0, 0.2)';
ctx.beginPath();
ctx.arc(flareCx - 130, flareCy + 90, 40, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'rgba(80, 255, 80, 0.15)';
ctx.beginPath();
ctx.arc(flareCx - 220, flareCy + 150, 75, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'rgba(100, 150, 255, 0.2)';
ctx.beginPath();
ctx.arc(flareCx + 180, flareCy - 130, 30, 0, Math.PI * 2);
ctx.fill();
// Final Post-Process Tint (simulating deep intense contrast typical of these videos)
ctx.fillStyle = 'rgba(255, 120, 0, 0.06)';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = 'rgba(255, 255, 255, 0.08)';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes