You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
titleText = "Дело о Последней стреле Амура",
subTitleText = "FILE NO. 014-VALENTINE",
noteText = "Who stole the final arrow of love?",
stampText = "EVIDENCE",
accentColor = "#9b111e"
) {
// Create the canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set Dimensions (Investigation board style)
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 900;
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
// 1. Draw Mysterious Dark & Moody Background
const bgGradient = ctx.createRadialGradient(
CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2, 0,
CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2, CANVAS_HEIGHT * 0.8
);
bgGradient.addColorStop(0, '#3a2d2d');
bgGradient.addColorStop(1, '#0f0a0a');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// subtle grid pattern for a "detective board" feel
ctx.strokeStyle = 'rgba(255, 255, 255, 0.03)';
ctx.lineWidth = 1;
for (let x = 0; x <= CANVAS_WIDTH; x += 40) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, CANVAS_HEIGHT);
ctx.stroke();
}
for (let y = 0; y <= CANVAS_HEIGHT; y += 40) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(CANVAS_WIDTH, y);
ctx.stroke();
}
// 2. Draw Top Text (Title and Subtitle)
ctx.fillStyle = '#e8dcc8'; // Vintage parchment looking color
ctx.textAlign = 'center';
// Title
ctx.font = 'bold 42px "Times New Roman", Times, serif';
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = 8;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(titleText, CANVAS_WIDTH / 2, 80);
// Subtitle
ctx.shadowColor = 'transparent';
ctx.font = 'letter-spacing 20px 22px "Courier New", Courier, monospace';
ctx.fillStyle = '#9e8c75';
ctx.fillText(subTitleText, CANVAS_WIDTH / 2, 130);
// 3. Process Original Image & Polaroid Frame
// Max constraints for the photo
const maxImgWidth = 560;
const maxImgHeight = 440;
// Calculate aspect ratio
const imgRatio = originalImg.width / originalImg.height;
let pWidth = maxImgWidth;
let pHeight = pWidth / imgRatio;
if (pHeight > maxImgHeight) {
pHeight = maxImgHeight;
pWidth = pHeight * imgRatio;
}
const paddingX = 20;
const paddingTop = 20;
const paddingBottom = 70; // extra space at bottom for polaroid look
const frameW = pWidth + (paddingX * 2);
const frameH = pHeight + paddingTop + paddingBottom;
const frameX = (CANVAS_WIDTH - frameW) / 2;
const frameY = 200;
// Draw Frame Shadow
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 15;
// Draw Frame Background (Polaroid color)
ctx.fillStyle = '#f7f6ec';
ctx.fillRect(frameX, frameY, frameW, frameH);
// Draw Tape on top-left and top-right holding the photo
function drawTape(x, y, rotation) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rotation);
ctx.fillStyle = 'rgba(235, 230, 215, 0.6)';
ctx.shadowColor = 'rgba(0,0,0,0.2)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.fillRect(-45, -15, 90, 30);
ctx.restore();
}
drawTape(frameX, frameY, -Math.PI / 12);
drawTape(frameX + frameW, frameY, Math.PI / 10);
// Draw Image
ctx.shadowColor = 'transparent'; // Reset shadow for image
ctx.drawImage(originalImg, frameX + paddingX, frameY + paddingTop, pWidth, pHeight);
// Draw Note Text inside the Polaroid
ctx.fillStyle = '#111';
ctx.font = 'italic 20px "Georgia", serif';
ctx.textAlign = 'center';
ctx.fillText(noteText, frameX + (frameW / 2), frameY + frameH - 25, frameW - 20);
// 4. Draw Evidence Stamp
ctx.save();
ctx.translate(frameX + frameW - 60, frameY + 60);
ctx.rotate(Math.PI / 8);
// Stamp border
ctx.strokeStyle = accentColor;
ctx.lineWidth = 4;
ctx.strokeRect(-90, -25, 180, 50);
// Stamp inner text
ctx.fillStyle = accentColor;
ctx.font = 'bold 24px "Courier New", Courier, monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.globalAlpha = 0.8; // Faded stamp look
ctx.fillText(stampText, 0, 0);
ctx.restore();
// 5. Draw Cupid's Last Arrow piercing through everything
ctx.save();
ctx.translate(CANVAS_WIDTH / 2, frameY + frameH / 2); // Center of the frame
ctx.rotate(-Math.PI / 5); // Diagonal upward arrow
const arrowLen = 420;
// Adding shadow to the whole arrow to pop out of the canvas
ctx.shadowColor = 'rgba(0, 0, 0, 0.7)';
ctx.shadowBlur = 8;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// Arrow Shaft (Gold/Wood)
ctx.beginPath();
ctx.moveTo(-arrowLen, 0);
ctx.lineTo(arrowLen, 0);
ctx.strokeStyle = '#cda651';
ctx.lineCap = 'round';
ctx.lineWidth = 6;
ctx.stroke();
// Arrow Fletching (Feathers)
ctx.beginPath();
for(let i = 0; i < 6; i++) {
let fx = -arrowLen + (i * 18);
ctx.moveTo(fx, 0);
ctx.lineTo(fx - 25, -20);
ctx.moveTo(fx, 0);
ctx.lineTo(fx - 25, 20);
ctx.moveTo(fx + 5, 0);
ctx.lineTo(fx - 20, -20);
ctx.moveTo(fx + 5, 0);
ctx.lineTo(fx - 20, 20);
}
ctx.strokeStyle = '#e8dcc8';
ctx.lineWidth = 2;
ctx.stroke();
// Arrow Head (Heart Shaped Tip)
ctx.beginPath();
let hx = arrowLen;
ctx.moveTo(hx, 0);
// Draw heart tip bezier curves
ctx.bezierCurveTo(hx - 20, -35, hx - 50, -20, hx - 30, 0);
ctx.bezierCurveTo(hx - 50, 20, hx - 20, 35, hx, 0);
ctx.fillStyle = accentColor; // The bloody/romantic heart tip
ctx.fill();
// Heart tip metallic stroke
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
// 6. Draw additional subtle ambiance (blood/ink splatters related to the accentColor)
ctx.fillStyle = accentColor;
ctx.globalAlpha = 0.6;
const splatters = [
{ x: CANVAS_WIDTH * 0.8, y: frameY + frameH + 30, r: 6 },
{ x: CANVAS_WIDTH * 0.82, y: frameY + frameH + 45, r: 3 },
{ x: CANVAS_WIDTH * 0.78, y: frameY + frameH + 50, r: 4 },
{ x: CANVAS_WIDTH * 0.2, y: frameY - 20, r: 5 },
{ x: CANVAS_WIDTH * 0.22, y: frameY - 5, r: 2 }
];
splatters.forEach(s => {
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1.0;
return canvas;
}
Apply Changes