Please bookmark this page to avoid losing your image tool!

Image World War Propaganda Poster Style 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.
async function processImage(
    originalImg,
    posterColorsStr = "#D72323,#222831,#EEEEEE,#F0A500", // Red, Dark Gray/Black, Off-White, Yellow/Gold
    contrastFactor = 1.3,
    posterizeLevels = 3,
    noiseAmount = 0.05, // 0 to 1 for noise intensity, 0 to disable
    addText = "",
    textColor = "#FFFFFF",
    textFont = "bold 40px 'Oswald', Impact, 'Arial Black', sans-serif",
    textPositionXPercent = 0.5, // 0 to 1, percentage of width
    textPositionYPercent = 0.9, // 0 to 1, percentage of height
    textStrokeColor = "#000000",
    textStrokeWidth = 3
) {

    // --- Helper Functions ---

    function hexToRgb(hex) {
        const bigint = parseInt(hex.slice(1), 16);
        const r = (bigint >> 16) & 255;
        const g = (bigint >> 8) & 255;
        const b = bigint & 255;
        return { r, g, b };
    }

    function colorDistanceSq(color1, color2) {
        const dr = color1.r - color2.r;
        const dg = color1.g - color2.g;
        const db = color1.b - color2.b;
        return dr * dr + dg * dg + db * db;
    }

    function findClosestColor(pixelRgb, paletteRgbs) {
        let closestColor = paletteRgbs[0];
        let minDistanceSq = colorDistanceSq(pixelRgb, closestColor);

        for (let i = 1; i < paletteRgbs.length; i++) {
            const distanceSq = colorDistanceSq(pixelRgb, paletteRgbs[i]);
            if (distanceSq < minDistanceSq) {
                minDistanceSq = distanceSq;
                closestColor = paletteRgbs[i];
            }
        }
        return closestColor;
    }
    
    // Use a Set outside to track loaded fonts across multiple calls if this function were part of a larger app.
    // For a single function call, this Set helps if ensureFontLoaded is called multiple times within one processImage call.
    // For the given constraints "Output only the JavaScript function", it's better to have it self-contained.
    const _loadedFonts = new Set();
    async function ensureFontLoaded(fontFamily, fontFileUrl, weight = 'normal', style = 'normal', display = 'swap') {
        const fontIdentifier = `${fontFamily}-${weight}-${style}`;
        if (_loadedFonts.has(fontIdentifier) || document.fonts.check(`${style} ${weight} 12px "${fontFamily}"`)) {
            return true;
        }

        const fontFace = new FontFace(fontFamily, `url(${fontFileUrl})`, { weight, style, display });
        try {
            await fontFace.load();
            document.fonts.add(fontFace);
            _loadedFonts.add(fontIdentifier);
            // console.log(`Font "${fontFamily}" (${weight}, ${style}) loaded from ${fontFileUrl}`);
            return true;
        } catch (e) {
            console.error(`Failed to load font "${fontFamily}" (${weight}, ${style}):`, e);
            return false;
        }
    }

    // --- Main Logic ---

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Ensure the image is loaded before trying to get its dimensions
    if (!originalImg.complete || originalImg.naturalWidth === 0) {
        // Fallback or error for unloaded image. For now, let's assume it's loaded.
        // If it's critical, one might await originalImg.onload here, but it's passed as loaded.
        console.error("Image not loaded or has zero dimensions.");
        // Create a small placeholder canvas
        canvas.width = 100;
        canvas.height = 100;
        ctx.fillStyle = 'red';
        ctx.fillRect(0,0,100,100);
        ctx.fillStyle = 'white';
        ctx.fillText("Error: Image not loaded", 10, 50);
        return canvas;
    }

    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    // Draw the original image
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // Parse poster colors from string to RGB objects
    const paletteRgbs = posterColorsStr.split(',').map(hex => hexToRgb(hex.trim()));
    if (paletteRgbs.length === 0) { // Fallback if string is bad
        paletteRgbs.push({r:0,g:0,b:0}, {r:255,g:255,b:255});
    }


    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
    const posterStep = (posterizeLevels > 1) ? 255 / (posterizeLevels - 1) : 0;

    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i + 1];
        let b = data[i + 2];

        // 1. Contrast
        if (contrastFactor !== 1.0) {
            r = contrastFactor * (r - 128) + 128;
            g = contrastFactor * (g - 128) + 128;
            b = contrastFactor * (b - 128) + 128;
        }

        // 2. Posterization (Quantization)
        if (posterizeLevels > 1 && posterStep > 0) {
            r = Math.round(r / posterStep) * posterStep;
            g = Math.round(g / posterStep) * posterStep;
            b = Math.round(b / posterStep) * posterStep;
        }
        
        // Clamp values after contrast and posterization
        r = Math.max(0, Math.min(255, r));
        g = Math.max(0, Math.min(255, g));
        b = Math.max(0, Math.min(255, b));

        // 3. Map to limited palette
        const closest = findClosestColor({ r, g, b }, paletteRgbs);
        r = closest.r;
        g = closest.g;
        b = closest.b;

        // 4. Noise (optional)
        if (noiseAmount > 0) {
            const noise = (Math.random() - 0.5) * 2 * 255 * noiseAmount;
            r = Math.max(0, Math.min(255, r + noise));
            g = Math.max(0, Math.min(255, g + noise));
            b = Math.max(0, Math.min(255, b + noise));
        }
        
        data[i] = r;
        data[i + 1] = g;
        data[i + 2] = b;
        // Alpha (data[i+3]) remains unchanged
    }

    ctx.putImageData(imageData, 0, 0);

    // 5. Add Text Overlay
    if (addText && addText.trim() !== "") {
        // Attempt to load Oswald font if specified and potentially not available
        if (textFont.toLowerCase().includes('oswald')) {
            // URL for Oswald Bold (700 weight)
            const oswaldBoldUrl = 'https://fonts.gstatic.com/s/oswald/v49/TK3_WkUHHAIjg75cFRf3bXL8LICs1_FvsUJiZTaR.woff2';
            // Assume 'bold' or weight 700 is desired for Oswald in this style
            await ensureFontLoaded('Oswald', oswaldBoldUrl, '700', 'normal');
        }

        ctx.font = textFont;
        ctx.fillStyle = textColor;
        ctx.textAlign = 'center';
        
        // Adjust baseline based on Y position to make it more intuitive
        if (textPositionYPercent < 0.2) { // Text near top
            ctx.textBaseline = 'top';
        } else if (textPositionYPercent > 0.8) { // Text near bottom
             ctx.textBaseline = 'bottom';
        } else { // Text in middle
            ctx.textBaseline = 'middle';
        }

        const x = canvas.width * textPositionXPercent;
        const y = canvas.height * textPositionYPercent;

        if (textStrokeWidth > 0 && textStrokeColor) {
            ctx.strokeStyle = textStrokeColor;
            ctx.lineWidth = textStrokeWidth;
            ctx.strokeText(addText, x, y);
        }
        ctx.fillText(addText, x, y);
    }

    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 World War Propaganda Poster Style Generator allows users to create stylized images that mimic the design elements of World War propaganda posters. Users can upload their images and apply a vintage effect by adjusting color palettes, adding contrast, and applying posterization for a simplistic color scheme. Additionally, the tool provides options to overlay custom text with various font styles, colors, and placements, enabling users to produce eye-catching, retro-styled graphics. This tool can be useful for graphic designers, artists, or anyone looking to create themed artwork, social media posts, or marketing materials inspired by historical propaganda art.

Leave a Reply

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