You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, caption = "Семейный секрет", vintageLevel = "100", addStamp = "1") {
// Parse input parameters
const vLevel = Math.max(0, Math.min(100, Number(vintageLevel))) / 100;
const doStamp = Number(addStamp) === 1;
// Load web font for the handwritten caption
const fontName = 'Marck Script';
try {
if (!document.fonts.check(`1em "${fontName}"`)) {
const fontLink = document.createElement('link');
fontLink.href = `https://fonts.googleapis.com/css2?family=Marck+Script&display=swap`;
fontLink.rel = 'stylesheet';
document.head.appendChild(fontLink);
await document.fonts.load(`40px "${fontName}"`);
}
} catch (e) {
console.warn("Custom cursive font failed to load, falling back to defaults.", e);
}
// Calculate dimensions maintaining aspect ratio (limiting maximum size)
const maxSize = 900;
let scale = 1;
if (originalImg.width > maxSize || originalImg.height > maxSize) {
scale = Math.min(maxSize / originalImg.width, maxSize / originalImg.height);
}
const imgW = originalImg.width * scale;
const imgH = originalImg.height * scale;
// Polaroid frame proportions
const border = Math.max(20, imgW * 0.04);
const bottomBorder = Math.max(80, imgH * 0.18);
const polW = imgW + border * 2;
const polH = imgH + border + bottomBorder;
// Create main canvas with padding to fit shadows and rotation
const padding = 80;
const canvas = document.createElement('canvas');
canvas.width = polW + padding * 2;
canvas.height = polH + padding * 2;
const ctx = canvas.getContext('2d');
// 1. Draw desk/table background
const bgGrad = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, Math.min(canvas.width, canvas.height) * 0.2,
canvas.width / 2, canvas.height / 2, Math.max(canvas.width, canvas.height)
);
bgGrad.addColorStop(0, '#5a5c63'); // lighter center
bgGrad.addColorStop(1, '#111111'); // dark edges
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Context setup for the polaroid paper placement
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
// Add random subtle rotation simulating photo tossed on a desk
const rot = (Math.random() * 4 - 2) * Math.PI / 180;
ctx.rotate(rot);
// Move coordinates to top-left of the polaroid bounds
ctx.translate(-polW / 2, -polH / 2);
// 2. Draw Polaroid drop shadow
ctx.shadowColor = 'rgba(0, 0, 0, 0.7)';
ctx.shadowBlur = 30;
ctx.shadowOffsetX = 12;
ctx.shadowOffsetY = 15;
// 3. Draw Polaroid frame (Paper)
ctx.fillStyle = "#fdfcf7"; // Slightly off-white, aged paper
ctx.fillRect(0, 0, polW, polH);
ctx.shadowColor = 'transparent'; // Reset shadow for inner elements
// 4. Draw the original image with vintage CSS filters
ctx.filter = `sepia(${vLevel * 0.8}) contrast(${1 + vLevel * 0.25}) brightness(${1 - vLevel * 0.15}) grayscale(${vLevel * 0.3})`;
ctx.drawImage(originalImg, border, border, imgW, imgH);
ctx.filter = "none";
// 5. Apply Vignette and Noise to the image area
if (vLevel > 0) {
ctx.save();
ctx.beginPath();
ctx.rect(border, border, imgW, imgH);
ctx.clip(); // Ensure effects don't bleed onto the white frame
// Center vignette gradient
const cx = border + imgW / 2;
const cy = border + imgH / 2;
const maxR = Math.max(imgW, imgH) * 0.75;
const vhGrad = ctx.createRadialGradient(cx, cy, maxR * 0.3, cx, cy, maxR);
vhGrad.addColorStop(0, 'rgba(0,0,0,0)');
vhGrad.addColorStop(1, `rgba(0,0,0,${0.85 * vLevel})`); // Darken edges
ctx.fillStyle = vhGrad;
ctx.fillRect(border, border, imgW, imgH);
// Procedural film grain (noise) via pattern
const nCanvas = document.createElement('canvas');
nCanvas.width = 128;
nCanvas.height = 128;
const nCtx = nCanvas.getContext('2d');
const nData = nCtx.createImageData(128, 128);
for(let i = 0; i < nData.data.length; i += 4) {
const val = Math.random() * 255;
nData.data[i] = val; // R
nData.data[i+1] = val; // G
nData.data[i+2] = val; // B
nData.data[i+3] = 28 * vLevel; // Alpha (Noise intensity)
}
nCtx.putImageData(nData, 0, 0);
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = ctx.createPattern(nCanvas, 'repeat');
ctx.fillRect(border, border, imgW, imgH);
ctx.restore(); // Removes clip and resets composite op
}
// 6. Draw Red "Classified" Stamp (СЕКРЕТНО)
if (doStamp) {
ctx.save();
ctx.translate(border + imgW * 0.85, border + imgH * 0.15);
ctx.rotate(-15 * Math.PI / 180);
ctx.globalCompositeOperation = 'multiply';
const stampText = "СЕКРЕТНО";
const sFontSize = Math.max(20, imgW * 0.05);
ctx.font = `bold ${sFontSize}px 'Courier New', monospace`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.lineWidth = Math.max(2, imgW * 0.006);
ctx.strokeStyle = "rgba(185, 30, 30, 0.8)";
ctx.fillStyle = "rgba(185, 30, 30, 0.8)";
const textMetrics = ctx.measureText(stampText);
const sw = textMetrics.width + 30;
const sh = sFontSize * 1.6;
// Outer stamp frame
ctx.strokeRect(-sw / 2, -sh / 2, sw, sh);
// Inner thin stamp frame
ctx.lineWidth = ctx.lineWidth * 0.5;
const paddingOffset = Math.max(3, imgW * 0.008);
ctx.strokeRect(-sw / 2 + paddingOffset, -sh / 2 + paddingOffset, sw - paddingOffset * 2, sh - paddingOffset * 2);
// Stamp text
ctx.fillText(stampText, 0, 2); // Slight offset for optical center
ctx.restore();
}
// 7. Draw Handwritten Caption below the image
if (caption.trim() !== '') {
const cFontSize = bottomBorder * 0.45;
ctx.fillStyle = "#1b2033"; // Old dark blue pen ink color
ctx.font = `${cFontSize}px "${fontName}", cursive, 'Comic Sans MS', sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.save();
ctx.translate(polW / 2, polH - (bottomBorder / 2));
ctx.rotate(-0.03); // Slight slant characteristic of handwriting
ctx.fillText(caption, 0, 0);
ctx.restore();
}
ctx.restore(); // Restore global context canvas translation/rotation
return canvas;
}
Apply Changes