Please bookmark this page to avoid losing your image tool!

Evil Poster Image 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,
    posterText = "BEWARE",
    textColor = "rgb(200, 0, 0)", // A strong red
    tintColor = "rgba(80, 0, 0, 0.5)", // Dark red semi-transparent overlay
    fontFamily = "Creepster, cursive", // Evil-themed Google Font with fallback
    fontSizeRatio = 0.18, // Relative to image height
    baseFilter = 'grayscale(50%) contrast(150%) brightness(90%) sepia(30%)', // Initial image treatment
    vignetteColor = "rgba(0,0,0,0.7)", // Dark vignette at edges
    textShadowColor = "black",
    textShadowBlur = 8, // Set to 0 to disable shadow
    textShadowOffsetX = 3,
    textShadowOffsetY = 3
) {

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

    // Set canvas dimensions from image, with fallbacks
    canvas.width = originalImg.naturalWidth || originalImg.width || 300;
    canvas.height = originalImg.naturalHeight || originalImg.height || 150;

    // Ensure dimensions are positive; otherwise, return a minimal canvas
    if (canvas.width <= 0 || canvas.height <= 0) {
        console.warn("Image has invalid dimensions. Returning basic canvas.");
        canvas.width = Math.max(1, canvas.width || 100); // Ensure positive
        canvas.height = Math.max(1, canvas.height || 100); // Ensure positive
        ctx.fillStyle = 'gray'; // Indicate an issue or empty state
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        if (posterText) {
            ctx.fillStyle = 'white';
            ctx.font = '10px sans-serif';
            ctx.textAlign = 'center';
            ctx.fillText("Error: Invalid Image", canvas.width/2, canvas.height/2);
        }
        return canvas;
    }
    
    const H_FONT_FAMILY_TO_USE = fontFamily;
    // Extract the primary font name (e.g., "Creepster" from "Creepster, cursive")
    const primaryFontName = fontFamily.split(',')[0].replace(/["']/g, '').trim();

    // Dynamically load "Creepster" font if specified and not already available in a browser environment
    if (primaryFontName === "Creepster" && typeof document !== 'undefined' && document.head && document.fonts) {
        // Check if font is already loaded/available
        if (!document.fonts.check(`1em "${primaryFontName}"`)) { 
            const link = document.createElement('link');
            link.href = `https://fonts.googleapis.com/css2?family=${primaryFontName.replace(/\s/g, '+')}&display=swap`;
            link.rel = 'stylesheet';
            
            // Create a promise to await font loading
            const fontLoadPromise = new Promise((resolve, reject) => {
                link.onload = () => {
                    // Stylesheet loaded, now attempt to load the font definition
                    document.fonts.load(`1em "${primaryFontName}"`)
                        .then(resolve) // Font face ready
                        .catch(reject); // Font face loading failed
                };
                link.onerror = reject; // CSS link loading failed
            });

            document.head.appendChild(link);

            try {
                await fontLoadPromise;
                // console.log(`Font "${primaryFontName}" loaded successfully.`);
            } catch (e) {
                console.warn(`Failed to load font "${primaryFontName}". Fallback font will be used. Error:`, e);
                // Browser will use fallback font (e.g., "cursive") as specified in H_FONT_FAMILY_TO_USE
            }
        }
    }

    // --- Image Processing ---

    // 1. Draw original image with base filters
    // Ensure baseFilter is a non-empty string before applying
    if (baseFilter && baseFilter.trim() !== "" && baseFilter.trim().toLowerCase() !== "none") {
        ctx.filter = baseFilter;
    }
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    ctx.filter = 'none'; // Reset filter for subsequent drawing operations

    // 2. Apply the "evil" tint overlay
    // Ensure tintColor is a non-empty string and not transparent before applying
    if (tintColor && tintColor.trim() !== "" && tintColor.toLowerCase() !== "transparent" && tintColor.toLowerCase() !== "none") {
        ctx.fillStyle = tintColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // 3. Add vignette effect
    // Ensure vignetteColor is a non-empty string and not transparent before applying
    if (vignetteColor && vignetteColor.trim() !== "" && vignetteColor.toLowerCase() !== "transparent" && vignetteColor.toLowerCase() !== "none") {
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        
        // Make gradient radii relative to min dimension for a somewhat circular inner transparent area,
        // and max dimension for outer edge to cover corners well.
        const innerRadius = Math.min(canvas.width, canvas.height) * 0.2; // Smaller transparent center
        const outerRadius = Math.max(canvas.width, canvas.height) * 0.85; // Ensure vignette reaches far edges

        const gradient = ctx.createRadialGradient(
            centerX, centerY, innerRadius,
            centerX, centerY, outerRadius
        );
        
        let centerVignetteColor = 'rgba(0,0,0,0)'; // Default fully transparent black center
        // Try to make the center of the vignette match the hue & saturation of vignetteColor, but fully transparent
        if (vignetteColor.startsWith('rgba')) { // e.g. rgba(80,0,0,0.7) -> rgba(80,0,0,0)
            centerVignetteColor = vignetteColor.replace(/,( ?[\d.]+ ?)\)$/, ',0)'); // Replace alpha component with 0
        } else if (vignetteColor.startsWith('rgb')) { // e.g. rgb(80,0,0) -> rgba(80,0,0,0)
            centerVignetteColor = vignetteColor.replace('rgb', 'rgba').replace(')', ',0)');
        }
        // For hex or named colors, the default 'rgba(0,0,0,0)' for center works well.

        gradient.addColorStop(0, centerVignetteColor); 
        gradient.addColorStop(1, vignetteColor);    
        
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // --- Text Rendering ---
    if (posterText && posterText.trim() !== "") {
        const fontSize = Math.max(10, Math.floor(canvas.height * fontSizeRatio)); // Minimum font size 10px
        ctx.font = `${fontSize}px ${H_FONT_FAMILY_TO_USE}`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'alphabetic'; // A common baseline for text rendering

        const textX = canvas.width / 2;
        // Position text Y: Considering padding from bottom.
        // 'alphabetic' baseline is where most characters sit. Padding of 0.5 * fontSize from bottom edge.
        const textY = canvas.height - (fontSize * 0.5); 

        // Apply shadow for "evil" text effect
        if (textShadowBlur > 0 || textShadowOffsetX !== 0 || textShadowOffsetY !== 0) {
            ctx.shadowColor = textShadowColor;
            ctx.shadowBlur = textShadowBlur;
            ctx.shadowOffsetX = textShadowOffsetX;
            ctx.shadowOffsetY = textShadowOffsetY;
        }
        
        ctx.fillStyle = textColor;
        ctx.fillText(posterText, textX, textY);

        // Reset shadow settings for any subsequent drawing operations (good practice)
        ctx.shadowColor = 'transparent';
        ctx.shadowBlur = 0;
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
    }

    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 Evil Poster Image Creator is an online tool designed to transform your images into spooky, themed posters. With customizable features, users can apply a dark tint, vintage effects, and add eerie text overlays to their images, making it ideal for creating Halloween-themed graphics, promotional materials for creepy events, or simply for artistic expression. Users can easily adjust text color, font styles, and apply various filters to achieve the desired menacing look, making it versatile for both personal and professional use.

Leave a Reply

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