Please bookmark this page to avoid losing your image tool!

Image To Braille Pattern Art Converter

(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, charsAcross = "80", threshold = "128", invert = "false", fontSize = "10") {
    const numCharsAcrossVal = parseInt(charsAcross) || 80;
    const thresholdValue = parseFloat(threshold) || 128;
    const invertOutput = String(invert).toLowerCase() === "true";
    const outputFontSizeVal = parseInt(fontSize) || 10;

    if (!originalImg || typeof originalImg.naturalWidth === 'undefined' || !originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        console.error("Image is not fully loaded, is invalid, or has zero dimensions.");
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 350;
        errorCanvas.height = 100;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = '#fdd'; // Light red background
        errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
        errorCtx.fillStyle = 'black';
        errorCtx.font = '14px Arial';
        errorCtx.textAlign = 'center';
        errorCtx.textBaseline = 'middle';
        errorCtx.fillText("Error: Image not loaded or invalid.", errorCanvas.width / 2, errorCanvas.height / 2 - 10);
        errorCtx.fillText("Please provide a valid, loaded image.", errorCanvas.width / 2, errorCanvas.height / 2 + 10);
        return errorCanvas;
    }

    const sourceCanvas = document.createElement('canvas');
    const sourceCtx = sourceCanvas.getContext('2d', { willReadFrequently: true });
    
    sourceCanvas.width = originalImg.naturalWidth;
    sourceCanvas.height = originalImg.naturalHeight;
    sourceCtx.drawImage(originalImg, 0, 0);

    const imgWidth = sourceCanvas.width;
    const imgHeight = sourceCanvas.height;

    const typicalCharAspectRatio = 0.5; // Assumed width/height for a font character (e.g., 8px wide / 16px tall)
    const numCharsDownVal = Math.max(1, Math.round(numCharsAcrossVal * (imgHeight / imgWidth) / typicalCharAspectRatio));

    const cellWidthPx = imgWidth / numCharsAcrossVal;
    const cellHeightPx = imgHeight / numCharsDownVal;

    // Dimensions of the sub-region within an image cell that maps to one dot
    const dotRegionWidthPx = cellWidthPx / 2; // Braille characters are 2 dots wide
    const dotRegionHeightPx = cellHeightPx / 4; // Braille characters are 4 dots high

    let imageData;
    try {
        imageData = sourceCtx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error getting image data (CORS issue or other):", e);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 350; errorCanvas.height = 150;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = '#fdd';
        errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
        errorCtx.fillStyle = 'black'; errorCtx.font = '14px Arial';
        errorCtx.textAlign = 'center'; errorCtx.textBaseline = 'middle';
        errorCtx.fillText("Error processing image data.", errorCanvas.width / 2, errorCanvas.height / 2 - 20);
        errorCtx.fillText("This might be a CORS issue if", errorCanvas.width/2, errorCanvas.height/2);
        errorCtx.fillText("the image is from another domain.", errorCanvas.width/2, errorCanvas.height/2 + 20);
        return errorCanvas;
    }
    const pixels = imageData.data;

    const brailleCharsMatrix = [];

    for (let charY = 0; charY < numCharsDownVal; charY++) {
        const rowChars = [];
        for (let charX = 0; charX < numCharsAcrossVal; charX++) {
            let brailleCode = 0; // Bitmask for Braille dots for U+28xx

            // Iterate over the 8 dot positions within the Braille cell (2 columns x 4 rows)
            for (let dotCol = 0; dotCol < 2; dotCol++) { // 0 for left, 1 for right column of dots
                for (let dotRow = 0; dotRow < 4; dotRow++) { // 0 to 3 for rows of dots

                    // Calculate the image region for this specific dot
                    const regionXOrigin = charX * cellWidthPx + dotCol * dotRegionWidthPx;
                    const regionYOrigin = charY * cellHeightPx + dotRow * dotRegionHeightPx;
                    
                    let sumLuminance = 0;
                    let numPixelsInRegion = 0;

                    // Define the pixel sampling loop bounds for this dot's region
                    // Ensure start coords are floored, end coords are ceiled, and within image bounds
                    const R_X_START = Math.floor(regionXOrigin);
                    const R_Y_START = Math.floor(regionYOrigin);
                    const R_X_END = Math.min(imgWidth, Math.ceil(regionXOrigin + dotRegionWidthPx));
                    const R_Y_END = Math.min(imgHeight, Math.ceil(regionYOrigin + dotRegionHeightPx));

                    for (let py = R_Y_START; py < R_Y_END; py++) {
                        for (let px = R_X_START; px < R_X_END; px++) {
                            // Pixels array is 1D: (y * width + x) * 4 channels (R,G,B,A)
                            const offset = (py * imgWidth + px) * 4;
                            const r = pixels[offset];
                            const g = pixels[offset + 1];
                            const b = pixels[offset + 2];
                            // Standard luminance calculation (Rec.709 formula)
                            const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
                            sumLuminance += luminance;
                            numPixelsInRegion++;
                        }
                    }
                    
                    // Calculate average luminance, or default if region was empty/outside image
                    // Default to background color (non-dot) if region empty.
                    const avgLuminance = (numPixelsInRegion > 0) ? 
                                         sumLuminance / numPixelsInRegion : 
                                         (invertOutput ? 0 : 255); 

                    // Determine if dot is set based on threshold and invert logic
                    // Default: dark image part = dot (luminance < threshold).
                    // Inverted: light image part = dot (luminance > threshold).
                    let dotIsSet = (invertOutput) ? (avgLuminance > thresholdValue) : (avgLuminance < thresholdValue);
                    
                    if (dotIsSet) {
                        // Braille dot mapping to bits (for U+2800 + bitmask)
                        // Dot  Pattern | Bit  | Value
                        //  1    * .    |  0   |  0x01
                        //  2    * .    |  1   |  0x02
                        //  3    * .    |  2   |  0x04
                        //  4    . *    |  3   |  0x08
                        //  5    . *    |  4   |  0x10
                        //  6    . *    |  5   |  0x20
                        //  7    * .    |  6   |  0x40  (bottom-left)
                        //  8    . *    |  7   |  0x80  (bottom-right)
                        let bitPosition = -1;
                        if (dotCol === 0) { // Left column of dots
                            if (dotRow === 0) bitPosition = 0; // Dot 1
                            else if (dotRow === 1) bitPosition = 1; // Dot 2
                            else if (dotRow === 2) bitPosition = 2; // Dot 3
                            else if (dotRow === 3) bitPosition = 6; // Dot 7
                        } else { // Right column of dots (dotCol === 1)
                            if (dotRow === 0) bitPosition = 3; // Dot 4
                            else if (dotRow === 1) bitPosition = 4; // Dot 5
                            else if (dotRow === 2) bitPosition = 5; // Dot 6
                            else if (dotRow === 3) bitPosition = 7; // Dot 8
                        }
                        if (bitPosition !== -1) {
                            brailleCode |= (1 << bitPosition);
                        }
                    }
                }
            }
            rowChars.push(String.fromCharCode(0x2800 + brailleCode));
        }
        brailleCharsMatrix.push(rowChars);
    }

    // Render Braille characters to the output canvas
    const outputCanvas = document.createElement('canvas');
    const outputCtx = outputCanvas.getContext('2d');
    
    // Use a font stack favoring those with good Braille character support
    const fontFamily = '"DejaVu Sans Mono", Consolas, "Courier New", monospace';
    outputCtx.font = `${outputFontSizeVal}px ${fontFamily}`;
    
    // Measure character dimensions for canvas sizing
    const testCharMetrics = outputCtx.measureText("⣿"); // A full Braille character (U+28FF)
    let charDisplayWidth = testCharMetrics.width;
    if (charDisplayWidth === 0) charDisplayWidth = outputFontSizeVal * 0.6; // Fallback width if measurement fails

    // For line height, using the specified font size tends to be robust.
    let charDisplayHeight = outputFontSizeVal; 

    outputCanvas.width = Math.max(1, charDisplayWidth * numCharsAcrossVal);
    outputCanvas.height = Math.max(1, charDisplayHeight * numCharsDownVal);

    // Re-apply font settings as canvas resize might reset them
    outputCtx.font = `${outputFontSizeVal}px ${fontFamily}`;
    outputCtx.textBaseline = "top"; // Align text rendering to the top
    
    // Set background and foreground colors (dots are typically black on white)
    outputCtx.fillStyle = "white"; // Canvas background
    outputCtx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
    outputCtx.fillStyle = "black"; // Braille dot color

    for (let y = 0; y < numCharsDownVal; y++) {
        for (let x = 0; x < numCharsAcrossVal; x++) {
            outputCtx.fillText(brailleCharsMatrix[y][x], x * charDisplayWidth, y * charDisplayHeight);
        }
    }
    
    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 To Braille Pattern Art Converter allows users to convert images into a representation using Braille patterns. This tool can take an uploaded image and, based on user-defined parameters such as the number of Braille characters across the output, a luminance threshold for dot representation, and text inversion options, transforms the image into a grid of Braille characters. This functionality is particularly useful for creating tactile images for visually impaired individuals, educational materials, or innovative art projects that incorporate both visual and tactile elements.

Leave a Reply

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

Other Image Tools:

Image To Mathematical Symbols Art Converter

Image To ANSI Terminal Art Converter

Image Dashed Line Adder

Image To Dingbat Symbol Art Converter

Image Custom Polygon Cropper

Image Rule Of Thirds Grid Overlay Tool

Image Resizer for Social Media Platforms

Image Circular Crop Tool

Image Date Stamp Adder

Image Circular Guides Adder

Image Center Cropper

Image Petzval Lens Swirly Bokeh Effect Creator

Image Mimiya 645 Medium Format Filter Effect Tool

Photo Fujifilm Klasse W Filter Effect Application

Image Deardorff Large Format Filter Effect Application

Image Lomo LC-A Filter Effect Tool

Image Large Format Filter Effect Application

Image Zone Plate Lens Effect Creator

Photo Kodak Retina Filter Effect Tool

Image Polaroid 600 Filter Effect Tool

Photo Black and White Yellow Filter Effect Tool

Image Contax G2 Film Camera Render Effect Applicator

Image 110 Film Format Filter Effect Tool

Photo Jupiter-9 Portrait Lens Filter Effect

Image Fujifilm GW690 Texas Leica Filter Effect Application

Image Zeiss T* Coating Filter Effect Tool

Image Hoya R72 Infrared Filter Effect Tool

Image Filter Effect for Zeiss Ikon Contaflex

Photo Olympus Mju-II/Stylus Epic Filter Effect Tool

Image NiSi Nano IR ND Filter Effect Tool

Image Polaroid SX-70 Filter Effect Tool

Image Linhof Technika Filter Effect Tool

Image Lee Big Stopper 10-Stop ND Filter Effect Tool

Image Minolta X-700 Film Camera Render Effect Creator

Image ORWO UN54 Motion Picture Film Effect Applicator

Image Shen-Hao Large Format Filter Effect Tool

See All →