Please bookmark this page to avoid losing your image tool!

Image Storytelling Tool For Cupid’s Last Arrow Case

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
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;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

This tool transforms your uploaded images into a stylized, detective-themed investigation board display. It automatically places your photo within a Polaroid-style frame complete with tape effects, adds a mysterious dark background with a subtle grid pattern, and overlays dramatic elements such as a title, subtitle, custom notes, and an ‘evidence’ stamp. The final result includes thematic artistic touches like a heart-tipped arrow and subtle ink or splatter effects, making it ideal for creating atmospheric storytelling assets, mystery-themed social media content, or creative digital scrapbooking for role-playing games and themed events.

Leave a Reply

Your email address will not be published. Required fields are marked *