Please bookmark this page to avoid losing your image tool!

Image Award Certificate Template

(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,
    recipientName = "Jane Doe",
    awardReason = "Excellence in JavaScript Development",
    dateText = "", // Default to current date if empty string
    issuedBy = "The Code Masters",
    certificateTitle = "Certificate of Achievement",
    presenterLine = "This certificate is proudly presented to",
    mainColor = "#003366", // Dark Blue
    accentColor = "#B8860B", // DarkGoldenrod
    fontFamilyTitleParam = "Times New Roman, serif",
    fontFamilyBodyParam = "Georgia, serif",
    fontFamilyScriptParam = "Great Vibes, cursive", // Example: 'Great Vibes, cursive' or 'Zapfino, cursive'
    signaturePlaceholder = "Authorized Signature",
    issuedByTitle = "Issuing Authority",
    logoPlacement = "top-center", // "top-left", "top-right", "seal", "none"
    logoSize = 100, // Max dimension (width or height) for the logo image, forming a bounding box.
    backgroundColor = "#FDF5E6" // Old Lace / Antique White
) {

    // Helper function to load fonts dynamically (e.g., from Google Fonts)
    async function loadFont(fontName, fontUrl) {
        const fontId = `dynamic-font-${fontName.replace(/\s+/g, '-').toLowerCase()}`;
        if (!document.getElementById(fontId)) {
            const link = document.createElement('link');
            link.id = fontId;
            link.rel = 'stylesheet';
            link.href = fontUrl;
            document.head.appendChild(link);
            try {
                await document.fonts.load(`1em "${fontName}"`);
            } catch (e) {
                console.warn(`Failed to load font: ${fontName} from ${fontUrl}. Error: ${e}`);
            }
        }
    }

    // Helper function to wrap text
    function wrapText(context, text, x, y, maxWidth, lineHeight) {
        const words = text.split(' ');
        let lineBuffer = '';
        let currentDrawingY = y; // Y for drawing current line (baseline)
        
        const initialBaseline = context.textBaseline;
        // Ensure consistent baseline for calculations & drawing, alphabetic is common.
        context.textBaseline = 'alphabetic'; 

        for (let n = 0; n < words.length; n++) {
            const testLine = lineBuffer + words[n] + ' ';
            const metrics = context.measureText(testLine);
            const testWidth = metrics.width;
            if (testWidth > maxWidth && n > 0) {
                context.fillText(lineBuffer.trim(), x, currentDrawingY);
                lineBuffer = words[n] + ' ';
                currentDrawingY += lineHeight;
            } else {
                lineBuffer = testLine;
            }
        }
        context.fillText(lineBuffer.trim(), x, currentDrawingY); // Draw the last line

        context.textBaseline = initialBaseline; // Restore original baseline
        return currentDrawingY; // Returns the Y baseline of the last drawn line
    }

    // Load script font if specified (example 'Great Vibes')
    const primaryScriptFont = fontFamilyScriptParam.split(',')[0].trim().toLowerCase();
    if (primaryScriptFont === 'great vibes') {
        await loadFont('Great Vibes', 'https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap');
    }
    // Other fonts could be loaded here if specified as known web fonts in parameters.

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

    // Date default
    if (!dateText.trim()) {
        dateText = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
    }

    // Fill background
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, RENDER_WIDTH, RENDER_HEIGHT);

    // Border
    const borderWidthOuter = 5;
    const borderWidthInner = 2;
    const padding = 20; // Outer padding for the first border

    ctx.strokeStyle = mainColor;
    ctx.lineWidth = borderWidthOuter;
    ctx.strokeRect(padding, padding, RENDER_WIDTH - 2 * padding, RENDER_HEIGHT - 2 * padding);

    const innerBorderPadding = padding + borderWidthOuter + 5;
    ctx.strokeStyle = accentColor;
    ctx.lineWidth = borderWidthInner;
    ctx.strokeRect(innerBorderPadding, innerBorderPadding,
                   RENDER_WIDTH - 2 * innerBorderPadding,
                   RENDER_HEIGHT - 2 * innerBorderPadding);

    // Logo (if not used as seal)
    let logoTopOffset = innerBorderPadding + 40; // Default top margin
    if (originalImg && originalImg.complete && originalImg.naturalWidth > 0 && 
        logoPlacement !== 'none' && logoPlacement !== 'seal' && logoSize > 0) {
        
        let w = originalImg.naturalWidth;
        let h = originalImg.naturalHeight;
        let logoDisplayWidth, logoDisplayHeight;

        const aspectRatio = w / h;
        if (aspectRatio >= 1) { // Wider than tall, or square
            logoDisplayWidth = logoSize;
            logoDisplayHeight = logoSize / aspectRatio;
        } else { // Taller than wide
            logoDisplayHeight = logoSize;
            logoDisplayWidth = logoSize * aspectRatio;
        }
        
        let logoX = -1, logoY = -1;
        const logoMargin = 20; // Margin from inner border edge

        switch (logoPlacement) {
            case 'top-center':
                logoX = RENDER_WIDTH / 2 - logoDisplayWidth / 2;
                logoY = innerBorderPadding + logoMargin;
                break;
            case 'top-left':
                logoX = innerBorderPadding + logoMargin;
                logoY = innerBorderPadding + logoMargin;
                break;
            case 'top-right':
                logoX = RENDER_WIDTH - innerBorderPadding - logoMargin - logoDisplayWidth;
                logoY = innerBorderPadding + logoMargin;
                break;
        }

        if (logoX !== -1 ) {
             if (logoPlacement.startsWith('top-')) {
                logoTopOffset = logoY + logoDisplayHeight + 20; 
             }
             ctx.drawImage(originalImg, logoX, logoY, logoDisplayWidth, logoDisplayHeight);
        }
    }


    // Content
    ctx.fillStyle = mainColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'alphabetic';

    let currentY = logoTopOffset;

    // Certificate Title
    ctx.font = `bold 48px ${fontFamilyTitleParam}`;
    ctx.fillText(certificateTitle, RENDER_WIDTH / 2, currentY);
    currentY += 48 * 0.7; // Approx height used by title text

    // Decorative line under title
    currentY += 20; // Space above line
    ctx.beginPath();
    ctx.moveTo(RENDER_WIDTH / 2 - 150, currentY);
    ctx.lineTo(RENDER_WIDTH / 2 + 150, currentY);
    ctx.strokeStyle = accentColor;
    ctx.lineWidth = 1.5;
    ctx.stroke();
    currentY += 35; // Space after line

    // Presenter Line
    ctx.font = `24px ${fontFamilyBodyParam}`;
    ctx.fillStyle = mainColor;
    ctx.fillText(presenterLine, RENDER_WIDTH / 2, currentY);
    currentY += 24 + 20; // Text height + space

    // Recipient Name - script fonts often need y adjustment
    ctx.font = `bold 60px ${fontFamilyScriptParam}`;
    ctx.fillStyle = accentColor;
    // Use 'middle' baseline for more predictable vertical centering of script fonts
    ctx.textBaseline = 'middle';
    const recipientNameY = currentY + (60 * 0.5); // Center the text block roughly
    ctx.fillText(recipientName, RENDER_WIDTH / 2, recipientNameY);
    ctx.textBaseline = 'alphabetic'; // Reset
    currentY = recipientNameY + (60 * 0.5) + 20; // Approx bottom of text + space

    // Award Reason
    ctx.fillStyle = mainColor;
    ctx.font = `22px ${fontFamilyBodyParam}`;
    let awardReasonLineHeight = 28;
    // The y for wrapText is the baseline of the first line.
    let firstLineReasonY = currentY + awardReasonLineHeight * 0.8; // Start first line's baseline
    let lastLineReasonY = wrapText(ctx, awardReason, RENDER_WIDTH / 2, firstLineReasonY, RENDER_WIDTH - 2 * (innerBorderPadding + 30), awardReasonLineHeight);
    currentY = lastLineReasonY + 20; // Space after reason text

    // Bottom section: Date and Signature
    const bottomSectionLineY = RENDER_HEIGHT - innerBorderPadding - 70; // Y coord of the signature/date lines

    // Date
    const dateAreaX = innerBorderPadding + 150; 
    ctx.font = `16px ${fontFamilyBodyParam}`;
    ctx.fillStyle = mainColor;
    ctx.textAlign = 'center';
    ctx.fillText("Date: " + dateText, dateAreaX, bottomSectionLineY + 25); // Text below line
    ctx.beginPath(); // Line for Date
    ctx.moveTo(dateAreaX - 100, bottomSectionLineY); 
    ctx.lineTo(dateAreaX + 100, bottomSectionLineY);
    ctx.strokeStyle = mainColor;
    ctx.lineWidth = 1;
    ctx.stroke();

    // Signature & Issued By
    const signatureAreaX = RENDER_WIDTH - innerBorderPadding - 150; 
    ctx.textAlign = 'center'; 
    
    ctx.font = `20px ${fontFamilyScriptParam}`; 
    ctx.fillStyle = mainColor;
    ctx.textBaseline = 'middle'; 
    ctx.fillText(signaturePlaceholder, signatureAreaX, bottomSectionLineY - (20*0.3) ); // Position name slightly above the line
    ctx.textBaseline = 'alphabetic';

    ctx.beginPath(); // Line for Signature
    ctx.moveTo(signatureAreaX - 125, bottomSectionLineY); 
    ctx.lineTo(signatureAreaX + 125, bottomSectionLineY);
    ctx.strokeStyle = mainColor;
    ctx.lineWidth = 1;
    ctx.stroke();
    
    ctx.font = `16px ${fontFamilyBodyParam}`; 
    ctx.fillStyle = mainColor;
    ctx.fillText(issuedBy, signatureAreaX, bottomSectionLineY + 25);
    
    if (issuedByTitle) { 
        ctx.font = `italic 14px ${fontFamilyBodyParam}`;
        ctx.fillText(issuedByTitle, signatureAreaX, bottomSectionLineY + 25 + 18);
    }

    // Seal
    const sealRadius = 35;
    const sealX = RENDER_WIDTH / 2;
    
    // Calculate vertical space for seal: between award reason and signature lines
    const spaceForSealTop = currentY + sealRadius + 10; // Bottom of awardReason text + margin
    const spaceForSealBottom = bottomSectionLineY - (16 * 1.5) - sealRadius - 10; // Top of signature lines area - margin

    let sealFinalY = spaceForSealTop + (spaceForSealBottom - spaceForSealTop) / 2;
    
    const canFitSeal = (spaceForSealBottom >= spaceForSealTop) && (logoPlacement === 'seal' || logoSize>0) ; // also check logoSize for seal context

    if (canFitSeal) {
        sealFinalY = Math.max(sealFinalY, spaceForSealTop); // ensure it's below award reason
        sealFinalY = Math.min(sealFinalY, spaceForSealBottom); // ensure it's above signatures

        if (logoPlacement === 'seal' && originalImg && originalImg.complete && originalImg.naturalWidth > 0) {
            ctx.save();
            ctx.beginPath();
            ctx.arc(sealX, sealFinalY, sealRadius, 0, Math.PI * 2, true);
            ctx.clip();
            ctx.drawImage(originalImg, sealX - sealRadius, sealFinalY - sealRadius, sealRadius * 2, sealRadius * 2);
            ctx.restore();
            ctx.beginPath(); // Border for image seal
            ctx.arc(sealX, sealFinalY, sealRadius, 0, 2 * Math.PI, false);
            ctx.strokeStyle = mainColor;
            ctx.lineWidth = 1.5;
            ctx.stroke();
        } else if (logoPlacement !== 'seal') { // Draw default graphic seal only if originalImg is not meant for seal
            ctx.beginPath();
            ctx.arc(sealX, sealFinalY, sealRadius, 0, 2 * Math.PI, false);
            ctx.fillStyle = accentColor;
            ctx.fill();
            ctx.lineWidth = 2;
            ctx.strokeStyle = mainColor;
            ctx.stroke();
            
            ctx.font = `bold ${sealRadius * 0.4}px ${fontFamilyTitleParam}`;
            // Simple color contrast for seal text: use background color, assumes it's light.
            ctx.fillStyle = backgroundColor; 
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText("SEAL", sealX, sealFinalY);
        }
    }
    
    ctx.textBaseline = 'alphabetic'; // Reset to default for safety
    ctx.textAlign = 'left'; 

    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 Award Certificate Template tool allows users to create customized certificate designs by inputting recipient details, award reasons, dates, and other personalized content. This tool is ideal for schools, organizations, and businesses looking to acknowledge achievements, awards, or certifications. Users can select colors, fonts, and layouts to suit their branding or event themes, making it a versatile solution for generating professional-looking certificates quickly and easily.

Leave a Reply

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