You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, line1 = "20th", line2 = "CENTURY", line3 = "STUDIO", depth = "40") {
const canvas = document.createElement('canvas');
const width = 800;
const height = 450;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 1. Draw Background
if (originalImg && originalImg.width > 0 && originalImg.height > 0) {
ctx.drawImage(originalImg, 0, 0, width, height);
// Apply a cinematic dark-blue tint over the image
ctx.fillStyle = 'rgba(2, 6, 20, 0.65)';
ctx.fillRect(0, 0, width, height);
} else {
// Fallback: Night sky with stars
let bgGrad = ctx.createLinearGradient(0, 0, 0, height);
bgGrad.addColorStop(0, '#020616');
bgGrad.addColorStop(1, '#0a1d42');
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, width, height);
// Draw stars
ctx.fillStyle = '#ffffff';
for (let i = 0; i < 150; i++) {
let sx = Math.random() * width;
let sy = Math.random() * (height - 50);
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;
}
// 2. Add Background Cinematic Glow
let glow = ctx.createRadialGradient(width / 2, height / 2 + 100, 50, width / 2, height / 2, 400);
glow.addColorStop(0, 'rgba(255, 200, 50, 0.15)');
glow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = glow;
ctx.fillRect(0, 0, width, height);
// 3. Draw Searchlights (intersecting beams)
function drawSearchLight(x, y, angle, length, startWidth, endWidth) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
let grad = ctx.createLinearGradient(0, 0, 0, -length);
grad.addColorStop(0, 'rgba(255, 245, 220, 0.4)');
grad.addColorStop(0.5, 'rgba(255, 245, 220, 0.1)');
grad.addColorStop(1, 'rgba(255, 245, 220, 0)');
ctx.beginPath();
ctx.moveTo(-startWidth / 2, 0);
ctx.lineTo(-endWidth / 2, -length);
ctx.lineTo(endWidth / 2, -length);
ctx.lineTo(startWidth / 2, 0);
ctx.closePath();
ctx.fillStyle = grad;
ctx.fill();
ctx.restore();
}
ctx.globalCompositeOperation = 'screen';
drawSearchLight(150, 480, -Math.PI / 7, 700, 30, 250);
drawSearchLight(650, 480, Math.PI / 7, 700, 30, 250);
drawSearchLight(10, 400, Math.PI / 4, 800, 40, 300);
drawSearchLight(790, 400, -Math.PI / 4, 800, 40, 300);
drawSearchLight(400, 500, 0, 800, 60, 200);
ctx.globalCompositeOperation = 'source-over';
// 4. Volumetric 3D Text Configuration
const planesCount = parseInt(depth) || 40;
// Y-positions and layout to create a "wedding cake" pedestal structure
const elements = [
{ type: 'text', text: line1, size: 70, y: 80, maxW: 400 },
{ type: 'rect', w: 220, h: 12, y: 130 },
{ type: 'text', text: line2, size: 90, y: 195, maxW: 650 },
{ type: 'rect', w: 420, h: 18, y: 260 },
{ type: 'text', text: line3, size: 125, y: 335, maxW: 750 },
{ type: 'rect', w: 580, h: 25, y: 410 },
{ type: 'rect', w: 750, h: 35, y: 445 }
];
// Master Gradients for the Golden Text & Blocks
let gradFront = ctx.createLinearGradient(0, 0, 0, height);
gradFront.addColorStop(0, '#fff9e6');
gradFront.addColorStop(0.2, '#ffcc00');
gradFront.addColorStop(0.5, '#d48800');
gradFront.addColorStop(0.8, '#ffcc00');
gradFront.addColorStop(1, '#ffeb99');
let gradSide = ctx.createLinearGradient(0, 0, 0, height);
gradSide.addColorStop(0, '#5a4600');
gradSide.addColorStop(0.3, '#332400');
gradSide.addColorStop(0.6, '#1a1000');
gradSide.addColorStop(1, '#4a3600');
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// 5. Draw the geometry slices from back to front
// This perfectly handles all intersections and 3D occlusion
for (let i = planesCount; i >= 0; i--) {
let depthFactor = i / planesCount; // 1.0 (back) to 0.0 (front)
ctx.save();
// Define Vanishing Point & Extrusion Distance
// Center the coordinate system for scaling + transformations
let vpX = width / 2;
let vpY = height / 2;
ctx.translate(vpX, vpY);
// Perspective Scale: Back is scaled down
let scale = 1.0 - (depthFactor * 0.35);
// Extrude text backwards and slightly down to simulate point-of-view looking up
let extX = depthFactor * 45;
let extY = depthFactor * -30;
ctx.translate(extX, extY);
ctx.scale(scale, scale);
// Famous cinematic upward-right slope perspective
ctx.transform(1, -0.12, 0, 1, 0, 0);
ctx.translate(-vpX, -vpY); // Move back origin
// Front Face (i=0) glows slightly and has bright edges
let isFront = (i === 0);
if (isFront) {
ctx.shadowColor = 'rgba(255, 200, 50, 0.8)';
ctx.shadowBlur = 15;
} else {
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
}
elements.forEach(el => {
let cx = width / 2;
let cy = el.y;
if (el.type === 'text') {
ctx.font = 'bold ' + el.size + 'px Helvetica, "Arial Black", Impact, sans-serif';
ctx.fillStyle = isFront ? gradFront : gradSide;
ctx.fillText(el.text, cx, cy, el.maxW);
// Stoke bridges the gap between layers and offers edge highlights
ctx.strokeStyle = isFront ? '#ffffcc' : '#110a00';
ctx.lineWidth = isFront ? 1.5 : 3.0; // thicker on sides to fill slice gaps
ctx.strokeText(el.text, cx, cy, el.maxW);
} else if (el.type === 'rect') {
let rx = cx - el.w / 2;
let ry = cy - el.h / 2;
ctx.fillStyle = isFront ? gradFront : gradSide;
ctx.fillRect(rx, ry, el.w, el.h);
ctx.strokeStyle = isFront ? '#ffffcc' : '#110a00';
ctx.lineWidth = isFront ? 1.5 : 3.0;
ctx.strokeRect(rx, ry, el.w, el.h);
}
});
ctx.restore();
}
return canvas;
}
Apply Changes