Please bookmark this page to avoid losing your image tool!

Image Memory Box Creator

(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, caption = "Шкатулка воспоминаний", vintageLevel = 1, showTrinkets = 1) {
    const CW = 1000;
    const CH = 800;
    const canvas = document.createElement('canvas');
    canvas.width = CW;
    canvas.height = CH;
    const ctx = canvas.getContext('2d');

    // Parse parameters
    const vLevel = Math.max(0, Math.min(1, parseFloat(vintageLevel) || 1));
    const showAccessories = parseInt(showTrinkets) === 1;

    // Draw Background Wood Texture
    ctx.fillStyle = '#2a1a12';
    ctx.fillRect(0, 0, CW, CH);
    for (let x = 0; x < CW; x += 3) {
        if (Math.random() > 0.3) {
            ctx.fillStyle = Math.random() > 0.5 ? 'rgba(0,0,0,0.15)' : 'rgba(255,255,255,0.02)';
            ctx.fillRect(x, 0, Math.random() * 3 + 1, CH);
        }
        // Wood knots
        if (Math.random() < 0.003) {
            ctx.save();
            ctx.beginPath();
            ctx.ellipse(x, Math.random() * CH, Math.random() * 6 + 4, Math.random() * 80 + 40, 0, 0, Math.PI * 2);
            ctx.fillStyle = 'rgba(0,0,0,0.2)';
            ctx.fill();
            ctx.restore();
        }
    }
    
    // Deep vignette/shadow for the box interior effect
    const bgGrad = ctx.createRadialGradient(CW / 2, CH / 2, 200, CW / 2, CH / 2, 700);
    bgGrad.addColorStop(0, 'rgba(0,0,0,0)');
    bgGrad.addColorStop(1, 'rgba(0,0,0,0.85)');
    ctx.fillStyle = bgGrad;
    ctx.fillRect(0, 0, CW, CH);

    // Box Inner Frame Edges
    ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
    ctx.fillRect(0, 0, CW, 15);
    ctx.fillRect(0, CH - 15, CW, 15);
    ctx.fillRect(0, 0, 15, CH);
    ctx.fillRect(CW - 15, 0, 15, CH);

    // Helper Functions for Trinkets
    function drawBgPhoto(ctx, x, y, rot) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(rot);
        ctx.shadowColor = 'rgba(0,0,0,0.6)';
        ctx.shadowBlur = 15;
        ctx.shadowOffsetX = 5;
        ctx.shadowOffsetY = 8;
        
        ctx.fillStyle = '#e8e4d6';
        ctx.fillRect(-150, -180, 300, 360);
        ctx.shadowColor = 'transparent';
        
        ctx.fillStyle = '#bbb';
        ctx.fillRect(-130, -160, 260, 260);
        ctx.globalAlpha = 0.35;
        
        // Square crop original image
        const dim = Math.min(originalImg.width, originalImg.height);
        const sx = (originalImg.width - dim) / 2;
        const sy = (originalImg.height - dim) / 2;
        ctx.drawImage(originalImg, sx, sy, dim, dim, -130, -160, 260, 260);
        
        ctx.globalAlpha = 1.0;
        const g = ctx.createRadialGradient(0, -30, 50, 0, -30, 180);
        g.addColorStop(0, 'rgba(0,0,0,0)');
        g.addColorStop(1, 'rgba(0,0,0,0.5)');
        ctx.fillStyle = g;
        ctx.fillRect(-130, -160, 260, 260);
        ctx.restore();
    }

    function drawPocketWatch(ctx, x, y, r) {
        ctx.save();
        ctx.translate(x, y);
        ctx.shadowColor = 'rgba(0,0,0,0.7)';
        ctx.shadowBlur = 15;
        ctx.shadowOffsetX = 5;
        ctx.shadowOffsetY = 8;

        const grad = ctx.createRadialGradient(-r / 3, -r / 3, r / 4, 0, 0, r);
        grad.addColorStop(0, '#f9e8a2');
        grad.addColorStop(0.5, '#cda135');
        grad.addColorStop(1, '#6b4c10');
        
        ctx.beginPath();
        ctx.arc(0, 0, r, 0, Math.PI * 2);
        ctx.fillStyle = grad;
        ctx.fill();

        ctx.beginPath();
        ctx.arc(0, -r - r * 0.15, r * 0.25, 0, Math.PI * 2);
        ctx.strokeStyle = '#cda135';
        ctx.lineWidth = r * 0.1;
        ctx.stroke();
        ctx.shadowColor = 'transparent';

        ctx.beginPath();
        ctx.arc(0, 0, r * 0.85, 0, Math.PI * 2);
        ctx.fillStyle = '#f8f4e6';
        ctx.fill();
        
        const inShadow = ctx.createRadialGradient(0, 0, r * 0.6, 0, 0, r * 0.85);
        inShadow.addColorStop(0, 'rgba(0,0,0,0)');
        inShadow.addColorStop(1, 'rgba(0,0,0,0.5)');
        ctx.fillStyle = inShadow;
        ctx.fill();

        ctx.strokeStyle = '#444';
        ctx.lineWidth = r * 0.03;
        for (let i = 0; i < 12; i++) {
            ctx.save();
            ctx.rotate((Math.PI * 2 / 12) * i);
            ctx.beginPath();
            ctx.moveTo(0, -r * 0.82);
            ctx.lineTo(0, -r * 0.68);
            ctx.stroke();
            ctx.restore();
        }

        ctx.lineCap = 'round';
        ctx.beginPath();
        ctx.lineWidth = r * 0.06;
        ctx.moveTo(0, 0);
        ctx.lineTo(r * 0.3, -r * 0.4);
        ctx.stroke();
        
        ctx.beginPath();
        ctx.lineWidth = r * 0.04;
        ctx.moveTo(0, 0);
        ctx.lineTo(-r * 0.5, r * 0.2);
        ctx.stroke();
        
        ctx.beginPath();
        ctx.arc(0, 0, r * 0.06, 0, Math.PI * 2);
        ctx.fillStyle = '#111';
        ctx.fill();
        ctx.restore();
    }

    function drawPetal(ctx, x, y, rot, s) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(rot);
        ctx.scale(s, s);
        
        ctx.shadowColor = 'rgba(0,0,0,0.6)';
        ctx.shadowBlur = 8;
        ctx.shadowOffsetX = 3;
        ctx.shadowOffsetY = 4;

        const grad = ctx.createLinearGradient(0, 0, 0, -45);
        grad.addColorStop(0, '#5a1515');
        grad.addColorStop(1, '#8b2020');

        ctx.fillStyle = grad;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.bezierCurveTo(20, -15, 30, -35, 0, -45);
        ctx.bezierCurveTo(-30, -35, -20, -15, 0, 0);
        ctx.fill();

        ctx.strokeStyle = 'rgba(0,0,0,0.2)';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(0, -40);
        ctx.stroke();
        ctx.restore();
    }

    function drawTape(ctx, x, y, rot) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(rot);
        
        ctx.shadowColor = 'rgba(0,0,0,0.3)';
        ctx.shadowBlur = 4;
        ctx.shadowOffsetX = 1;
        ctx.shadowOffsetY = 2;

        ctx.fillStyle = 'rgba(235, 230, 205, 0.85)';
        ctx.beginPath();
        ctx.moveTo(-60, -15);
        for (let i = 0; i < 5; i++) ctx.lineTo(-60 + Math.random() * 5, -15 + (30 / 5) * i);
        ctx.lineTo(-60, 15);
        ctx.lineTo(60, 15);
        for (let i = 0; i < 5; i++) ctx.lineTo(60 - Math.random() * 5, 15 - (30 / 5) * i);
        ctx.closePath();
        ctx.fill();

        const lg = ctx.createLinearGradient(-60, 0, 60, 0);
        lg.addColorStop(0, 'rgba(0,0,0,0.03)');
        lg.addColorStop(0.5, 'rgba(255,255,255,0.2)');
        lg.addColorStop(1, 'rgba(0,0,0,0.03)');
        ctx.fillStyle = lg;
        ctx.fill();
        ctx.restore();
    }

    // Render Trinkets
    if (showAccessories) {
        drawBgPhoto(ctx, 320, 480, -0.15);
        drawBgPhoto(ctx, 720, 320, 0.2);
        drawPocketWatch(ctx, 180, 220, 65);

        const petals = [
            { x: 120, y: 550, r: 0.5,  s: 1.0 },
            { x: 230, y: 680, r: 1.2,  s: 0.8 },
            { x: 830, y: 150, r: -0.5, s: 0.9 },
            { x: 890, y: 620, r: 2.1,  s: 1.1 },
            { x: 500, y: 130, r: -1.2, s: 0.7 },
            { x: 910, y: 380, r: 0.8,  s: 0.6 }
        ];
        petals.forEach(p => drawPetal(ctx, p.x, p.y, p.r, p.s));
    }

    // Main Polaroid Setup
    const maxImgDim = 460;
    const scale = Math.min(maxImgDim / originalImg.width, maxImgDim / originalImg.height);
    const imgW = originalImg.width * scale;
    const imgH = originalImg.height * scale;

    const pPadX = 25;
    const pPadTop = 25;
    const pPadBot = 100;
    const pW = imgW + pPadX * 2;
    const pH = imgH + pPadTop + pPadBot;

    // Slightly right-aligned
    const cx = CW / 2 + 30; 
    const cy = CH / 2;

    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(0.06); // casual diagonal placement

    // Polaroid Shadow
    ctx.shadowColor = 'rgba(0,0,0,0.85)';
    ctx.shadowBlur = 35;
    ctx.shadowOffsetX = 15;
    ctx.shadowOffsetY = 20;

    // Polaroid Base
    ctx.fillStyle = '#f9f6ef';
    ctx.fillRect(-pW / 2, -pH / 2, pW, pH);
    ctx.shadowColor = 'transparent';

    ctx.strokeStyle = 'rgba(0,0,0,0.06)';
    ctx.lineWidth = 1;
    ctx.strokeRect(-pW / 2, -pH / 2, pW, pH);

    // Filter and Process Original Image
    const tempCan = document.createElement('canvas');
    tempCan.width = imgW;
    tempCan.height = imgH;
    const tCtx = tempCan.getContext('2d');
    tCtx.drawImage(originalImg, 0, 0, imgW, imgH);
    
    if (vLevel > 0) {
        const d = tCtx.getImageData(0, 0, imgW, imgH);
        const data = d.data;
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i+1];
            const b = data[i+2];

            const tr = (r * 0.393) + (g * 0.769) + (b * 0.189);
            const tg = (r * 0.349) + (g * 0.686) + (b * 0.168);
            const tb = (r * 0.272) + (g * 0.534) + (b * 0.131);

            data[i]   = r + (tr - r) * vLevel;
            data[i+1] = g + (tg - g) * vLevel;
            data[i+2] = b + (tb - b) * vLevel;

            // Grain
            const noise = (Math.random() - 0.5) * 20 * vLevel;
            data[i]   += noise;
            data[i+1] += noise;
            data[i+2] += noise;
        }
        tCtx.putImageData(d, 0, 0);

        // Subtly darkened photo edges
        const vigGrad = tCtx.createRadialGradient(imgW / 2, imgH / 2, Math.min(imgW, imgH) * 0.3, imgW / 2, imgH / 2, Math.max(imgW, imgH) * 0.75);
        vigGrad.addColorStop(0, 'rgba(0,0,0,0)');
        vigGrad.addColorStop(1, `rgba(0,0,0,${0.5 * vLevel})`);
        tCtx.fillStyle = vigGrad;
        tCtx.fillRect(0, 0, imgW, imgH);
    }
    
    // Draw Vintage Image onto Polaroid
    ctx.drawImage(tempCan, -pW / 2 + pPadX, -pH / 2 + pPadTop);

    ctx.strokeStyle = 'rgba(0,0,0,0.15)';
    ctx.strokeRect(-pW / 2 + pPadX, -pH / 2 + pPadTop, imgW, imgH);

    // Add Handwritten Text Label
    ctx.fillStyle = '#222';
    ctx.font = '40px "Segoe Script", "Caveat", "Comic Sans MS", cursive';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    const maxTextWidth = pW - 50;
    const words = String(caption).split(' ');
    let line = '';
    const lines = [];
    for (let n = 0; n < words.length; n++) {
        const testLine = line + words[n] + ' ';
        const metrics = ctx.measureText(testLine);
        if (metrics.width > maxTextWidth && n > 0) {
            lines.push(line);
            line = words[n] + ' ';
        } else {
            line = testLine;
        }
    }
    lines.push(line);
    
    const textCenterY = pH / 2 - pPadBot / 2;
    const totalTextHeight = lines.length * 40;
    const startY = textCenterY - (totalTextHeight / 2) + 20;

    for (let i = 0; i < lines.length; i++) {
        ctx.fillText(lines[i].trim(), 0, startY + (i * 40));
    }

    // Add Retro Timestamp Date
    ctx.fillStyle = '#888';
    ctx.font = '14px "Courier New", Courier, monospace';
    ctx.textAlign = 'right';
    ctx.fillText(new Date().toLocaleDateString(), pW / 2 - pPadX + 10, pH / 2 - 15);

    // Apply Tape
    if (showAccessories) {
        drawTape(ctx, 0, -pH / 2 + 20, -0.04);
    }

    ctx.restore();

    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

The Image Memory Box Creator is a creative tool that transforms your photos into nostalgic, vintage-style Polaroid memories. It places your image into a stylized Polaroid frame with handwritten captions and timestamps, set against a textured wooden background. Users can customize the aesthetic by adjusting the level of vintage effects (such as sepia tones and film grain) and choosing whether to include decorative trinkets like pocket watches, flower petals, and secondary background photos. This tool is perfect for creating sentimental social media posts, digital scrapbooks, or personalized nostalgic keepsakes.

Leave a Reply

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