Please bookmark this page to avoid losing your image tool!

Image Wanted Poster Generator

(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.
function processImage(
    originalImg,
    headlineText = "WANTED",
    subText = "DEAD OR ALIVE",
    nameText = "UNKNOWN MISCREANT",
    rewardText = "$10,000 REWARD",
    mainFont = "Impact, Charcoal, sans-serif",
    secondaryFont = "'Courier New', Courier, monospace",
    textColor = "#3A2A1B",
    backgroundColor = "#F5E8C7",
    imageBorderColor = "#704214",
    posterBorderWidth = 20,
    posterBorderColor = "#5C3D2E",
    imageEffect = "sepia" // "none", "grayscale", "sepia"
) {

    const CANVAS_WIDTH = 600;
    const CANVAS_HEIGHT = 800;
    const INNER_PADDING = 20; // Padding inside the border for content elements

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

    // Helper function for text wrapping
    function _helper_drawWrappedText_internal(context, text, x, y, maxWidth, lineHeight, maxLines = 2) {
        let words = text.split(' ');
        let line = '';
        let currentY = y;
        let linesCount = 0;
        let totalHeight = 0;

        for(let i = 0; i < words.length; i++) {
            let testLine = line + words[i] + ' ';
            // futuros issues: measureText for very long strings or special chars might be slow.
            let metrics = context.measureText(testLine); 
            let testWidth = metrics.width;

            if (testWidth > maxWidth && i > 0) { 
                if (linesCount < maxLines) {
                    context.fillText(line.trim(), x, currentY);
                    currentY += lineHeight;
                    totalHeight += lineHeight;
                    linesCount++;
                    line = words[i] + ' '; 
                } else { 
                    return totalHeight; 
                }
            } else { 
                line = testLine;
            }
        }

        if (linesCount < maxLines && line.trim() !== '') {
            context.fillText(line.trim(), x, currentY);
            totalHeight += lineHeight;
            // linesCount++; // Not strictly needed here as we return totalHeight
        }
        return totalHeight; 
    }


    // 1. Draw Background Color
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

    // 2. Draw Poster Border
    if (posterBorderWidth > 0) {
        ctx.strokeStyle = posterBorderColor;
        ctx.lineWidth = posterBorderWidth;
        ctx.strokeRect(
            posterBorderWidth / 2,
            posterBorderWidth / 2,
            CANVAS_WIDTH - posterBorderWidth,
            CANVAS_HEIGHT - posterBorderWidth
        );
    }
    
    // Calculate effective Rcontent area boundaries
    const contentAreaX_Start = posterBorderWidth;
    const contentAreaY_Start = posterBorderWidth;
    const contentAreaWidth = CANVAS_WIDTH - 2 * posterBorderWidth;
    const contentAreaHeight = CANVAS_HEIGHT - 2 * posterBorderWidth;
    
    let currentY = contentAreaY_Start + INNER_PADDING;

    ctx.fillStyle = textColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'top';

    // 3. Headline Text ("WANTED")
    const HEADLINE_FONT_SIZE = Math.max(20, Math.min(80, contentAreaWidth / 7));
    ctx.font = `bold ${HEADLINE_FONT_SIZE}px ${mainFont}`;
    ctx.fillText(headlineText.toUpperCase(), CANVAS_WIDTH / 2, currentY);
    currentY += HEADLINE_FONT_SIZE + (HEADLINE_FONT_SIZE * 0.15); // Spacing

    // 4. Sub Text ("DEAD OR ALIVE")
    const SUBTEXT_FONT_SIZE = Math.max(14,Math.min(30, contentAreaWidth / 16));
    ctx.font = `${SUBTEXT_FONT_SIZE}px ${secondaryFont}`;
    ctx.fillText(subText.toUpperCase(), CANVAS_WIDTH / 2, currentY);
    currentY += SUBTEXT_FONT_SIZE + (SUBTEXT_FONT_SIZE * 0.5); // Spacing

    // 5. Image Processing & Drawing
    let imageToDraw = originalImg;
    const imgOriginalWidth = originalImg.naturalWidth || originalImg.width;
    const imgOriginalHeight = originalImg.naturalHeight || originalImg.height;

    if (imageEffect !== "none" && imgOriginalWidth > 0 && imgOriginalHeight > 0) {
        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = imgOriginalWidth;
        tempCanvas.height = imgOriginalHeight;
        const tempCtx = tempCanvas.getContext('2d');
        tempCtx.drawImage(originalImg, 0, 0, imgOriginalWidth, imgOriginalHeight);
        const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
        const data = imageData.data;

        if (imageEffect === "grayscale") {
            for (let i = 0; i < data.length; i += 4) {
                const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
                data[i] = data[i + 1] = data[i + 2] = avg;
            }
        } else if (imageEffect === "sepia") {
            for (let i = 0; i < data.length; i += 4) {
                const r = data[i], g = data[i + 1], b = data[i + 2];
                data[i]     = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
                data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
                data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
            }
        }
        tempCtx.putImageData(imageData, 0, 0);
        imageToDraw = tempCanvas;
    }
    
    // Calculate image display dimensions
    const MAX_NAME_LINES = 2;
    const NAME_FONT_SIZE = Math.max(16,Math.min(32, contentAreaWidth / 18));
    const NAME_LINE_HEIGHT_FACTOR = 1.2;
    const nameLineHeight = NAME_FONT_SIZE * NAME_LINE_HEIGHT_FACTOR;
    const spaceForNameText = nameLineHeight * MAX_NAME_LINES + (NAME_FONT_SIZE * 0.5); // Font size + spacing after

    const REWARD_FONT_SIZE = Math.max(18,Math.min(36, contentAreaWidth / 16));
    const spaceForRewardText = REWARD_FONT_SIZE + INNER_PADDING; // Font size + bottom padding

    const topSectionHeight = currentY - (contentAreaY_Start + INNER_PADDING);
    const availableHeightForImageAndLowerText = contentAreaHeight - INNER_PADDING - topSectionHeight - INNER_PADDING; // Top padding already used, Subtract bottom padding too
    
    const IMG_AREA_MAX_HEIGHT = Math.max(50, availableHeightForImageAndLowerText - spaceForNameText - spaceForRewardText);
    const IMG_AREA_MAX_WIDTH = contentAreaWidth - 2 * INNER_PADDING;

    let imgDrawWidth = imageToDraw.width;
    let imgDrawHeight = imageToDraw.height;

    if (imgDrawWidth <= 0 || imgDrawHeight <= 0) { // Handle case of invalid image (e.g. not loaded)
        imgDrawWidth = IMG_AREA_MAX_WIDTH;
        imgDrawHeight = IMG_AREA_MAX_HEIGHT / 2; // Default placeholder size
        ctx.fillStyle = '#CCCCCC';
        ctx.fillRect((CANVAS_WIDTH - imgDrawWidth) / 2, currentY, imgDrawWidth, imgDrawHeight);
        ctx.fillStyle = textColor; // Reset fillStyle
        ctx.fillText("Image Error", CANVAS_WIDTH / 2, currentY + imgDrawHeight/2 - 10);
    } else {
        const imgAspect = imgDrawWidth / imgDrawHeight;
        if (imgDrawWidth > IMG_AREA_MAX_WIDTH) {
            imgDrawWidth = IMG_AREA_MAX_WIDTH;
            imgDrawHeight = imgDrawWidth / imgAspect;
        }
        if (imgDrawHeight > IMG_AREA_MAX_HEIGHT) {
            imgDrawHeight = IMG_AREA_MAX_HEIGHT;
            imgDrawWidth = imgDrawHeight * imgAspect;
        }
        if (imgDrawWidth > IMG_AREA_MAX_WIDTH) { // Re-check width if height adjustment pushed it over
            imgDrawWidth = IMG_AREA_MAX_WIDTH;
            imgDrawHeight = imgDrawWidth / imgAspect;
        }
    }
    
    const imgDrawX = (CANVAS_WIDTH - imgDrawWidth) / 2;
    const imgDrawY = currentY;

    if (imgDrawWidth > 0 && imgDrawHeight > 0 && imageToDraw.width > 0 && imageToDraw.height > 0) { // Check again before drawing
       ctx.drawImage(imageToDraw, imgDrawX, imgDrawY, imgDrawWidth, imgDrawHeight);
    }

    // Image Border
    const IMG_BORDER_WIDTH = Math.max(2, Math.min(6, posterBorderWidth / 4));
    ctx.strokeStyle = imageBorderColor;
    ctx.lineWidth = IMG_BORDER_WIDTH;
    ctx.strokeRect(
        imgDrawX - IMG_BORDER_WIDTH / 2,
        imgDrawY - IMG_BORDER_WIDTH / 2,
        imgDrawWidth + IMG_BORDER_WIDTH,
        imgDrawHeight + IMG_BORDER_WIDTH
    );

    currentY += imgDrawHeight + (NAME_FONT_SIZE * 0.5); // Spacing after image

    // 6. Name Text
    ctx.font = `bold ${NAME_FONT_SIZE}px ${secondaryFont}`;
    const nameTextActualHeight = _helper_drawWrappedText_internal(
        ctx, nameText.toUpperCase(), CANVAS_WIDTH / 2, currentY,
        contentAreaWidth - 2 * INNER_PADDING, // Max width for name
        nameLineHeight, MAX_NAME_LINES
    );
    currentY += nameTextActualHeight + (REWARD_FONT_SIZE * 0.25); // Spacing

    // 7. Reward Text
    ctx.font = `bold ${REWARD_FONT_SIZE}px ${secondaryFont}`;
    // Adjust Y for reward text if currentY is too low, ensuring it's on canvas
    const rewardY = Math.min(currentY, CANVAS_HEIGHT - posterBorderWidth - INNER_PADDING - REWARD_FONT_SIZE);
    ctx.fillText(rewardText.toUpperCase(), CANVAS_WIDTH / 2, rewardY);

    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 Wanted Poster Generator is a fun tool that allows you to create customized wanted posters with your images. Users can upload a photo and add personalized text including a headline, subtext, name, and reward amount. The tool offers various design options like text font styles, colors, and effects such as sepia or grayscale for the image. This utility is useful for creating playful wanted posters for events, parties, or social media, and can be used for personal projects, art, or just for a bit of humorous creativity.

Leave a Reply

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