You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, title = 'The Grand Showcase', subtitle = 'A Masterpiece Unveiled', curtainColor = '#8B0000', spotlightIntensity = 0.7, fontName = 'Playfair Display') {
/**
* Dynamically loads a Google Font into the document.
* @param {string} font - The name of the font to load.
* @returns {Promise<void>}
*/
const loadFont = async (font) => {
const fontId = `google-font-${font.replace(/\s/g, '-')}`;
if (document.getElementById(fontId)) {
return; // Font already requested
}
try {
const fontUrl = `https://fonts.googleapis.com/css2?family=${font.replace(/ /g, '+')}:wght@400;700&display=swap`;
const link = document.createElement('link');
link.id = fontId;
link.href = fontUrl;
link.rel = 'stylesheet';
document.head.appendChild(link);
// Wait for the font to be ready to use
await document.fonts.load(`12px "${font}"`);
} catch (e) {
console.error(`Could not load font: ${font}`, e);
throw e; // Rethrow to be caught by the caller
}
};
let effectiveFont = fontName;
try {
await loadFont(fontName);
} catch (e) {
console.warn(`Falling back to serif font as '${fontName}' could not be loaded.`);
effectiveFont = 'serif'; // Set a web-safe fallback font
}
// 1. Canvas Setup and Sizing
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const sceneAspectRatio = 16 / 9;
const sceneWidth = Math.max(800, originalImg.width * 1.5);
const sceneHeight = sceneWidth / sceneAspectRatio;
canvas.width = sceneWidth;
canvas.height = sceneHeight;
// Calculate the display size for the image to fit nicely on the stage
const stageAreaWidth = sceneWidth * 0.5;
const stageAreaHeight = sceneHeight * 0.55;
let displayW, displayH;
if (originalImg.width / originalImg.height > stageAreaWidth / stageAreaHeight) {
displayW = stageAreaWidth;
displayH = displayW * (originalImg.height / originalImg.width);
} else {
displayH = stageAreaHeight;
displayW = displayH * (originalImg.width / originalImg.height);
}
const imgX = (canvas.width - displayW) / 2;
const imgY = canvas.height * 0.25;
const floorLevel = imgY + displayH + (canvas.height * 0.05);
// 2. Draw Stage Background
// Dark theater backdrop
const backdropGrad = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
backdropGrad.addColorStop(0, '#3a3a4e');
backdropGrad.addColorStop(1, '#1a1a2a');
ctx.fillStyle = backdropGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Wooden stage floor with perspective
ctx.fillStyle = '#4a2a1a';
ctx.beginPath();
ctx.moveTo(0, floorLevel);
ctx.lineTo(canvas.width, floorLevel);
ctx.lineTo(canvas.width * 1.2, canvas.height);
ctx.lineTo(canvas.width * -0.2, canvas.height);
ctx.closePath();
ctx.fill();
// 3. Draw Image with Shadow and Frame
ctx.save();
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = Math.min(displayW, displayH) * 0.1;
ctx.shadowOffsetY = canvas.height * 0.02;
// Simple black frame
const frameSize = Math.min(displayW, displayH) * 0.03;
ctx.fillStyle = '#111111';
ctx.fillRect(imgX - frameSize, imgY - frameSize, displayW + 2 * frameSize, displayH + 2 * frameSize);
ctx.restore();
// Draw the main image
ctx.drawImage(originalImg, imgX, imgY, displayW, displayH);
// 4. Draw Stage Curtains
const drawCurtain = (isRightSide) => {
const x = isRightSide ? canvas.width : 0;
const dir = isRightSide ? -1 : 1;
const width = canvas.width * 0.22;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x + dir * width * 0.7, 0);
ctx.quadraticCurveTo(x + dir * width * 1.5, canvas.height / 2, x + dir * width * 0.6, canvas.height);
ctx.lineTo(x, canvas.height);
ctx.closePath();
ctx.fillStyle = curtainColor;
ctx.fill();
// Add shading for depth
const shadeGrad = ctx.createLinearGradient(x, 0, x + dir * width, 0);
shadeGrad.addColorStop(0, 'rgba(0,0,0,0.6)');
shadeGrad.addColorStop(0.5, 'rgba(255,255,255,0.1)');
shadeGrad.addColorStop(1, 'rgba(0,0,0,0.4)');
ctx.fillStyle = shadeGrad;
ctx.fill();
};
// Top valance (curtain banner)
const valanceHeight = canvas.height * 0.1;
ctx.fillStyle = curtainColor;
ctx.fillRect(0, 0, canvas.width, valanceHeight);
const valanceShade = ctx.createLinearGradient(0, 0, 0, valanceHeight * 1.5);
valanceShade.addColorStop(0, 'rgba(0,0,0,0.6)');
valanceShade.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = valanceShade;
ctx.fillRect(0, 0, canvas.width, valanceHeight * 1.5);
// Side curtains
drawCurtain(false); // Left
drawCurtain(true); // Right
// 5. Draw Spotlight Vignette
const safeIntensity = Math.max(0, Math.min(1, 1 - spotlightIntensity));
const centerX = canvas.width / 2;
const centerY = imgY + displayH / 2;
const vignette = ctx.createRadialGradient(centerX, centerY, displayW * 0.3, centerX, centerY, canvas.width * 0.8);
vignette.addColorStop(0, 'rgba(0,0,0,0)');
vignette.addColorStop(1, `rgba(0,0,0,${safeIntensity})`);
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 6. Draw Text
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.shadowColor = 'rgba(0,0,0,0.9)';
ctx.shadowBlur = 10;
// Title text
const titleSize = Math.round(canvas.height * 0.06);
ctx.font = `700 ${titleSize}px "${effectiveFont}", serif`;
ctx.fillStyle = '#eaeaea';
ctx.fillText(title, canvas.width / 2, valanceHeight / 2 + titleSize * 0.1);
// Subtitle text
const subtitleSize = Math.round(canvas.height * 0.025);
ctx.font = `400 ${subtitleSize}px "${effectiveFont}", serif`;
ctx.fillStyle = '#cccccc';
const subtitleY = floorLevel + (canvas.height - floorLevel) / 2;
ctx.fillText(subtitle, canvas.width / 2, subtitleY);
return canvas;
}
Apply Changes