Please bookmark this page to avoid losing your image tool!

Photo Parents Reunion Maker

(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, secondImageBase64 = "", reunionText = "A Beautiful Reunion", theme = "vintage") {
    // Dynamically load Google Font for elegant typography
    const fontFamily = 'Playfair Display';
    if (!document.getElementById('reunion-font-link')) {
        const fontLink = document.createElement('link');
        fontLink.id = 'reunion-font-link';
        fontLink.rel = 'stylesheet';
        fontLink.href = 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@1,600&display=swap';
        document.head.appendChild(fontLink);
    }

    try {
        await document.fonts.load(`italic 600 50px "${fontFamily}"`);
    } catch (err) {
        console.warn("Custom font loading failed, using fallback serif.");
    }

    // Load the second parent/family image or fallback to original if empty or invalid
    let img2 = originalImg;
    if (secondImageBase64 && secondImageBase64.trim() !== "") {
        img2 = await new Promise((resolve) => {
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.onload = () => resolve(img);
            img.onerror = () => resolve(originalImg);
            img.src = secondImageBase64;
        });
    }

    // Setup Canvas
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 1000;
    canvas.height = 700;

    // Determine theme configurations
    let themeProps = {};
    switch (theme.toLowerCase()) {
        case "modern":
            themeProps = {
                bgInner: '#ffffff',
                bgOuter: '#cdd3d8',
                textColor: '#2c3e50',
                filter: 'grayscale(0.9)',
                tapeColor: 'rgba(255, 255, 255, 0.8)'
            };
            break;
        case "romantic":
            themeProps = {
                bgInner: '#fff0f5',
                bgOuter: '#ffb6c1',
                textColor: '#c2185b',
                filter: 'contrast(1.1) saturate(1.3)',
                tapeColor: 'rgba(255, 220, 230, 0.8)'
            };
            break;
        case "vintage":
        default:
            themeProps = {
                bgInner: '#4a3b32',
                bgOuter: '#1e140d',
                textColor: '#e8d5b5',
                filter: 'sepia(0.7) contrast(1.1)',
                tapeColor: 'rgba(245, 222, 179, 0.7)'
            };
            break;
    }

    // Draw Radial Background
    const bgGradient = ctx.createRadialGradient(
        canvas.width / 2, canvas.height / 2, 100, 
        canvas.width / 2, canvas.height / 2, 700
    );
    bgGradient.addColorStop(0, themeProps.bgInner);
    bgGradient.addColorStop(1, themeProps.bgOuter);
    ctx.fillStyle = bgGradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Helper: Draw image containing object-fit 'cover' aspect ratio logic
    function drawImageCover(ctx, img, x, y, w, h) {
        const imgRatio = img.width / img.height;
        const boxRatio = w / h;
        let drawW, drawH, drawX, drawY;

        if (imgRatio > boxRatio) {
            drawH = h;
            drawW = img.width * (h / img.height);
            drawX = x + (w - drawW) / 2;
            drawY = y;
        } else {
            drawW = w;
            drawH = img.height * (w / img.width);
            drawX = x;
            drawY = y + (h - drawH) / 2;
        }

        ctx.drawImage(img, drawX, drawY, drawW, drawH);
    }

    // Helper: Draw a polaroid style frame with the image
    function drawPolaroid(imageToDraw, cx, cy, angleDeg, flipHorizontally = false) {
        ctx.save();
        ctx.translate(cx, cy);
        ctx.rotate(angleDeg * Math.PI / 180);

        // Frame Shadow
        ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
        ctx.shadowBlur = 20;
        ctx.shadowOffsetX = 8;
        ctx.shadowOffsetY = 12;

        // Polaroid Card Base
        ctx.fillStyle = '#fefdfb';
        ctx.fillRect(-170, -200, 340, 410);

        // Remove shadow for inner elements
        ctx.shadowColor = 'transparent';

        // Inner frame recess (to simulate photo paper boundary)
        ctx.fillStyle = '#eaeaea';
        ctx.fillRect(-150, -180, 300, 300);

        // Optional Flip for creative mirroring if the same image is duplicated
        if (flipHorizontally) {
            ctx.scale(-1, 1);
        }

        // Apply theme filters to the image
        ctx.filter = themeProps.filter;

        // Clip region for drawing the photo
        ctx.beginPath();
        ctx.rect(flipHorizontally ? -150 : -150, -180, 300, 300);
        ctx.clip();
        
        // Draw image inside specific boundaries
        drawImageCover(ctx, imageToDraw, flipHorizontally ? -150 : -150, -180, 300, 300);
        
        // Reset clipping and transformations for tape
        ctx.restore();
        ctx.save();
        ctx.translate(cx, cy);
        ctx.rotate(angleDeg * Math.PI / 180);

        // Draw masking tape attached at the top of the polaroid
        ctx.fillStyle = themeProps.tapeColor;
        ctx.shadowColor = 'rgba(0,0,0,0.2)';
        ctx.shadowBlur = 5;
        ctx.shadowOffsetX = 2;
        ctx.shadowOffsetY = 2;
        // Introduce slight rotation for the tape itself
        ctx.rotate(-2 * Math.PI / 180);
        ctx.fillRect(-60, -215, 120, 35);

        ctx.restore();
    }

    // Determine if we are mirroring (if second photo wasn't provided, mirror the original for an artistic union)
    const isSameImage = (originalImg === img2);

    // Draw Left Polaroid
    drawPolaroid(originalImg, 300, 300, -6, isSameImage);

    // Draw Right Polaroid 
    drawPolaroid(img2, 700, 330, 8, false);

    // Draw the reunion text banner along the bottom
    ctx.save();
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = themeProps.textColor;
    ctx.font = `italic 600 55px "${fontFamily}", serif`;
    
    // Add graceful text shadow
    ctx.shadowColor = 'rgba(0, 0, 0, 0.6)';
    ctx.shadowBlur = 10;
    ctx.shadowOffsetX = 3;
    ctx.shadowOffsetY = 3;

    ctx.fillText(reunionText, canvas.width / 2, 620);
    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 Photo Parents Reunion Maker is an image composition tool designed to create sentimental, themed collages. It allows users to combine two different images into a stylized layout featuring Polaroid-style frames with decorative masking tape effects. Users can customize the final creation with personalized text and choose from various aesthetic themes, such as vintage, modern, or romantic, which apply unique color palettes and photographic filters. This tool is ideal for creating digital keepsakes, celebrating anniversaries, or making heartwarming commemorative graphics for family reunions and special occasions.

Leave a Reply

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