You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, titleText = "THE CONJURING", effectIntensity = "1") {
const intensity = parseFloat(effectIntensity) || 1;
// 1. Dynamically load the "Cinzel" font from Google Fonts for that cinematic horror feel
const fontName = 'Cinzel';
const fontUrl = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@600&display=swap';
// Check if font is already appended
if (!document.querySelector(`link[href="${fontUrl}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
}
try {
// Wait for font to load and be ready
await document.fonts.load(`600 20px "${fontName}"`);
} catch (e) {
console.warn("Cinzel font failed to load, falling back to default serif.");
}
// 2. Setup the target canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
// 3. Draw Background with heavily styled filters (Dark, High Contrast, Sepia, Blurred)
// The Conjuring style: desaturated, greenish-yellow tint, very dark, slightly blurry background
ctx.filter = `grayscale(90%) sepia(${40 * intensity}%) hue-rotate(-15deg) contrast(${200 * intensity}%) brightness(${35 / intensity}%) blur(${Math.max(w, h) * 0.002}px)`;
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// 4. Add Heavy Vignette (Dark edges focusing on the center)
const gradient = ctx.createRadialGradient(w / 2, h / 2, Math.min(w, h) * 0.1, w / 2, h / 2, Math.max(w, h) * 0.7);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.5, `rgba(0, 0, 0, ${0.5 * intensity})`);
gradient.addColorStop(1, `rgba(0, 0, 0, ${0.98 * intensity})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// 5. Generate and Apply Film Grain
const grainCanvas = document.createElement('canvas');
const grainSize = 256;
grainCanvas.width = grainSize;
grainCanvas.height = grainSize;
const gCtx = grainCanvas.getContext('2d');
const imgData = gCtx.createImageData(grainSize, grainSize);
for (let i = 0; i < imgData.data.length; i += 4) {
const noise = Math.random() * 255;
imgData.data[i] = noise; // R
imgData.data[i + 1] = noise; // G
imgData.data[i + 2] = noise; // B
imgData.data[i + 3] = 45 * intensity; // Alpha (Intensity)
}
gCtx.putImageData(imgData, 0, 0);
ctx.globalCompositeOperation = 'overlay';
const pattern = ctx.createPattern(grainCanvas, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, w, h);
// 6. Add Film Scratches
ctx.globalCompositeOperation = 'screen';
ctx.beginPath();
for (let i = 0; i < 25 * intensity; i++) {
const x = Math.random() * w;
ctx.moveTo(x, 0);
// Slight organic angle to the scratch
ctx.lineTo(x + (Math.random() - 0.5) * (w * 0.05), h);
}
ctx.lineWidth = Math.max(1, w * 0.001);
ctx.strokeStyle = `rgba(255, 255, 255, ${0.08 * intensity})`;
ctx.stroke();
ctx.globalCompositeOperation = 'multiply';
ctx.beginPath();
for (let i = 0; i < 35 * intensity; i++) {
const x = Math.random() * w;
ctx.moveTo(x, 0);
ctx.lineTo(x + (Math.random() - 0.5) * (w * 0.05), h);
}
ctx.lineWidth = Math.max(2, w * 0.002);
ctx.strokeStyle = `rgba(0, 0, 0, ${0.3 * intensity})`;
ctx.stroke();
// Reset composite operation to normal for text
ctx.globalCompositeOperation = 'source-over';
// 7. Render Typography
// Space out characters to emulate the classic horror movie title spacing
const cleanText = titleText || "THE CONJURING";
// We add regular spaces between letters. E.g. "T H E C O N J U R I N G"
const spacedText = cleanText.toUpperCase().split('').join(' ').replace(/ /g, ' ');
let fontSize = Math.floor(w / (spacedText.length * 0.65));
if (fontSize > h / 6) fontSize = h / 6; // Cap font size relative to height
ctx.font = `600 ${fontSize}px "${fontName}", serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Add Chromatic Aberration & Edge Blur to Text
const aberrationOffset = fontSize * 0.03;
// Red Channel (Left offset)
ctx.fillStyle = `rgba(255, 0, 0, ${0.5 * intensity})`;
ctx.fillText(spacedText, (w / 2) - aberrationOffset, h / 2);
// Cyan Channel (Right offset)
ctx.fillStyle = `rgba(0, 255, 255, ${0.5 * intensity})`;
ctx.fillText(spacedText, (w / 2) + aberrationOffset, h / 2);
// Main Text Glow
ctx.shadowColor = 'rgba(230, 215, 170, 0.8)'; // Pale yellowish glow
ctx.shadowBlur = fontSize * 0.3;
// Main Text Fill (Dirty, pale yellowish-white)
ctx.fillStyle = '#e6dbc3';
ctx.fillText(spacedText, w / 2, h / 2);
// Disable shadow and draw one overlay text layer for extremely sharp core
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillText(spacedText, w / 2, h / 2);
return canvas;
}
Apply Changes