Please bookmark this page to avoid losing your image tool!

Image Video Game Achievement 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,
    achievementTitle = "Achievement Unlocked",
    achievementDescription = "You did something amazing!",
    titleColor = "#FFFFFF",
    descriptionColor = "#B0B0B0",
    backgroundColor = "#1E1E1E",
    accentColor = "#FFBF00", 
    fontFamily = "Arial, sans-serif",
    iconCharacter = "⭐" 
) {
    const canvasHeight = 100;
    const canvasWidth = 450;
    const padding = 12;

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

    // 1. Draw Background & Top Accent
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, canvasWidth, canvasHeight);
    
    ctx.fillStyle = accentColor;
    ctx.fillRect(0, 0, canvasWidth, 4); // Top accent bar

    // 2. Draw Original Image
    const imageContainerSize = canvasHeight - 2 * padding; // e.g., 100 - 24 = 76
    const imgDisplayX = padding;
    const imgDisplayY = padding;

    // Calculate aspect ratio for originalImg to fit into imageContainerSize
    const imgAspectRatio = originalImg.width / originalImg.height;
    let drawnImgWidth = imageContainerSize;
    let drawnImgHeight = imageContainerSize;
    if (imgAspectRatio > 1) { // Image is wider than tall
        drawnImgHeight = imageContainerSize / imgAspectRatio;
    } else { // Image is taller than wide or square
        drawnImgWidth = imageContainerSize * imgAspectRatio;
    }
    // Calculate position to center the image within its container
    const imgPosX = imgDisplayX + (imageContainerSize - drawnImgWidth) / 2;
    const imgPosY = imgDisplayY + (imageContainerSize - drawnImgHeight) / 2;
    
    ctx.drawImage(originalImg, imgPosX, imgPosY, drawnImgWidth, drawnImgHeight);

    // Add a border around the image
    ctx.strokeStyle = accentColor;
    ctx.lineWidth = 2;
    ctx.strokeRect(imgDisplayX, imgDisplayY, imageContainerSize, imageContainerSize);

    // --- Layout for Icon and Text (right side of the image) ---
    let currentX = imgDisplayX + imageContainerSize + padding; // Starting X for elements right of the image

    // 3. Draw Icon (if any)
    if (iconCharacter && iconCharacter.trim() !== "") {
        const iconDisplaySize = 28; 
        ctx.font = `${iconDisplaySize}px ${fontFamily}`;
        ctx.fillStyle = accentColor;
        ctx.textAlign = "left";
        ctx.textBaseline = "middle"; 
        
        const iconX = currentX;
        const iconY = canvasHeight / 2; // Vertically center icon in the canvas overall height
        ctx.fillText(iconCharacter, iconX, iconY);
        
        currentX += ctx.measureText(iconCharacter).width + (padding / 2); // Advance X, add small gap
    }
    
    // --- Text Block Calculation & Drawing ---
    const titleFontSize = 20;
    const descriptionFontSize = 14;
    const textLineHeightDesc = Math.round(descriptionFontSize * 1.2); // For multi-line description
    const spaceBetweenTitleDesc = Math.round(padding / 3); // Space between title and description block

    const textAvailableWidth = canvasWidth - currentX - padding; // Remaining width for text

    // Determine description lines (max 2 lines)
    let line1Desc = "";
    let line2Desc = "";
    const descFont = `${descriptionFontSize}px ${fontFamily}`;

    if (achievementDescription && achievementDescription.trim() !== "") {
        ctx.font = descFont; // Set font for measuring description text
        let words = achievementDescription.split(' ');
        let currentBuildLine = "";
        
        // Determine Line 1 of Description
        for (const word of words) {
            if (ctx.measureText(currentBuildLine + word + " ").width > textAvailableWidth && currentBuildLine !== "") {
                break; 
            }
            currentBuildLine += word + " ";
        }
        line1Desc = currentBuildLine.trim();
        
        let remainingText = achievementDescription.substring(line1Desc.length).trimLeft();
        
        // Determine Line 2 of Description (if there's remaining text)
        if (remainingText) {
            if (ctx.measureText(remainingText).width <= textAvailableWidth) {
                line2Desc = remainingText; // All remaining text fits in line 2
            } else { // Remaining text needs truncation and ellipsis for line 2
                let tempLine2Content = "";
                const wordsForLine2 = remainingText.split(' ');
                for (let i = 0; i < wordsForLine2.length; i++) {
                    const word = wordsForLine2[i];
                    const prospectiveText = tempLine2Content + (tempLine2Content ? " " : "") + word;
                    // Check if this prospective text + "..." fits
                    if (ctx.measureText(prospectiveText + "...").width > textAvailableWidth) {
                        // If tempLine2Content is empty, the first word itself (with "...") is too long.
                        if (!tempLine2Content) { 
                            let truncatedWord = word;
                            while(ctx.measureText(truncatedWord + "...").width > textAvailableWidth && truncatedWord.length > 0) {
                                truncatedWord = truncatedWord.slice(0, -1);
                            }
                            // Check if even truncated word + "..." is too long (e.g., textAvailableWidth is tiny)
                            if (ctx.measureText(truncatedWord + "...").width > textAvailableWidth) {
                                tempLine2Content = ""; // Cannot fit this word
                            } else {
                                tempLine2Content = truncatedWord;
                            }
                        }
                        // else tempLine2Content (without current word) is the base.
                        break; 
                    }
                    tempLine2Content = prospectiveText;
                }
                line2Desc = tempLine2Content + "...";
                
                // Final check: if line2Desc (e.g. "...") is still too wide
                if (ctx.measureText(line2Desc).width > textAvailableWidth) {
                    if (ctx.measureText("...").width <= textAvailableWidth) line2Desc = "...";
                    else line2Desc = ""; // Cannot even fit ellipsis
                }
            }
        }
    }
    
    let actualNumDescLines = 0;
    if (line1Desc) actualNumDescLines++;
    if (line2Desc) actualNumDescLines++;

    // Calculate actual total height of the text block (title + description)
    let actualTextBlockHeight = titleFontSize; 
    if (actualNumDescLines > 0) {
        actualTextBlockHeight += spaceBetweenTitleDesc + (textLineHeightDesc * actualNumDescLines);
    }
    
    // Determine Y positions for the text block to be vertically centered in the canvas
    const textBlockStartY = (canvasHeight - actualTextBlockHeight) / 2;
    const titleX = currentX;
    const titleY = textBlockStartY;
    
    const descriptionX = currentX;
    // Description Y starts after title and spacing, still relative to textBlockStartY
    const descriptionY = titleY + titleFontSize + spaceBetweenTitleDesc;

    // Draw Title
    ctx.font = `bold ${titleFontSize}px ${fontFamily}`;
    ctx.fillStyle = titleColor;
    ctx.textAlign = "left";
    ctx.textBaseline = "top"; // Align text from its top-left corner
    ctx.fillText(achievementTitle, titleX, titleY, textAvailableWidth); // fillText handles overflow by clipping

    // Draw Description
    ctx.font = descFont; // Use the description font
    ctx.fillStyle = descriptionColor;
    if (line1Desc) {
        ctx.fillText(line1Desc, descriptionX, descriptionY, textAvailableWidth);
    }
    if (line2Desc) {
        // Y position for the second line of description
        ctx.fillText(line2Desc, descriptionX, descriptionY + textLineHeightDesc, textAvailableWidth);
    }

    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 Video Game Achievement Frame Creator is a tool designed to create customized achievement frames for images. Users can upload an image and add personalized text for the achievement title and description, which can be customized in terms of color, font, and icons. This tool is ideal for gamers and developers looking to showcase achievements in a visually appealing format, helping enhance the presentation of gaming milestones or accomplishments in screenshots. It can also be used for creating social media posts or sharing content that highlights achievements in a stylish manner.

Leave a Reply

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