You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
titleText = "Имя Розы",
authorText = "УМБЕРТО ЭКО",
themeTint = "#5b3c29",
titleColor = "#9e1515",
noiseIntensity = 18
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// Apply base filters for a grim, old medieval manuscript / cinematic look
ctx.filter = 'grayscale(80%) sepia(60%) contrast(130%) brightness(75%)';
ctx.drawImage(originalImg, 0, 0, width, height);
ctx.filter = 'none';
// 1. Color Tint Overlay (Multiply)
ctx.globalCompositeOperation = 'multiply';
ctx.fillStyle = themeTint;
ctx.fillRect(0, 0, width, height);
// 2. Heavy Vignette for cinematic drama
const maxDim = Math.max(width, height);
const vignette = ctx.createRadialGradient(
width / 2, height / 2, maxDim * 0.2,
width / 2, height / 2, maxDim * 0.85
);
vignette.addColorStop(0, 'rgba(0, 0, 0, 0)');
vignette.addColorStop(0.5, 'rgba(0, 0, 0, 0.4)');
vignette.addColorStop(1, 'rgba(0, 0, 0, 0.95)');
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'source-over';
// 3. Film Grain / Parchment Noise
const grainCanvas = document.createElement('canvas');
grainCanvas.width = 128;
grainCanvas.height = 128;
const gCtx = grainCanvas.getContext('2d');
const gData = gCtx.createImageData(128, 128);
for (let i = 0; i < gData.data.length; i += 4) {
const val = Math.random() * 255;
gData.data[i] = val;
gData.data[i + 1] = val;
gData.data[i + 2] = val;
gData.data[i + 3] = noiseIntensity;
}
gCtx.putImageData(gData, 0, 0);
ctx.fillStyle = ctx.createPattern(grainCanvas, 'repeat');
ctx.fillRect(0, 0, width, height);
// 4. Load Custom Classic Font (Cormorant SC for Cyrillic support)
const fontFamily = "Cormorant SC";
try {
const fontUrl = `https://fonts.googleapis.com/css2?family=Cormorant+SC:wght@700&display=swap`;
if (!document.querySelector(`link[href="${fontUrl}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
}
// Wait for font with a fallback timeout
await Promise.race([
document.fonts.load(`700 10px "${fontFamily}"`),
new Promise(res => setTimeout(res, 2500))
]);
} catch (err) {
console.warn("Could not load font, falling back to serif.", err);
}
// 5. Typography Layout
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Shadows for text readability
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
ctx.shadowBlur = width * 0.02;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = height * 0.005;
// Draw Author (Top)
const authorSize = Math.max(16, width * 0.04);
ctx.font = `700 ${authorSize}px "${fontFamily}", "Times New Roman", serif`;
ctx.fillStyle = '#e6c8a3'; // Aged parchment color
if (ctx.letterSpacing !== undefined) {
ctx.letterSpacing = `${width * 0.015}px`;
}
const authorY = height * 0.12;
ctx.fillText(authorText, width / 2, authorY);
// Separator line under Author
ctx.shadowBlur = 5;
ctx.beginPath();
const lineY = authorY + authorSize;
ctx.moveTo(width * 0.4, lineY);
ctx.lineTo(width * 0.6, lineY);
ctx.strokeStyle = '#e6c8a3';
ctx.lineWidth = Math.max(1, width * 0.003);
ctx.stroke();
// Draw Title (Bottom) - "Blood red" themed after the Rose motif
const titleSize = Math.max(30, width * 0.12);
ctx.font = `700 ${titleSize}px "${fontFamily}", "Times New Roman", serif`;
ctx.fillStyle = titleColor;
ctx.shadowBlur = width * 0.03;
if (ctx.letterSpacing !== undefined) {
ctx.letterSpacing = `${width * 0.005}px`;
}
// Subtle red glow
ctx.shadowColor = titleColor;
ctx.fillText(titleText, width / 2, height * 0.85);
// Inner standard shadow to give text solid definition
ctx.shadowColor = 'rgba(0,0,0,0.9)';
ctx.shadowBlur = width * 0.01;
ctx.shadowOffsetX = width * 0.002;
ctx.shadowOffsetY = width * 0.002;
ctx.fillText(titleText, width / 2, height * 0.85);
// Reset letter processing
if (ctx.letterSpacing !== undefined) {
ctx.letterSpacing = '0px';
}
return canvas;
}
Apply Changes