Please bookmark this page to avoid losing your image tool!

Family Secret Image Generator

(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.
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;
}

Free Image Tool Creator

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

Description

The Family Secret Image Generator is a creative tool designed to transform standard photos into nostalgic, vintage-style Polaroid snapshots. It applies several aesthetic effects, including sepia tones, film grain, and vignette, to give images an aged look. The tool places the photo within a classic Polaroid-style frame, complete with a handwritten-style caption at the bottom and an optional red ‘Classified’ stamp for a dramatic, mysterious effect. It is ideal for creating themed social media content, digital scrapbooking, or adding a sense of mystery and history to personal memories.

Leave a Reply

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