You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
projectName = "UNTITLED MOVIE PROJECT",
director = "Alan Smithee",
status = "POST-PRODUCTION",
date = "2023-11-20"
) {
// Create the canvas
const canvas = document.createElement('canvas');
const width = 1200;
const height = 800;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Color Palette
const bgDark = '#141414';
const panelBg = '#1A1A1A';
const topBarBg = '#222222';
const accentAmber = '#FF9900';
const textLight = '#E0E0E0';
const textGray = '#888888';
const borderDark = '#333333';
// 1. Draw App Background
ctx.fillStyle = bgDark;
ctx.fillRect(0, 0, width, height);
// 2. Draw Top Navigation Bar
ctx.fillStyle = topBarBg;
ctx.fillRect(0, 0, width, 40);
// Top Bar Text / Branding
ctx.fillStyle = accentAmber;
ctx.font = 'bold 18px "Courier New", Courier, monospace';
ctx.textBaseline = 'middle';
ctx.fillText('⚡ STUDIO DB : ASSET MANAGEMENT SYSTEM', 20, 20);
// Fake App Menu
ctx.fillStyle = textGray;
ctx.font = '13px sans-serif';
ctx.fillText('File Edit View Project Assets Timeline Help', 450, 20);
// Default line style for panels
ctx.strokeStyle = borderDark;
ctx.lineWidth = 2;
// 3. Source Monitor / Image Viewer Panel
ctx.fillStyle = panelBg;
ctx.fillRect(30, 70, 720, 480);
ctx.strokeRect(30, 70, 720, 480);
// Panel Label
ctx.fillStyle = accentAmber;
ctx.font = '12px sans-serif';
ctx.fillText('SOURCE MONITOR // PROXY_V1', 35, 60);
// Calculate Image Scaling and Drawing
const maxWidth = 680;
const maxHeight = 440;
const scale = Math.min(maxWidth / originalImg.width, maxHeight / originalImg.height);
const imgW = originalImg.width * scale;
const imgH = originalImg.height * scale;
const imgX = 30 + (720 - imgW) / 2;
const imgY = 70 + (480 - imgH) / 2;
// Draw the actual image
ctx.drawImage(originalImg, imgX, imgY, imgW, imgH);
// Viewer Overlays (Title Safe Area & Timecode)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.lineWidth = 1;
ctx.strokeRect(imgX + imgW * 0.05, imgY + imgH * 0.05, imgW * 0.9, imgH * 0.9); // Action safe
ctx.strokeRect(imgX + imgW * 0.1, imgY + imgH * 0.1, imgW * 0.8, imgH * 0.8); // Title safe
// Timecode Box overlay
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(imgX + imgW / 2 - 70, imgY + imgH - 40, 140, 30);
ctx.fillStyle = '#FFFFFF';
ctx.font = '16px "Courier New", Courier, monospace';
ctx.textAlign = 'center';
ctx.fillText('01:23:45:12', imgX + imgW / 2, imgY + imgH - 25);
ctx.textAlign = 'left';
// 4. Metadata Panel
ctx.fillStyle = panelBg;
ctx.fillRect(780, 70, 390, 480);
ctx.strokeStyle = borderDark;
ctx.lineWidth = 2;
ctx.strokeRect(780, 70, 390, 480);
ctx.fillStyle = accentAmber;
ctx.font = '12px sans-serif';
ctx.fillText('PROJECT METADATA DATABASE', 785, 60);
// Data Fields Loop
const metadataArr = [
{ label: "PROJECT TITLE", value: projectName.toUpperCase() },
{ label: "DIRECTOR", value: director.toUpperCase() },
{ label: "PRODUCTION STATUS", value: status.toUpperCase() },
{ label: "LATEST INGEST DATE", value: date },
{ label: "SOURCE RESOLUTION", value: `${originalImg.width} x ${originalImg.height}` },
{ label: "ASPECT RATIO", value: (originalImg.width / originalImg.height).toFixed(2) + ":1" },
{ label: "TARGET COLOR SPACE", value: "Rec.709 / Log-C" },
{ label: "AUDIO MASTER", value: "48kHz 24-bit 5.1" }
];
let startY = 100;
metadataArr.forEach((item) => {
// Label
ctx.fillStyle = textGray;
ctx.font = '11px sans-serif';
ctx.fillText(item.label, 800, startY);
// Value Box
ctx.fillStyle = '#222222';
ctx.fillRect(800, startY + 8, 350, 32);
ctx.strokeStyle = '#444444';
ctx.lineWidth = 1;
ctx.strokeRect(800, startY + 8, 350, 32);
// Value Text
ctx.fillStyle = textLight;
ctx.font = 'bold 15px "Courier New", Courier, monospace';
// Truncate text if it's too long
let valText = item.value;
if (ctx.measureText(valText).width > 330) {
valText = valText.substring(0, 32) + '...';
}
ctx.fillText(valText, 810, startY + 28);
startY += 52;
});
// Fake Barcode Graphic at bottom of meta panel
ctx.fillStyle = accentAmber;
let barX = 800;
for (let i = 0; i < 30; i++) {
const barW = Math.random() * 4 + 1;
const gap = Math.random() * 3 + 2;
ctx.fillRect(barX, startY + 5, barW, 25);
barX += (barW + gap);
if (barX > 1150) break;
}
// 5. Sequence Timeline Panel
ctx.fillStyle = panelBg;
ctx.fillRect(30, 580, 1140, 190);
ctx.strokeStyle = borderDark;
ctx.lineWidth = 2;
ctx.strokeRect(30, 580, 1140, 190);
ctx.fillStyle = accentAmber;
ctx.font = '12px sans-serif';
ctx.fillText('SEQUENCE TIMELINE', 35, 570);
// Timeline timecode ticks
ctx.fillStyle = textGray;
ctx.font = '10px sans-serif';
for (let t = 40; t < 1160; t += 100) {
ctx.fillText(`01:0${Math.floor((t - 40) / 100)}:00:00`, t, 595);
ctx.fillRect(t, 598, 1, 4);
}
// Track Backgrounds
ctx.fillStyle = topBarBg;
ctx.fillRect(40, 605, 1120, 26); // V2
ctx.fillRect(40, 635, 1120, 26); // V1
ctx.fillRect(40, 665, 1120, 36); // A1
ctx.fillRect(40, 705, 1120, 36); // A2
// Video Clips
ctx.fillStyle = '#2B6282'; // Standard Clip Blue
ctx.fillRect(50, 636, 180, 24);
ctx.fillRect(680, 636, 250, 24);
// Highlighted Active Clip (matching playhead)
ctx.fillStyle = '#3CA4E5';
ctx.fillRect(235, 636, 440, 24);
ctx.strokeStyle = accentAmber;
ctx.lineWidth = 1;
ctx.strokeRect(235, 636, 440, 24);
// V2 Overlay Clip
ctx.fillStyle = '#9C6228'; // Overlay Orange
ctx.fillRect(300, 606, 120, 24);
// Audio Clips
ctx.fillStyle = '#2B8246'; // Standard Audio Green
ctx.fillRect(50, 666, 180, 34);
ctx.fillRect(235, 666, 440, 34);
ctx.fillRect(680, 666, 250, 34);
ctx.fillRect(50, 706, 180, 34);
ctx.fillRect(235, 706, 440, 34);
// Procedural Audio Waveform visual
ctx.strokeStyle = '#124021';
ctx.lineWidth = 1;
ctx.beginPath();
for (let awX = 52; awX < 928; awX += 4) {
// Skip gaps
if (awX > 230 && awX < 235) continue;
if (awX > 675 && awX < 680) continue;
let awH = Math.random() * 14;
ctx.moveTo(awX, 683 - awH);
ctx.lineTo(awX, 683 + awH);
let awH2 = Math.random() * 14;
ctx.moveTo(awX, 723 - awH2);
ctx.lineTo(awX, 723 + awH2);
}
ctx.stroke();
// 6. Playhead Locator
const playheadX = 350;
ctx.strokeStyle = '#E3242B'; // Red Playhead Line
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(playheadX, 580);
ctx.lineTo(playheadX, 770);
ctx.stroke();
// Playhead arrow/head
ctx.fillStyle = '#E3242B';
ctx.beginPath();
ctx.moveTo(playheadX - 10, 580);
ctx.lineTo(playheadX + 10, 580);
ctx.lineTo(playheadX + 10, 590);
ctx.lineTo(playheadX, 600);
ctx.lineTo(playheadX - 10, 590);
ctx.fill();
return canvas;
}
Apply Changes