You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, titleText = "CHARACTER NAME", subtitleText = "A Cinematic Design", filter = "saturate(1.2) contrast(1.1)", letterboxRatio = 0.15, vignetteStrength = 0.7, fontColor = "white") {
const FONT_FAMILY = 'Bebas Neue';
const fontId = `google-font-${FONT_FAMILY.replace(/\s+/g, '-').toLowerCase()}`;
// Dynamically load the Google Font if it hasn't been added to the page yet.
if (!document.getElementById(fontId)) {
const fontUrl = `https://fonts.googleapis.com/css2?family=${FONT_FAMILY.replace(' ', '+')}:wght@400&display=swap`;
const link = document.createElement('link');
link.id = fontId;
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
}
// Wait for the font to be ready for the current rendering pass.
// This will resolve quickly if the font is already loaded and cached.
try {
await document.fonts.load(`1em "${FONT_FAMILY}"`);
} catch (e) {
console.error(`Font ${FONT_FAMILY} could not be loaded. A fallback font will be used.`, e);
}
// 1. Setup Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// 2. Apply filter and draw the original image
// The filter string is applied directly to the context.
// It accepts any valid CSS filter value.
ctx.filter = filter;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Reset filter so it doesn't affect subsequent drawings (vignette, text, etc.)
ctx.filter = 'none';
// 3. Draw Vignette effect
// Clamping the value ensures it's between 0 and 1.
const clampedVignette = Math.max(0, Math.min(1, vignetteStrength));
if (clampedVignette > 0) {
const gradient = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, canvas.width * 0.2, // Inner radius
canvas.width / 2, canvas.height / 2, Math.hypot(canvas.width / 2, canvas.height / 2) // Outer radius (to cover corners)
);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, `rgba(0,0,0,${clampedVignette})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// 4. Draw cinematic letterbox bars
// Clamping the value ensures each bar is at most half the screen height.
const clampedLetterbox = Math.max(0, Math.min(0.5, letterboxRatio));
if (clampedLetterbox > 0) {
const barHeight = canvas.height * clampedLetterbox;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, barHeight); // Top bar
ctx.fillRect(0, canvas.height - barHeight, canvas.width, barHeight); // Bottom bar
}
// 5. Draw Text
ctx.fillStyle = fontColor;
ctx.textAlign = 'center';
// Add a text shadow for better readability against various backgrounds
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
// --- Calculate text positions and sizes ---
const titleFontSize = Math.round(canvas.width / 15);
const subtitleFontSize = Math.round(titleFontSize / 2.2);
// Position the text block above the bottom letterbox bar, with some padding
const bottomPadding = canvas.height * 0.05;
const subtitleY = canvas.height - (canvas.height * clampedLetterbox) - bottomPadding;
const titleY = subtitleY - subtitleFontSize * 1.2;
// --- Draw Main Title ---
ctx.font = `${titleFontSize}px "${FONT_FAMILY}", sans-serif`;
ctx.textBaseline = 'bottom';
ctx.fillText(titleText.toUpperCase(), canvas.width / 2, titleY);
// --- Draw Subtitle ---
ctx.font = `${subtitleFontSize}px "${FONT_FAMILY}", sans-serif`;
ctx.textBaseline = 'bottom';
ctx.fillText(subtitleText.toUpperCase(), canvas.width / 2, subtitleY);
// 6. Return the final canvas element
return canvas;
}
Apply Changes