You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
mainText = "THE CONJURING",
subText = "BASED ON THE TRUE CASE FILES",
tintHex = "#8c7e61",
contrast = 80,
noiseIntensity = 45
) {
// Determine canvas dimensions
const w = originalImg.width;
const h = originalImg.height;
// Create canvas
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(originalImg, 0, 0, w, h);
// Get image data for pixel manipulation
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
// Parse Tint Hex (Fallback to #8c7e61 if invalid)
let tintR = 140, tintG = 126, tintB = 97;
if (/^#([0-9A-F]{3}){1,2}$/i.test(tintHex)) {
let hex = tintHex.substring(1);
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
tintR = parseInt(hex.substring(0, 2), 16);
tintG = parseInt(hex.substring(2, 4), 16);
tintB = parseInt(hex.substring(4, 6), 16);
}
// Calculate contrast factor
const factor = (259 * (contrast + 255)) / (255 * (259 - contrast));
// Process pixels: Grayscale -> Contrast -> Tint -> Screen Noise
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Grayscale
let gray = 0.299 * r + 0.587 * g + 0.114 * b;
// 2. High Contrast
gray = factor * (gray - 128) + 128;
gray = Math.max(0, Math.min(255, gray));
// 3. Tint (Multiply Blend)
let tr = (gray * tintR) / 255;
let tg = (gray * tintG) / 255;
let tb = (gray * tintB) / 255;
// 4. Uniform Noise (Film Grain)
let noise = (Math.random() - 0.5) * noiseIntensity;
data[i] = Math.max(0, Math.min(255, tr + noise));
data[i + 1] = Math.max(0, Math.min(255, tg + noise));
data[i + 2] = Math.max(0, Math.min(255, tb + noise));
}
ctx.putImageData(imgData, 0, 0);
// Add Film Scratches
const numScratches = Math.floor(w * 0.08);
for (let j = 0; j < numScratches; j++) {
let x = Math.random() * w;
let yStart = Math.random() * h;
let length = (Math.random() * 0.4 + 0.1) * h;
ctx.beginPath();
ctx.moveTo(x, yStart);
// Slight vertical tilt for authenticity
ctx.lineTo(x + (Math.random() * 4 - 2), yStart + length);
ctx.strokeStyle = `rgba(220, 220, 200, ${Math.random() * 0.15})`;
ctx.lineWidth = Math.random() * 2 + 0.5;
ctx.stroke();
}
// Apply Heavy Vignette
const cx = w / 2;
const cy = h / 2;
const maxRadius = Math.max(w, h) * 0.8;
const vignette = ctx.createRadialGradient(cx, cy, maxRadius * 0.2, cx, cy, maxRadius);
vignette.addColorStop(0, 'rgba(0, 0, 0, 0)');
vignette.addColorStop(0.5, 'rgba(0, 0, 0, 0.5)');
vignette.addColorStop(1, 'rgba(0, 0, 0, 0.95)');
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, w, h);
// Load Cinematic / Creepy Font (IM Fell English SC closely resembles film titles)
const fontName = 'IM Fell English SC';
if (!document.getElementById('conjuring-font')) {
const link = document.createElement('link');
link.id = 'conjuring-font';
link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(/ /g, '+')}&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
}
try {
await document.fonts.load(`10px "${fontName}"`);
} catch (e) {
// Fallback silently if font fails to load, canvas will use closest serif
}
// Prepare text styling properties
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
if ('letterSpacing' in ctx) {
ctx.letterSpacing = `${Math.max(2, w * 0.008)}px`; // Wide cinematic spacing
}
// Draw Main Title Text
const mainFontSize = Math.max(30, w * 0.06);
ctx.font = `${mainFontSize}px "${fontName}", serif`;
ctx.fillStyle = '#dbceb4'; // Pale off-white/beige
// Add Drop shadow for depth and separation from dark background
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
ctx.shadowBlur = Math.max(5, w * 0.01);
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
const mainY = h / 2 - h * 0.02;
ctx.fillText(mainText.toUpperCase(), cx, mainY);
// Draw Subtext
const subFontSize = Math.max(14, w * 0.022);
ctx.font = `${subFontSize}px "${fontName}", serif`;
ctx.fillStyle = '#a62b2b'; // Dull, vintage blood red
ctx.shadowBlur = Math.max(2, w * 0.005);
const subY = h / 2 + h * 0.07;
ctx.fillText(subText.toUpperCase(), cx, subY);
// Draw a subtle distressed divider line
ctx.shadowColor = 'transparent';
ctx.beginPath();
ctx.moveTo(w * 0.4, h / 2 + h * 0.025);
ctx.lineTo(w * 0.6, h / 2 + h * 0.025);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
ctx.lineWidth = Math.max(1, h * 0.002);
ctx.stroke();
return canvas;
}
Apply Changes