Please bookmark this page to avoid losing your image tool!

Vintage Photo Booth Strip Template 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.
function processImage(originalImg,
    numPhotos = 4,
    photoDisplaySize = 150, // The size of the image content area in pixels
    frameBorderColor = '#FFFFFF',
    frameBorderWidth = 10, // Border width around each photo in pixels
    frameCornerRadius = 8, // Outer corner radius of the photo frame in pixels
    spacingBetweenFrames = 10, // Space between photo frames in pixels
    stripBackgroundColor = '#222222',
    stripOverallPadding = 15, // Padding around the entire strip content in pixels
    vintageEffect = 'sepia' // 'none', 'grayscale', 'sepia'
) {

    // Helper function to create a rounded rectangle path
    function createRoundedRectPath(ctx, x, y, width, height, radius) {
        // Clamp radius to be valid for the given width/height
        if (typeof radius === 'undefined') radius = 0;
        
        // Ensure radius is not more than half the shortest side
        let r = Math.min(radius, width / 2, height / 2);
        // Ensure radius is non-negative
        if (r < 0) r = 0;

        ctx.beginPath();
        ctx.moveTo(x + r, y);
        ctx.lineTo(x + width - r, y); // Top edge
        ctx.quadraticCurveTo(x + width, y, x + width, y + r); // Top-right corner
        ctx.lineTo(x + width, y + height - r); // Right edge
        ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height); // Bottom-right corner
        ctx.lineTo(x + r, y + height); // Bottom edge
        ctx.quadraticCurveTo(x, y + height, x, y + height - r); // Bottom-left corner
        ctx.lineTo(x, y + r); // Left edge
        ctx.quadraticCurveTo(x, y, x + r, y); // Top-left corner
        ctx.closePath();
    }

    // --- Parameter Sanitization ---
    numPhotos = parseInt(String(numPhotos), 10);
    if (isNaN(numPhotos) || numPhotos <= 0) numPhotos = 4;

    photoDisplaySize = parseFloat(String(photoDisplaySize));
    if (isNaN(photoDisplaySize) || photoDisplaySize <= 0) photoDisplaySize = 150;
    
    frameBorderWidth = parseFloat(String(frameBorderWidth));
    if (isNaN(frameBorderWidth) || frameBorderWidth < 0) frameBorderWidth = 10;

    frameCornerRadius = parseFloat(String(frameCornerRadius));
    if (isNaN(frameCornerRadius) || frameCornerRadius < 0) frameCornerRadius = 8;

    spacingBetweenFrames = parseFloat(String(spacingBetweenFrames));
    if (isNaN(spacingBetweenFrames) || spacingBetweenFrames < 0) spacingBetweenFrames = 10;

    stripOverallPadding = parseFloat(String(stripOverallPadding));
    if (isNaN(stripOverallPadding) || stripOverallPadding < 0) stripOverallPadding = 15;

    frameBorderColor = String(frameBorderColor);
    stripBackgroundColor = String(stripBackgroundColor);
    vintageEffect = String(vintageEffect).toLowerCase(); // Normalize to lowercase

    // --- Dimension Calculations ---
    const frameOuterWidth = photoDisplaySize + 2 * frameBorderWidth;
    const frameOuterHeight = photoDisplaySize + 2 * frameBorderWidth;

    const canvasWidth = frameOuterWidth + 2 * stripOverallPadding;
    const canvasHeight = (numPhotos * frameOuterHeight) + ((numPhotos - 1) * spacingBetweenFrames) + (2 * stripOverallPadding);

    // --- Canvas Setup ---
    const canvas = document.createElement('canvas');
    canvas.width = Math.max(1, canvasWidth); // Ensure canvas dimensions are at least 1px
    canvas.height = Math.max(1, canvasHeight);
    const ctx = canvas.getContext('2d');

    // 1. Fill strip background
    ctx.fillStyle = stripBackgroundColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // --- Draw each photo frame and image ---
    for (let i = 0; i < numPhotos; i++) {
        const currentFrameX = stripOverallPadding;
        const currentFrameY = stripOverallPadding + i * (frameOuterHeight + spacingBetweenFrames);

        // 2. Draw the frame's border (as a filled shape)
        ctx.fillStyle = frameBorderColor;
        createRoundedRectPath(ctx, currentFrameX, currentFrameY, frameOuterWidth, frameOuterHeight, frameCornerRadius);
        ctx.fill();

        // Calculate image destination properties
        const imgDestX = currentFrameX + frameBorderWidth;
        const imgDestY = currentFrameY + frameBorderWidth;
        const imgDestWidth = photoDisplaySize;
        const imgDestHeight = photoDisplaySize;
        
        // Inner corner radius for the image clipping area
        const innerCornerRadius = Math.max(0, frameCornerRadius - frameBorderWidth);

        ctx.save(); // Save context state (incl. current filter, transform, etc.)

        // 3. Create clipping path for the image content area
        createRoundedRectPath(ctx, imgDestX, imgDestY, imgDestWidth, imgDestHeight, innerCornerRadius);
        ctx.clip();

        // 4. Apply vintage effect if specified (modifies current drawing state)
        if (vintageEffect === 'grayscale') {
            ctx.filter = 'grayscale(100%)';
        } else if (vintageEffect === 'sepia') {
            ctx.filter = 'sepia(100%)';
        } // If 'none' or unrecognized, current GState filter (likely 'none') remains.

        // 5. Draw the image (or fallback)
        const imgSrc = originalImg;
        let imageDrawnSuccessfully = false;

        if (imgSrc && imgSrc.complete && imgSrc.naturalWidth > 0 && imgSrc.naturalHeight > 0) {
            const imgAspect = imgSrc.naturalWidth / imgSrc.naturalHeight;
            const destAspect = imgDestWidth / imgDestHeight; // Typically 1.0 for square photos

            let sx = 0, sy = 0, sWidth = imgSrc.naturalWidth, sHeight = imgSrc.naturalHeight;

            if (imgAspect > destAspect) { // Source image is wider than destination aspect ratio
                sHeight = imgSrc.naturalHeight;
                sWidth = sHeight * destAspect; // Crop width to fit destination aspect ratio
                sx = (imgSrc.naturalWidth - sWidth) / 2; // Center horizontally
            } else { // Source image is taller or has the same aspect ratio
                sWidth = imgSrc.naturalWidth;
                sHeight = sWidth / destAspect; // Crop height to fit destination aspect ratio
                sy = (imgSrc.naturalHeight - sHeight) / 2; // Center vertically
            }
            
            // Ensure calculated source dimensions are valid
            if (sWidth > 0 && sHeight > 0 && 
                sx >= 0 && sy >= 0 &&
                sx + sWidth <= imgSrc.naturalWidth + 0.0001 && // Add epsilon for float comparisons
                sy + sHeight <= imgSrc.naturalHeight + 0.0001) {
                 ctx.drawImage(imgSrc, sx, sy, sWidth, sHeight, imgDestX, imgDestY, imgDestWidth, imgDestHeight);
                 imageDrawnSuccessfully = true;
            }
        }
        
        if (!imageDrawnSuccessfully) {
            // Fallback: Draw a placeholder if image is invalid, not loaded, or calculations failed
            // This fallback will also be clipped by the rounded rectangle path.
            // It should be drawn without the vintage filter.
            let tempFilter = ctx.filter; // Store current filter (e.g., 'sepia(100%)')
            ctx.filter = 'none'; // Temporarily disable filter for placeholder

            ctx.fillStyle = '#CCCCCC'; // Light gray for placeholder background
            ctx.fillRect(imgDestX, imgDestY, imgDestWidth, imgDestHeight); 
            
            ctx.fillStyle = '#555555'; // Darker gray for text
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            const fontSize = Math.min(imgDestWidth, imgDestHeight) / 6; // Adjust font size based on photo size
            ctx.font = `${fontSize}px Arial`; // Use a common font
            ctx.fillText('Error', imgDestX + imgDestWidth / 2, imgDestY + imgDestHeight / 2);
            
            ctx.filter = tempFilter; // Restore filter to what it was before drawing placeholder
        }
        
        ctx.restore(); // Restore context state: removes clip, and restores filter to its value at ctx.save()
    }

    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 Vintage Photo Booth Strip Template Generator allows users to create a customizable photo strip template featuring a series of images enclosed in vintage-style frames. Users can specify the number of photos to include, their display size, frame border attributes, and padding options. The tool also supports vintage effects such as sepia or grayscale to give the photos a nostalgic look. This generator is ideal for creating personalized photo booth strips for events like weddings, parties, and celebrations, as well as for promotional materials or creative projects.

Leave a Reply

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