You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, line1 = "DATABASE", line2 = "PROJECT", line3 = "STUDIOS") {
// 1. Setup Canvas based on original image dimensions
const canvas = document.createElement('canvas');
const width = originalImg.width || 800;
const height = originalImg.height || 600;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 2. Draw base image
ctx.drawImage(originalImg, 0, 0, width, height);
// 3. Apply Night/Cinematic Tint (Dark Blue Multiply)
ctx.globalCompositeOperation = 'multiply';
const bgGrad = ctx.createLinearGradient(0, 0, 0, height);
bgGrad.addColorStop(0, '#000814');
bgGrad.addColorStop(0.5, '#001d3d');
bgGrad.addColorStop(1, '#003566');
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'source-over';
// 4. Add Stars to the night sky
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = '#ffffff';
for(let i = 0; i < 150; i++) {
let x = Math.random() * width;
let y = Math.random() * (height * 0.7);
let r = Math.random() * (width * 0.002) + 0.5;
let alpha = Math.random() * 0.8 + 0.2;
ctx.globalAlpha = alpha;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1.0;
// 5. Draw Movie Searchlights (Screen blend mode)
function drawBeam(cx, cy, tx, ty, widthAtBase, widthAtTop) {
ctx.save();
let grad = ctx.createLinearGradient(cx, cy, tx, ty);
grad.addColorStop(0, 'rgba(255, 255, 255, 0.25)');
grad.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.fillStyle = grad;
// Calculate perpendicular vectors for the beam's spread
let angle = Math.atan2(ty - cy, tx - cx);
let pdx = Math.cos(angle + Math.PI / 2);
let pdy = Math.sin(angle + Math.PI / 2);
ctx.beginPath();
ctx.moveTo(cx + pdx * widthAtBase / 2, cy + pdy * widthAtBase / 2);
ctx.lineTo(tx + pdx * widthAtTop / 2, ty + pdy * widthAtTop / 2);
ctx.lineTo(tx - pdx * widthAtTop / 2, ty - pdy * widthAtTop / 2);
ctx.lineTo(cx - pdx * widthAtBase / 2, cy - pdy * widthAtBase / 2);
ctx.closePath();
ctx.fill();
ctx.restore();
}
drawBeam(width * 0.1, height * 1.1, width * 0.35, -height * 0.1, width * 0.05, width * 0.4);
drawBeam(width * 0.2, height * 1.1, -width * 0.15, -height * 0.1, width * 0.05, width * 0.4);
drawBeam(width * 0.9, height * 1.1, width * 0.65, -height * 0.1, width * 0.05, width * 0.4);
drawBeam(width * 0.8, height * 1.1, width * 1.15, -height * 0.1, width * 0.05, width * 0.4);
ctx.globalCompositeOperation = 'source-over';
// 6. Add Cinematic Letterbox (Black Bars)
ctx.fillStyle = '#000000';
let barH = Math.max(20, height * 0.12);
ctx.fillRect(0, 0, width, barH);
ctx.fillRect(0, height - barH, width, barH);
// 7. Draw HUD (Movie Slate Info in the bottom bar)
ctx.font = `bold ${Math.max(12, height * 0.03)}px monospace`;
ctx.fillStyle = 'rgba(200, 200, 200, 0.7)';
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
ctx.fillText("SCENE: 1 TAKE: 1", width * 0.02, height - barH / 2);
ctx.textAlign = 'right';
let year = new Date().getFullYear();
ctx.fillText(`PROD. YR: ${year}`, width * 0.98, height - barH / 2);
// 8. Generate "Studio Style" 3D Monolith Text
function getFontSize(text, maxW, maxH) {
if (!text) return 0;
let size = maxH;
ctx.font = `bold ${size}px Impact, sans-serif`;
let w = ctx.measureText(text).width;
if (w > maxW) {
size = size * (maxW / w);
}
return Math.floor(size);
}
function draw3DText(text, x, y, fontSize, depth, dx, dy) {
if (!text || fontSize <= 0) return;
ctx.font = `bold ${fontSize}px Impact, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.lineJoin = 'round'; // Prevent crazy miter spikes on Impact font
// Optimize step resolution for performance on huge images
let step = Math.max(1, depth / 150);
for (let i = depth; i >= 0; i -= step) {
let ox = x + dx * i;
let oy = y + dy * i;
if (i < 0.01) {
// Determine Front Face
let grad = ctx.createLinearGradient(0, oy - fontSize * 0.45, 0, oy + fontSize * 0.45);
grad.addColorStop(0, '#ffffff'); // Catch top light
grad.addColorStop(0.15, '#ffe55c');
grad.addColorStop(0.4, '#c27c00'); // Shadow core
grad.addColorStop(0.65, '#ffd629'); // Golden bounce light
grad.addColorStop(0.85, '#e89e00');
grad.addColorStop(1, '#ffffff'); // Bottom light catch
ctx.fillStyle = grad;
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = fontSize * 0.08;
ctx.shadowOffsetY = fontSize * 0.03;
ctx.fillText(text, ox, oy);
// Fine metallic border on front face
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
ctx.lineWidth = Math.max(1, fontSize * 0.012);
ctx.strokeStyle = '#fffbf0';
ctx.strokeText(text, ox, oy);
} else {
// Determine Extrusion/Sides
let progress = i / depth; // 1 at very back, 0 at very front
let brightness = 35 - progress * 20; // range 35 -> 15 (creates smooth depth shading)
ctx.fillStyle = `hsl(45, 100%, ${brightness}%)`;
ctx.strokeStyle = `hsl(45, 100%, ${Math.max(0, brightness - 5)}%)`;
ctx.lineWidth = step * 1.5 + 1; // Safely cover gaps between steps
ctx.shadowColor = 'transparent';
ctx.fillText(text, ox, oy);
ctx.strokeText(text, ox, oy);
}
}
}
// Apply Transformation for the classic stylized "Monolith" skewed perspective
ctx.save();
ctx.translate(width / 2, height * 0.5);
// Skewing the context up and to the right
ctx.transform(1, -0.12, 0, 1, 0, 0);
// Calculate sizing to keep a pyramid structure
let s1 = getFontSize(line1, width * 0.65, height * 0.12);
let s2 = getFontSize(line2, width * 0.8, height * 0.18);
let s3 = getFontSize(line3, width * 0.9, height * 0.25);
// Draw lines sequentially starting with furthest/topmost (drawn highest relative to text anchor base)
// Depth is fixed straight down relatively (dx=0, dy=1) in the skewed coordinate system
draw3DText(line1, 0, -height * 0.22, s1, s1 * 0.4, 0, 1);
draw3DText(line2, 0, -height * 0.02, s2, s2 * 0.4, 0, 1);
draw3DText(line3, 0, height * 0.20, s3, s3 * 0.4, 0, 1);
ctx.restore();
return canvas;
}
Apply Changes