Please bookmark this page to avoid losing your image tool!

Image Space Mission Patch Template 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,
    missionName = "MISSION NAME",
    bottomText = `${new Date().getFullYear()} - EXPLORERS`,
    patchColor = "#002244", // Dark blue
    borderColor = "#FFD700", // Gold
    textColor = "#FFFFFF",   // White
    borderWidth = 30,
    fontChoice = "ArialBlack", // Options: "ArialBlack", "Impact", "Orbitron"
    topTextSize = 36,
    bottomTextSize = 24
) {

    let activeFontFamily = "Arial Black, Gadget, sans-serif"; // Default
    const orbitronFontName = "Orbitron";

    if (fontChoice === "Orbitron") {
        let isOrbitronReady = false;
        if (document.fonts) {
            try { 
                // Check for specific weights if they are used, e.g., 'bold 1em Orbitron'
                isOrbitronReady = await document.fonts.check(`bold 1em ${orbitronFontName}`); 
            } catch (e) { 
                console.warn("Error checking for Orbitron font:", e);
            }
        }

        if (!isOrbitronReady) {
            const orbitronNormalUrl = "https://fonts.gstatic.com/s/orbitron/v25/yMJRMIlzdpvBhQQL_OUhnw.woff2"; // Orbitron Regular 400
            const orbitronBoldUrl = "https://fonts.gstatic.com/s/orbitron/v25/yMJWMIlzdpvBhQQL_QJXLJ3tZv4.woff2"; // Orbitron Bold 700
            
            const styleId = "orbitron-font-style-patch-creator"; // Unique ID for the style tag
            if (!document.getElementById(styleId)) {
                const newStyle = document.createElement('style');
                newStyle.id = styleId;
                newStyle.textContent = `
                    @font-face { 
                        font-family: '${orbitronFontName}'; 
                        src: url('${orbitronNormalUrl}') format('woff2'); 
                        font-weight: normal; 
                        font-style: normal; 
                    }
                    @font-face { 
                        font-family: '${orbitronFontName}'; 
                        src: url('${orbitronBoldUrl}') format('woff2'); 
                        font-weight: bold; 
                        font-style: normal; 
                    }
                `;
                document.head.appendChild(newStyle);
            }

            try {
                if (document.fonts) {
                    await Promise.all([
                        document.fonts.load(`normal 1em ${orbitronFontName}`),
                        document.fonts.load(`bold 1em ${orbitronFontName}`)
                    ]);
                    activeFontFamily = `"${orbitronFontName}", Arial Black, Gadget, sans-serif`;
                } else {
                    // Fallback for environments without document.fonts
                    activeFontFamily = `"${orbitronFontName}", Arial Black, Gadget, sans-serif`;
                    await new Promise(resolve => setTimeout(resolve, 300)); // Small delay
                }
            } catch (e) {
                console.warn(`${orbitronFontName} font could not be loaded, using fallback. Error:`, e);
                // activeFontFamily remains the default
            }
        } else {
             activeFontFamily = `"${orbitronFontName}", Arial Black, Gadget, sans-serif`;
        }
    } else if (fontChoice === "Impact") {
        activeFontFamily = "Impact, Arial Black, Gadget, sans-serif";
    }
    // Else, "ArialBlack" uses the default activeFontFamily

    const canvas = document.createElement('canvas');
    const size = 600; // Patch size
    canvas.width = size;
    canvas.height = size;
    const ctx = canvas.getContext('2d');

    const centerX = size / 2;
    const centerY = size / 2;
    const outerRadius = size / 2 - 5; // Small margin from edge

    // 1. Draw patch background color (main circle)
    ctx.beginPath();
    ctx.arc(centerX, centerY, outerRadius, 0, Math.PI * 2);
    ctx.fillStyle = patchColor;
    ctx.fill();

    // 2. Draw border
    if (borderWidth > 0) {
        ctx.beginPath();
        ctx.arc(centerX, centerY, outerRadius - borderWidth / 2, 0, Math.PI * 2); // Border centered on its width
        ctx.lineWidth = borderWidth;
        ctx.strokeStyle = borderColor;
        ctx.stroke();
    }
    
    // Define radius for the image placement area (inside the border)
    const imageAreaRadius = outerRadius - borderWidth - 5; // 5px padding from border's inner edge

    // 3. Draw original image in the center, clipped to a circle
    if (originalImg && imageAreaRadius > 0) {
        ctx.save();
        ctx.beginPath();
        ctx.arc(centerX, centerY, imageAreaRadius, 0, Math.PI * 2);
        ctx.clip();

        const imgNatWidth = originalImg.naturalWidth || originalImg.width;
        const imgNatHeight = originalImg.naturalHeight || originalImg.height;
        const destDiameter = imageAreaRadius * 2;
        
        let sourceX = 0, sourceY = 0, sourceWidth = imgNatWidth, sourceHeight = imgNatHeight;

        // Crop to center square for a 'porthole' effect, maintaining aspect ratio
        if (imgNatWidth / imgNatHeight > 1) { // Landscape
            sourceWidth = imgNatHeight;
            sourceX = (imgNatWidth - sourceWidth) / 2;
        } else { // Portrait or square
            sourceHeight = imgNatWidth;
            sourceY = (imgNatHeight - sourceHeight) / 2;
        }

        ctx.drawImage(
            originalImg,
            sourceX,
            sourceY,
            sourceWidth,
            sourceHeight,
            centerX - imageAreaRadius,
            centerY - imageAreaRadius,
            destDiameter,
            destDiameter
        );
        ctx.restore(); // Release clipping path
    }

    // Radii for text paths (inside the border, outside the main image circle typically)
    // Place text base just inside the border
    const textRadiusTop = outerRadius - borderWidth - (topTextSize * 0.35);
    const textRadiusBottom = outerRadius - borderWidth - (bottomTextSize * 0.35);


    // Helper function for drawing curved text
    function drawCurvedTextInternal(currentCtx, text, cX, cY, radius, color, isTop, currentFont, currentFontSize) {
        currentCtx.save();
        currentCtx.fillStyle = color;
        currentCtx.font = `bold ${currentFontSize}px ${currentFont}`; // Set font here with size
        currentCtx.textAlign = 'center';

        const characters = text.toUpperCase().split('');
        const N = characters.length;
        if (N === 0) {
            currentCtx.restore();
            return;
        }

        let totalAngleSweep;
        if (isTop) {
            totalAngleSweep = (2 * Math.PI) / 3; // 120 degrees for top text
            currentCtx.textBaseline = 'bottom'; // Text hangs below the arc path for top
        } else { // Bottom text
            totalAngleSweep = (5 * Math.PI) / 9; // 100 degrees for bottom text
            // Small adjustment for very short bottom text to prevent wide spacing
            if (N <= 3) totalAngleSweep = Math.PI / 3; // 60 degrees
            else if (N <=5) totalAngleSweep = Math.PI / 2; // 90 degrees

            currentCtx.textBaseline = 'top';   // Text sits above the arc path for bottom
        }
        
        const anglePerCharCenter = totalAngleSweep / N;
        const baseAngle = isTop ? -Math.PI / 2 : Math.PI / 2; // Top or Bottom center of the patch
        // Start angle for the center of the first character
        let currentDisplayAngle = baseAngle - (totalAngleSweep / 2) + (anglePerCharCenter / 2);

        for (let i = 0; i < N; i++) {
            const char = characters[i];
            const charAngle = currentDisplayAngle + i * anglePerCharCenter;

            currentCtx.save();
            currentCtx.translate(cX, cY);
            currentCtx.rotate(charAngle);

            if (isTop) {
                currentCtx.translate(0, -radius); // Move out to radius on y-axis
            } else { // Bottom text
                currentCtx.translate(0, radius);  // Move out to radius on y-axis
                currentCtx.rotate(Math.PI);     // Rotate character to be upright
            }
            
            currentCtx.fillText(char, 0, 0);
            currentCtx.restore();
        }
        currentCtx.restore();
    }

    // 4. Draw Top Text (Mission Name)
    if (missionName && missionName.trim() !== "" && topTextSize > 0) {
        drawCurvedTextInternal(ctx, missionName, centerX, centerY, textRadiusTop, textColor, true, activeFontFamily, topTextSize);
    }

    // 5. Draw Bottom Text
    if (bottomText && bottomText.trim() !== "" && bottomTextSize > 0) {
        drawCurvedTextInternal(ctx, bottomText, centerX, centerY, textRadiusBottom, textColor, false, activeFontFamily, bottomTextSize);
    }
    
    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 Space Mission Patch Template Creator is an online tool designed for creating personalized mission patches. Users can input an original image and customize elements such as the mission name, border colors, and text. Ideal for space enthusiasts, educational projects, or commemorative events, this tool allows users to design circular patches that feature curved text and a central image, all while selecting various font styles and colors to enhance their design. The patches can be used for display, awards, or personal mementos.

Leave a Reply

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

Other Image Tools:

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

Image Steampunk Document Frame Creator

Image Vintage Scientific Illustration Frame Creator

Image Magazine Cover Template Creator

Image Album Cover Template Creator

Image Medieval Bestiary Page Template

Image Polaroid Instant Photo Frame

Image Pulp Fiction Book Cover Template

Image Medieval Manuscript Frame Creator

Image Vintage Advertisement Poster Generator

Victorian Image Calling Card Template

Image Award Certificate Template

Image Military Insignia Patch Template Creator

See All →