Please bookmark this page to avoid losing your image tool!

Image ASCII Art Filter

(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, charSize = 10, fontFamily = 'monospace', charsInput = " .,:;i1tfLCG08@", bgColor = 'white', fgColor = 'black') {
    
    // 0. Validate and process parameters
    let currentFontSize = parseInt(charSize, 10);
    if (isNaN(currentFontSize) || currentFontSize <= 0) {
        // Default font size if charSize is invalid
        currentFontSize = 10;
    }

    const currentChars = (typeof charsInput === 'string' && charsInput.length > 0) ? charsInput : " .,:;i1tfLCG08@";
    const currentBgColor = (typeof bgColor === 'string' && bgColor) ? bgColor : 'white';
    const currentFgColor = (typeof fgColor === 'string' && fgColor) ? fgColor : 'black';
    const currentFontFamily = (typeof fontFamily === 'string' && fontFamily.trim() !== "") ? fontFamily.trim() : 'monospace';

    // 1. Create a temporary canvas to get image data
    const tempCanvas = document.createElement('canvas');
    // Optimization hint for getImageData, useful if this function is called frequently.
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); 
    
    // Validate the Image object
    if (!originalImg || typeof originalImg.width === 'undefined' || originalImg.width === 0 || originalImg.height === 0) {
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 250; 
        errorCanvas.height = 50;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.fillStyle = currentBgColor; 
        errCtx.fillRect(0,0,errorCanvas.width, errorCanvas.height);
        errCtx.fillStyle = currentFgColor;
        errCtx.font = `12px ${currentFontFamily}`;
        errCtx.textAlign = 'center';
        errCtx.textBaseline = 'middle';
        errCtx.fillText("Invalid or zero-dimension image.", errorCanvas.width/2, errorCanvas.height/2);
        return errorCanvas;
    }

    tempCanvas.width = originalImg.width;
    tempCanvas.height = originalImg.height;
    
    try {
        // Draw the image onto the temporary canvas.
        // This step can throw a SecurityError if the image is cross-origin and tainted.
        tempCtx.drawImage(originalImg, 0, 0, originalImg.width, originalImg.height);
    } catch (e) {
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 300; errorCanvas.height = 100;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.fillStyle = currentBgColor;
        errCtx.fillRect(0,0,errorCanvas.width, errorCanvas.height);
        errCtx.fillStyle = 'red'; 
        errCtx.font = `14px ${currentFontFamily}`;
        errCtx.textAlign = 'center';
        errCtx.textBaseline = 'middle';
        let errorMsg1 = "Error drawing original image.";
        let errorMsg2 = "Ensure it's a valid image object.";
        if (e.name === 'SecurityError') {
            errorMsg1 = "Cross-origin security error.";
            errorMsg2 = "Cannot process external image due to CORS.";
        }
        errCtx.fillText(errorMsg1, errorCanvas.width/2, errorCanvas.height/2 - 10);
        errCtx.fillText(errorMsg2, errorCanvas.width/2, errorCanvas.height/2 + 10);
        return errorCanvas;
    }

    let imageData;
    try {
        // Get pixel data from the temporary canvas.
        // This is often where SecurityError occurs for cross-origin images.
        imageData = tempCtx.getImageData(0, 0, originalImg.width, originalImg.height);
    } catch (e) {
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 300; errorCanvas.height = 100;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.fillStyle = currentBgColor;
        errCtx.fillRect(0,0,errorCanvas.width, errorCanvas.height);
        errCtx.fillStyle = 'red';
        errCtx.font = `14px ${currentFontFamily}`;
        errCtx.textAlign = 'center';
        errCtx.textBaseline = 'middle';
        let errorMsg1 = "Cannot read image pixel data.";
        let errorMsg2 = "Is the image from a different origin (CORS)?";
         if (e.name === 'SecurityError') {
            errorMsg1 = "Cross-origin security error.";
            errorMsg2 = "Cannot access pixels of external image.";
        }
        errCtx.fillText(errorMsg1, errorCanvas.width/2, errorCanvas.height/2 - 10);
        errCtx.fillText(errorMsg2, errorCanvas.width/2, errorCanvas.height/2 + 10);
        return errorCanvas;
    }
    const data = imageData.data;

    // 2. Calculate dimensions for the ASCII art grid
    const numCharsX = Math.floor(originalImg.width / currentFontSize);
    const numCharsY = Math.floor(originalImg.height / currentFontSize);

    // If image is too small for even one character block, display a message.
    if (numCharsX === 0 || numCharsY === 0) {
        const infoCanvas = document.createElement('canvas');
        // Create a reasonably sized canvas for the message.
        infoCanvas.width = Math.max(currentFontSize * 15, originalImg.width, 250); 
        infoCanvas.height = Math.max(currentFontSize * 3, originalImg.height, 60);
        const infoCtx = infoCanvas.getContext('2d');
        infoCtx.fillStyle = currentBgColor; 
        infoCtx.fillRect(0,0,infoCanvas.width, infoCanvas.height);
        infoCtx.fillStyle = currentFgColor;
        infoCtx.font = `${Math.min(currentFontSize, 14)}px ${currentFontFamily}`;
        infoCtx.textAlign = 'center';
        infoCtx.textBaseline = 'middle';
        infoCtx.fillText("Image too small for selected character size.", infoCanvas.width/2, infoCanvas.height/2);
        return infoCanvas;
    }

    // 3. Create and configure the output canvas
    const outputCanvas = document.createElement('canvas');
    const outputCtx = outputCanvas.getContext('2d');
    
    // Set output canvas dimensions based on the number of characters and font size
    outputCanvas.width = numCharsX * currentFontSize;
    outputCanvas.height = numCharsY * currentFontSize;

    // Set font properties for drawing characters
    outputCtx.font = `${currentFontSize}px ${currentFontFamily}`;
    outputCtx.textAlign = 'center';
    outputCtx.textBaseline = 'middle';
    
    // Fill the background of the output canvas
    outputCtx.fillStyle = currentBgColor;
    outputCtx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
    
    // 4. Iterate through image blocks, calculate brightness, and draw ASCII characters
    for (let j = 0; j < numCharsY; j++) { // j is the row index in the character grid
        for (let i = 0; i < numCharsX; i++) { // i is the column index in the character grid
            
            let sumBrightness = 0;
            let numPixelsInBlock = 0;

            // Define the corresponding block in the original image
            const imgBlockStartX = i * currentFontSize;
            const imgBlockStartY = j * currentFontSize;
            // Ensure block boundaries do not exceed original image dimensions
            const imgBlockEndX = Math.min(imgBlockStartX + currentFontSize, originalImg.width);
            const imgBlockEndY = Math.min(imgBlockStartY + currentFontSize, originalImg.height);

            // Calculate average brightness for the current image block
            for (let y = imgBlockStartY; y < imgBlockEndY; y++) {
                for (let x = imgBlockStartX; x < imgBlockEndX; x++) {
                    const pixelArrayIndex = (y * originalImg.width + x) * 4; // Each pixel has 4 components (R,G,B,A)
                    const r = data[pixelArrayIndex];
                    const g = data[pixelArrayIndex + 1];
                    const b = data[pixelArrayIndex + 2];
                    // const a = data[pixelArrayIndex + 3]; // Alpha component, not used in this brightness calculation

                    // Calculate brightness using the luminance formula (standard for perceived brightness)
                    const brightness = 0.299 * r + 0.587 * g + 0.114 * b;
                    sumBrightness += brightness;
                    numPixelsInBlock++;
                }
            }

            if (numPixelsInBlock === 0) {
                // This case should be rare if numCharsX/Y > 0 and dimensions are valid,
                // but as a safeguard, skip drawing if no pixels were processed.
                continue; 
            }

            const avgBrightness = sumBrightness / numPixelsInBlock;
            
            // Set character color (foreground color)
            outputCtx.fillStyle = currentFgColor;

            // Map average brightness to a character from the currentChars string.
            // The currentChars string is assumed to be ordered from characters representing
            // bright areas (e.g., space for white) to characters representing dark areas (e.g., '@' for black).
            // Brightest image part (avgBrightness = 255) -> currentChars[0]
            // Darkest image part (avgBrightness = 0)   -> currentChars[currentChars.length - 1]
            let charIndex = Math.round(((255 - avgBrightness) / 255) * (currentChars.length - 1));
            
            // Clamp index to ensure it's within the bounds of the currentChars string
            charIndex = Math.max(0, Math.min(currentChars.length - 1, charIndex));
            const charToDraw = currentChars[charIndex];

            // Calculate drawing position for the character (center of the current cell in the grid)
            const drawX = i * currentFontSize + currentFontSize / 2;
            const drawY = j * currentFontSize + currentFontSize / 2;
            
            outputCtx.fillText(charToDraw, drawX, drawY);
        }
    }

    return outputCanvas;
}

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 ASCII Art Filter is a web-based tool that converts images into ASCII art representations. Users can upload an image and customize the character size, font style, and the characters used to represent different shades of brightness in the image. This tool is useful for creating unique and artistic representations of photos, which can be used in digital art projects, social media posts, and as creative display options for text-based environments. Additionally, it can serve educational purposes in art and programming disciplines by demonstrating the interplay between image processing and text graphics.

Leave a Reply

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