Please bookmark this page to avoid losing your image tool!

Image Vintage Carnival Sideshow Poster 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,
    mainHeadline = "THE ASTOUNDING",
    secondaryHeadline = "AMAZING FEATS!",
    starburstText = "ALIVE!",
    bottomLineText = "ADMISSION 25¢",
    accentColor = "#B80000",      // Deep Red (for stars, highlights, inner borders)
    textColor = "#FFFFF0",        // Ivory (for main text fill)
    textOutlineColor = "#000000", // Black (for text stroke)
    backgroundColor = "#F5E8C7",  // Aged Paper Beige
    frameColor = "#4A3B31"        // Dark Brown (for main poster frame/border)
) {

    const POSTER_WIDTH = 800;
    const POSTER_HEIGHT = 1200;
    const BORDER_WIDTH = 25; // Width of the main outer frame
    const INNER_MARGIN = 30; // Margin inside the frame for content
    const TEXT_STROKE_WIDTH = 3;

    const FONT_HEADLINE = "Rye";            // Google Font
    const FONT_SUB_HEADLINE = "Luckiest Guy"; // Google Font
    const FONT_STARBURST = "Bangers";       // Google Font
    const FONT_BOTTOM_LINE = "Carter One";  // Google Font

    // Helper to load Google Fonts
    async function loadGoogleFont(fontFamily) {
        const fontName = fontFamily.replace(/\s+/g, '+');
        const url = `https://fonts.googleapis.com/css2?family=${fontName}:wght@400&display=swap`;

        if (document.querySelector(`link[href="${url}"]`)) {
            try {
                await document.fonts.load(`12px "${fontFamily}"`);
                // console.log(`Font ${fontFamily} already linked and loaded.`);
                return;
            } catch (e) {
                // console.warn(`Font ${fontFamily} link existed, but load failed. Will try to add again.`, e);
            }
        } else if (document.fonts.check(`12px "${fontFamily}"`)) {
            // console.log(`Font ${fontFamily} already loaded (system or previously loaded).`);
            return;
        }
        
        return new Promise((resolve, reject) => {
            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = url;
            link.onload = async () => {
                try {
                    await document.fonts.load(`12px "${fontFamily}"`);
                    // console.log(`Font ${fontFamily} loaded successfully.`);
                    resolve();
                } catch (e) {
                    console.error(`Error loading font metrics for ${fontFamily} after CSS load. Using fallback.`, e);
                    resolve(); // Resolve anyway to not break Promise.all
                }
            };
            link.onerror = () => {
                console.error(`Failed to load Google Font CSS: ${fontFamily}. Using fallback.`);
                resolve(); // Resolve anyway
            };
            document.head.appendChild(link);
        });
    }

    // Helper to draw stroked and filled text
    function drawText(ctx, text, x, y, font, fontSize, fillColor, strokeColor, strokeWidth, textAlign = 'center', textBaseline = 'alphabetic') {
        ctx.font = `${fontSize}px "${font}"`;
        ctx.fillStyle = fillColor;
        ctx.strokeStyle = strokeColor;
        ctx.lineWidth = strokeWidth;
        ctx.textAlign = textAlign;
        ctx.textBaseline = textBaseline;

        if (strokeWidth > 0) {
            ctx.strokeText(text, x, y);
        }
        ctx.fillText(text, x, y);
    }

    // Helper for wrapped text
    function wrapAndDrawText(ctx, text, x, y, maxWidth, lineHeight, font, fontSize, fillColor, strokeColor, strokeWidth, textAlign = 'center') {
        const words = text.toUpperCase().split(' ');
        let line = '';
        let currentY = y;
        const lines = [];

        ctx.font = `${fontSize}px "${font}"`; // Set font for accurate measureText

        for (let n = 0; n < words.length; n++) {
            const testLine = line + words[n] + ' ';
            const metrics = ctx.measureText(testLine.trim());
            const testWidth = metrics.width;

            if (testWidth > maxWidth && line !== '') { // Ensure line is not empty before pushing
                lines.push({ text: line.trim(), y: currentY });
                line = words[n] + ' ';
                currentY += lineHeight;
            } else {
                line = testLine;
            }
        }
        if (line.trim() !== '') { // Add the last line
             lines.push({ text: line.trim(), y: currentY });
        }
       
        const totalTextHeight = lines.length * lineHeight;
        // Optional: Adjust Y to center the block of text if needed; for now, `y` is the top.

        lines.forEach(lineObj => {
            drawText(ctx, lineObj.text, x, lineObj.y, font, fontSize, fillColor, strokeColor, strokeWidth, textAlign, 'top');
        });
        
        return currentY + (lines.length > 0 ? lineHeight : 0); // Return Y for the position *after* this text block
    }

    // Helper to draw a starburst
    function drawStarburst(ctx, centerX, centerY, spikes, outerRadius, innerRadius, bgColor, borderColor, borderW, text, textColor, textFont, textFontSize) {
        ctx.beginPath();
        ctx.moveTo(centerX, centerY - outerRadius); // Start at top point
        for (let i = 0; i < spikes; i++) {
            const outerAngle = (i / spikes) * 2 * Math.PI - Math.PI / 2; // Offset to start at top
            const innerAngle = outerAngle + Math.PI / spikes; // Angle for inner point
            
            ctx.lineTo(centerX + Math.cos(outerAngle) * outerRadius, centerY + Math.sin(outerAngle) * outerRadius);
            ctx.lineTo(centerX + Math.cos(innerAngle) * innerRadius, centerY + Math.sin(innerAngle) * innerRadius);
        }
        ctx.closePath();

        ctx.fillStyle = bgColor;
        ctx.fill();
        if (borderColor && borderW > 0) {
            ctx.strokeStyle = borderColor;
            ctx.lineWidth = borderW;
            ctx.stroke();
        }

        if (text) {
             // Simple text splitting by \n for starburst
            const lines = text.toUpperCase().split('\\N'); // Using \N as custom newline
            const lineHeight = textFontSize * (lines.length > 1 ? 0.8 : 1); // Reduce line height for multi-line
            const totalTextHeight = lines.length * lineHeight;
            let textY = centerY - totalTextHeight / 2 + lineHeight / 2  - (lines.length > 1 ? textFontSize*0.1 : 0); // Adjusted centering

            lines.forEach((line, index) => {
                // Attempt to make text slightly curved or angled if it's on multiple lines for starbursts (complex)
                // For now, straight text
                drawText(ctx, line, centerX, textY + index * lineHeight, textFont, textFontSize, textColor, textOutlineColor, TEXT_STROKE_WIDTH / 2, 'center', 'middle');
            });
        }
    }
    
    await Promise.all([
        loadGoogleFont(FONT_HEADLINE),
        loadGoogleFont(FONT_SUB_HEADLINE),
        loadGoogleFont(FONT_STARBURST),
        loadGoogleFont(FONT_BOTTOM_LINE)
    ]);

    const canvas = document.createElement('canvas');
    canvas.width = POSTER_WIDTH;
    canvas.height = POSTER_HEIGHT;
    const ctx = canvas.getContext('2d');

    // 1. Main Border (frameColor)
    ctx.fillStyle = frameColor;
    ctx.fillRect(0, 0, POSTER_WIDTH, POSTER_HEIGHT);

    // 2. Background Fill (backgroundColor)
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(BORDER_WIDTH, BORDER_WIDTH, POSTER_WIDTH - 2 * BORDER_WIDTH, POSTER_HEIGHT - 2 * BORDER_WIDTH);

    // 3. Subtle Background Texture (on top of background color)
    ctx.save();
    ctx.rect(BORDER_WIDTH, BORDER_WIDTH, POSTER_WIDTH - 2 * BORDER_WIDTH, POSTER_HEIGHT - 2 * BORDER_WIDTH);
    ctx.clip(); // Confine texture to the paper area
    for (let i = 0; i < 20000; i++) { 
        const x = Math.random() * POSTER_WIDTH;
        const y = Math.random() * POSTER_HEIGHT;
        const alpha = Math.random() * 0.1 + 0.02; 
        const speckSize = Math.random() * 2 + 0.5;
        ctx.fillStyle = `rgba(0, 0, 0, ${alpha})`; // Darker specks for aged paper
        ctx.fillRect(x, y, speckSize, speckSize);
    }
    ctx.restore();

    // 4. Inner Accent Border Line (thin, accentColor)
    ctx.strokeStyle = accentColor;
    ctx.lineWidth = 5;
    const accentBorderOffset = BORDER_WIDTH + 8;
    ctx.strokeRect(accentBorderOffset, accentBorderOffset, 
                   POSTER_WIDTH - 2 * accentBorderOffset, POSTER_HEIGHT - 2 * accentBorderOffset);

    // Text and image layout variables
    let currentY = BORDER_WIDTH + INNER_MARGIN;
    const contentWidth = POSTER_WIDTH - 2 * (BORDER_WIDTH + INNER_MARGIN);
    const contentCenterX = POSTER_WIDTH / 2;

    // 5. Main Headline
    if (mainHeadline) {
        const headlineFontSize = 80;
        const headlineLineHeight = headlineFontSize * 0.9;
        currentY = wrapAndDrawText(ctx, mainHeadline, contentCenterX, currentY, contentWidth * 0.95, headlineLineHeight, 
                                FONT_HEADLINE, headlineFontSize, textColor, textOutlineColor, TEXT_STROKE_WIDTH, 'center');
        currentY += headlineLineHeight * 0.3; // Extra spacing after main headline
    }

    // 6. Secondary Headline
    if (secondaryHeadline) {
        const subHeadlineFontSize = 45;
        const subHeadlineLineHeight = subHeadlineFontSize * 0.9;
        currentY = wrapAndDrawText(ctx, secondaryHeadline, contentCenterX, currentY, contentWidth * 0.85, subHeadlineLineHeight,
                                FONT_SUB_HEADLINE, subHeadlineFontSize, textColor, textOutlineColor, TEXT_STROKE_WIDTH, 'center');
        currentY += subHeadlineLineHeight * 0.5; // Extra spacing
    }
    
    // 7. Image Area
    const imageMaxHeight = POSTER_HEIGHT * 0.45;
    const imageTopY = currentY;
    const imageSpaceHeight = POSTER_HEIGHT - currentY - (BORDER_WIDTH + INNER_MARGIN + 80); // Reserve space for bottom text

    let imgDrawWidth = originalImg.width;
    let imgDrawHeight = originalImg.height;
    const imgAspectRatio = originalImg.width / originalImg.height;

    if (imgDrawWidth > contentWidth) {
        imgDrawWidth = contentWidth;
        imgDrawHeight = imgDrawWidth / imgAspectRatio;
    }
    if (imgDrawHeight > imageMaxHeight) {
        imgDrawHeight = imageMaxHeight;
        imgDrawWidth = imgDrawHeight * imgAspectRatio;
    }
    if (imgDrawHeight > imageSpaceHeight) { // Final check against remaining space
        imgDrawHeight = imageSpaceHeight;
        imgDrawWidth = imgDrawHeight * imgAspectRatio;
    }


    const imgDrawX = contentCenterX - imgDrawWidth / 2;
    const imgDrawY = imageTopY + (imageSpaceHeight - imgDrawHeight) / 2; // Vertically center image in its allocated space

    if (imgDrawWidth > 0 && imgDrawHeight > 0) {
        ctx.save();
        // Vintage filter
        ctx.filter = 'sepia(0.6) contrast(1.1) brightness(0.95) saturate(0.7)';
        ctx.drawImage(originalImg, imgDrawX, imgDrawY, imgDrawWidth, imgDrawHeight);
        ctx.restore();

        // Image Frame
        ctx.strokeStyle = frameColor;
        ctx.lineWidth = 12;
        ctx.strokeRect(imgDrawX - ctx.lineWidth/2, imgDrawY - ctx.lineWidth/2, imgDrawWidth + ctx.lineWidth, imgDrawHeight + ctx.lineWidth);
        
        ctx.strokeStyle = accentColor;
        ctx.lineWidth = 4;
        ctx.strokeRect(imgDrawX - ctx.lineWidth/2, imgDrawY - ctx.lineWidth/2, imgDrawWidth + ctx.lineWidth, imgDrawHeight + ctx.lineWidth);
    }
    currentY = imgDrawY + imgDrawHeight + INNER_MARGIN * 0.5; // Update currentY to below the image


    // 8. Starburst Text (if image exists to position relative to)
    if (starburstText && imgDrawWidth > 0) {
        const starOuterRadius = Math.min(contentWidth * 0.22, 100) ;
        const starInnerRadius = starOuterRadius * 0.5;
        const starSpikes = 16;
        const starFontSize = starOuterRadius * 0.4;

        // Position starburst: e.g., bottom-right of image, or centered below
        let starX = imgDrawX + imgDrawWidth - starOuterRadius * 0.8;
        let starY = imgDrawY + imgDrawHeight - starOuterRadius * 0.7;
        
        // Ensure starburst is within poster visible area (considering main border)
        starX = Math.max(starX, BORDER_WIDTH + starOuterRadius);
        starX = Math.min(starX, POSTER_WIDTH - BORDER_WIDTH - starOuterRadius);
        starY = Math.max(starY, BORDER_WIDTH + starOuterRadius);
        starY = Math.min(starY, POSTER_HEIGHT - BORDER_WIDTH - starOuterRadius - 50); // Keep from very bottom
        
        drawStarburst(ctx, starX, starY, starSpikes, starOuterRadius, starInnerRadius,
                      accentColor, textOutlineColor, TEXT_STROKE_WIDTH, starburstText, textColor,
                      FONT_STARBURST, starFontSize);
    }

    // 9. Bottom Line Text
    if (bottomLineText) {
        const bottomTextFontSize = 35;
        const bottomTextLineHeight = bottomTextFontSize * 1;
        const bottomTextY = POSTER_HEIGHT - BORDER_WIDTH - INNER_MARGIN - bottomTextLineHeight; // Position from bottom
        
        wrapAndDrawText(ctx, bottomLineText, contentCenterX, bottomTextY, contentWidth, bottomTextLineHeight,
                        FONT_BOTTOM_LINE, bottomTextFontSize, textColor, textOutlineColor, TEXT_STROKE_WIDTH, 'center');
    }
    
    // Optional: Add some decorative stars using text characters
    ctx.fillStyle = accentColor;
    ctx.font = `30px "${FONT_HEADLINE}"`; // Use one of the loaded fonts
    ctx.textAlign = 'center';
    // Place a few stars around headlines if desired
    // Example: ctx.fillText("★", contentCenterX - contentWidth/2 * 0.6, BORDER_WIDTH + INNER_MARGIN + 30);
    // Example: ctx.fillText("★", contentCenterX + contentWidth/2 * 0.6, BORDER_WIDTH + INNER_MARGIN + 30);


    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 Carnival Sideshow Poster Creator is a web-based tool that allows users to create eye-catching vintage-style posters. By uploading an image, users can add customized headlines, secondary text, and decorative features like starbursts, all rendered with a classic carnival poster aesthetic. The tool offers options to adjust colors, font styles, and layout elements, providing a unique way to promote events, performances, or artistic endeavors in a playful, vintage format. Ideal for event organizers, artists, or social media marketers looking to capture attention with striking imagery and design.

Leave a Reply

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

Other Image Tools:

Image Space Explorer’s Log Creator

Image Neolithic Petroglyph Frame Creator

Image Ukiyo-e Japanese Woodblock Print Creator

Image Persian Miniature Painting Creator

Image Sci-Fi Movie Poster Template Creator

Image Horror Movie Poster Template

Image Social Media Milestone Certificate Creator

Halloween Death Certificate Template

Image Anatomical Illustration Frame Creator

Image Romance Novel Cover Template Creator

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

Image Birth Certificate Template Generator

Image Driver’s License Template Creator

Image Scout Explorer Badge Template Creator

See All →