Please bookmark this page to avoid losing your image tool!

Image Vintage Circus Poster Frame 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.
async function processImage(
    originalImg,
    frameColor1 = '#D32F2F', // Muted Red (for main colored band)
    frameColor2 = '#FFF9E0', // Softer Cream (for image background and canvas background)
    borderColor = '#3E2723', // Dark Brown (for outermost border)
    textColor = '#212121',   // Dark Gray / Black
    mainText = "THE SPECTACULAR",
    subText = "EXTRAVAGANZA!",
    starColor = '#FFB300',   // Amber/Yellow
    borderThickness = 15,    // Thickness of the outermost border in pixels
    padding = 20,            // Padding around the image, inside the image's direct background area
    bandThickness = 50,      // Thickness of the main colored band (e.g., red band) in pixels
    fontNameParam = "Bangers", // Font for the text, e.g., "Bangers", "Luckiest Guy", "Arial"
    textureOpacity = 0.08    // Opacity for the subtle paper texture (0 to 1, 0 for no texture)
) {

    let effectiveFontName = fontNameParam;
    const fontMap = {
        "Bangers": "https://fonts.gstatic.com/s/bangers/v24/FeVQS0hpEzpageX3msxaa_s2W.woff2",
        "Luckiest Guy": "https://fonts.gstatic.com/s/luckiestguy/v18/KFOlCnAfPSnP8EJ0WMfgkFUTvyc.woff2",
        "Pacifico": "https://fonts.gstatic.com/s/pacifico/v22/FwZY7-Qmy14u9lezJ-6H6Mk.woff2",
        "Arial": null,
        "Verdana": null,
        "Georgia": null,
        "Times New Roman": null,
        "serif": null,
        "sans-serif": null
    };

    let fontToLoadUrl = fontMap[effectiveFontName];

    if (fontToLoadUrl === undefined) { // Font not in our predefined map
        // If it's not a generic CSS font family, fall back. Otherwise, try to use it as is.
        const genericFamilies = ["serif", "sans-serif", "monospace", "cursive", "fantasy"];
        if (!genericFamilies.includes(effectiveFontName.toLowerCase())) {
             console.warn(`Font "${effectiveFontName}" not in predefined map and not a generic family. Falling back to Arial.`);
             effectiveFontName = "Arial";
        } // else, we assume it might be a system font the browser knows.
        fontToLoadUrl = null; 
    }
    
    if (fontToLoadUrl) {
        try {
            if (!document.fonts.check(`12px "${effectiveFontName}"`)) {
                const fontFace = new FontFace(effectiveFontName, `url(${fontToLoadUrl})`);
                await fontFace.load();
                document.fonts.add(fontFace);
            }
        } catch (e) {
            console.error(`Font ${effectiveFontName} (${fontToLoadUrl}) failed to load, falling back to Arial:`, e);
            effectiveFontName = "Arial";
        }
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    const imgWidth = originalImg.width;
    const imgHeight = originalImg.height;

    const mainTextFontSize = Math.max(20, Math.min(80, Math.round(imgWidth / 12)));
    const subTextFontSize = Math.max(16, Math.min(60, Math.round(mainTextFontSize * 0.75)));
    
    ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
    const mainTextMetrics = ctx.measureText(mainText);
    const mainTextActualHeight = (mainTextMetrics.actualBoundingBoxAscent || mainTextFontSize * 0.75) + (mainTextMetrics.actualBoundingBoxDescent || mainTextFontSize * 0.25);

    ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
    const subTextMetrics = ctx.measureText(subText);
    const subTextActualHeight = (subTextMetrics.actualBoundingBoxAscent || subTextFontSize * 0.75) + (subTextMetrics.actualBoundingBoxDescent || subTextFontSize * 0.25);

    const topTextSpace = mainText.length > 0 ? Math.max(bandThickness, mainTextActualHeight * 1.6) : bandThickness * 0.6;
    const bottomTextSpace = subText.length > 0 ? Math.max(bandThickness, subTextActualHeight * 1.6) : bandThickness * 0.6;

    canvas.width = imgWidth + 2 * padding + 2 * bandThickness + 2 * borderThickness;
    canvas.height = imgHeight + 2 * padding + 2 * bandThickness + 2 * borderThickness + topTextSpace + bottomTextSpace;

    ctx.fillStyle = frameColor2;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.fillStyle = borderColor;
    ctx.fillRect(0, 0, canvas.width, borderThickness);
    ctx.fillRect(0, canvas.height - borderThickness, canvas.width, borderThickness);
    ctx.fillRect(0, 0, borderThickness, canvas.height);
    ctx.fillRect(canvas.width - borderThickness, 0, borderThickness, canvas.height);

    ctx.fillStyle = frameColor1;
    ctx.fillRect(
        borderThickness, 
        borderThickness, 
        canvas.width - 2 * borderThickness, 
        canvas.height - 2 * borderThickness
    );
    
    const imgBgX = borderThickness + bandThickness;
    const imgBgY = borderThickness + bandThickness + topTextSpace;
    const imgBgW = imgWidth + 2 * padding;
    const imgBgH = imgHeight + 2 * padding;
    ctx.fillStyle = frameColor2;
    ctx.fillRect(imgBgX, imgBgY, imgBgW, imgBgH);

    ctx.drawImage(
        originalImg,
        imgBgX + padding,
        imgBgY + padding,
        imgWidth,
        imgHeight
    );

    ctx.fillStyle = textColor;
    ctx.textAlign = 'center';
    
    if (mainText.length > 0) {
        ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
        ctx.textBaseline = 'middle';
        ctx.fillText(mainText, canvas.width / 2, borderThickness + topTextSpace / 2);
    }

    if (subText.length > 0) {
        ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
        ctx.textBaseline = 'middle';
        ctx.fillText(subText, canvas.width / 2, canvas.height - borderThickness - bottomTextSpace / 2);
    }

    function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
        if (outerRadius <= 1 || innerRadius <= 0.5) return;
        let rot = Math.PI / 2 * 3;
        let x = cx;
        let y = cy;
        let step = Math.PI / spikes;
        ctx.beginPath();
        ctx.moveTo(cx, cy - outerRadius);
        for (let i = 0; i < spikes; i++) {
            x = cx + Math.cos(rot) * outerRadius;
            y = cy + Math.sin(rot) * outerRadius;
            ctx.lineTo(x, y);
            rot += step;
            x = cx + Math.cos(rot) * innerRadius;
            y = cy + Math.sin(rot) * innerRadius;
            ctx.lineTo(x, y);
            rot += step;
        }
        ctx.lineTo(cx, cy - outerRadius);
        ctx.closePath();
        ctx.fill();
    }

    ctx.fillStyle = starColor;
    const baseStarOuterRadius = Math.max(5, bandThickness * 0.30);
    const baseStarInnerRadius = baseStarOuterRadius / 2;

    if (baseStarOuterRadius > 1) {
        // Corner stars (on frameColor1 band, within its thickness)
        drawStar(borderThickness + bandThickness / 2, borderThickness + bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
        drawStar(canvas.width - borderThickness - bandThickness / 2, borderThickness + bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
        drawStar(borderThickness + bandThickness / 2, canvas.height - borderThickness - bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
        drawStar(canvas.width - borderThickness - bandThickness / 2, canvas.height - borderThickness - bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);

        const textStarScale = 0.8;
        if (mainText.length > 0) {
            ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
            const mtWidth = ctx.measureText(mainText).width;
            const mtY = borderThickness + topTextSpace / 2;
            if (mtWidth > 0) {
                 drawStar(canvas.width / 2 - mtWidth / 2 - baseStarOuterRadius * textStarScale - 5, mtY, 5, baseStarOuterRadius * textStarScale, baseStarInnerRadius * textStarScale);
                 drawStar(canvas.width / 2 + mtWidth / 2 + baseStarOuterRadius * textStarScale + 5, mtY, 5, baseStarOuterRadius * textStarScale, baseStarInnerRadius * textStarScale);
            }
        }

        if (subText.length > 0) {
            ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
            const stWidth = ctx.measureText(subText).width;
            const stY = canvas.height - borderThickness - bottomTextSpace / 2;
            if (stWidth > 0) {
                drawStar(canvas.width / 2 - stWidth / 2 - baseStarOuterRadius * textStarScale * 0.8 - 5, stY, 5, baseStarOuterRadius * textStarScale * 0.8, baseStarInnerRadius * textStarScale * 0.8);
                drawStar(canvas.width / 2 + stWidth / 2 + baseStarOuterRadius * textStarScale * 0.8 + 5, stY, 5, baseStarOuterRadius * textStarScale * 0.8, baseStarInnerRadius * textStarScale * 0.8);
            }
        }
    }

    if (textureOpacity > 0 && textureOpacity <= 1) {
        ctx.save();
        ctx.globalAlpha = textureOpacity;
        const textureCanvas = document.createElement('canvas');
        const textureCtx = textureCanvas.getContext('2d');
        const textureSize = 64;
        textureCanvas.width = textureSize;
        textureCanvas.height = textureSize;
        const iData = textureCtx.createImageData(textureSize, textureSize);
        const data = iData.data;
        for (let i = 0; i < data.length; i += 4) {
            const shade = Math.floor(Math.random() * 40) + 215; // Light gray noise (215-255)
            data[i] = shade; data[i + 1] = shade; data[i + 2] = shade; data[i + 3] = 255;
        }
        textureCtx.putImageData(iData, 0, 0);
        ctx.fillStyle = ctx.createPattern(textureCanvas, 'repeat');
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        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 Vintage Circus Poster Frame Creator allows users to transform their images into vintage-style circus posters with customizable frames. Users can select various frame colors, border thickness, and add personalized text with different font options. This tool is ideal for creating eye-catching posters for events, parties, or artistic projects, providing a nostalgic and vibrant aesthetic. Whether for branding, promotion, or creative expression, this tool makes it easy to add a unique vintage flair to any image.

Leave a Reply

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