Please bookmark this page to avoid losing your image tool!

Image To Hangul Character 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,
    outputWidthCharsStr = "80",
    fontSizeStr = "10",
    charSetString = ' ,ㆍ,ᆢ,ㅣ,ㅡ,ㄱ,ㄴ,ㅇ,ㅅ,ㅏ,ㅗ,ㄷ,ㄹ,ㅈ,ㅁ,ㅂ,ㅎ,ㅊ,ㅋ,ㅌ,ㅍ,ㄲ,ㄸ,ㅃ,ㅆ,ㅉ',
    invertColorsStr = "false",
    backgroundColorStr = "white",
    textColorStr = "black"
) {
    // --- Parameter Parsing and Validation ---
    const outputWidthChars = parseInt(outputWidthCharsStr, 10) || 80;
    const fontSize = parseInt(fontSizeStr, 10) || 10;
    
    let chars = charSetString.split(',');
    // If the character set is empty or contains only empty strings (e.g., from " ,, "), use a fallback.
    if (chars.length === 0 || chars.every(c => c === "")) {
        chars = [' ', '·', '■']; // A simple fallback set: space, middle dot, black square
    }

    const invert = invertColorsStr === 'true';
    const backgroundColor = backgroundColorStr || 'white';
    const textColor = textColorStr || 'black';

    const errorCanvas = document.createElement('canvas'); // Used for returning in case of error

    if (originalImg.width === 0 || originalImg.height === 0) {
        console.error("Original image has zero width or height.");
        errorCanvas.width = 1; errorCanvas.height = 1;
        return errorCanvas;
    }
    if (outputWidthChars <= 0) {
        console.error("Output width in characters must be positive.");
        errorCanvas.width = originalImg.width; errorCanvas.height = originalImg.height;
        const ctx = errorCanvas.getContext('2d');
        if (ctx) ctx.drawImage(originalImg, 0, 0);
        return errorCanvas;
    }
    if (fontSize <= 0) {
        console.error("Font size must be positive.");
        errorCanvas.width = originalImg.width; errorCanvas.height = originalImg.height;
        const ctx = errorCanvas.getContext('2d');
        if (ctx) ctx.drawImage(originalImg, 0, 0);
        return errorCanvas;
    }

    // --- Font Loading (Nanum Gothic Coding) ---
    const FONT_NAME_TAG = 'NanumGothicCodingHangulArt'; // Unique name for our loaded font
    let FONT_FAMILY_TO_USE = 'monospace'; // Default fallback

    if (typeof FontFace === 'function' && document.fonts) { // Check if FontFace API and document.fonts are supported
        try {
            // Check if the font is already loaded and available under our custom tag
            let fontAlreadyLoaded = false;
            for (const font of document.fonts) {
                if (font.family === FONT_NAME_TAG && font.status === 'loaded') {
                    fontAlreadyLoaded = true;
                    break;
                }
            }

            if (fontAlreadyLoaded) {
                 FONT_FAMILY_TO_USE = FONT_NAME_TAG;
            } else {
                const fontFace = new FontFace(FONT_NAME_TAG, 'url(https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_one@1.0/NanumGothicCoding.woff)');
                await fontFace.load();
                document.fonts.add(fontFace);
                FONT_FAMILY_TO_USE = FONT_NAME_TAG;
            }
        } catch (e) {
            console.warn(`Failed to load ${FONT_NAME_TAG} font. Falling back to generic monospace. Error: ${e}`);
            // FONT_FAMILY_TO_USE remains 'monospace'
        }
    } else {
        console.warn("FontFace API or document.fonts not supported. Using generic monospace.");
    }
    
    // --- Determine Character Cell Dimensions ---
    const tempMeasureCanvas = document.createElement('canvas');
    const tempMeasureCtx = tempMeasureCanvas.getContext('2d');
     if (!tempMeasureCtx) return document.createElement('canvas'); // Should not happen

    tempMeasureCtx.font = `${fontSize}px ${FONT_FAMILY_TO_USE}`;
    // Use '한' (a common full-width Hangul syllable) for width measurement.
    // Fallback to fontSize, assuming square cell if measurement fails.
    const charDisplayWidth = tempMeasureCtx.measureText('한').width || fontSize; 
    const charDisplayHeight = fontSize; // Font size is typically the primary measure of character height.

    if (charDisplayWidth === 0) {
        console.error("Character display width is zero (font measurement failed).");
        errorCanvas.width = originalImg.width; errorCanvas.height = originalImg.height;
        const ctx = errorCanvas.getContext('2d');
        if (ctx) ctx.drawImage(originalImg, 0, 0);
        return errorCanvas;
    }
    
    // --- Calculate Output Canvas Dimensions in Characters ---
    const imageAspectRatio = originalImg.height / originalImg.width;
    const charCellAspectRatio = charDisplayWidth / charDisplayHeight;
    
    const outputHeightChars = Math.round(outputWidthChars * imageAspectRatio / charCellAspectRatio);

    if (isNaN(outputHeightChars) || outputHeightChars <= 0) {
        console.error("Calculated output height in characters is invalid (<=0 or NaN).");
        errorCanvas.width = originalImg.width; errorCanvas.height = originalImg.height;
        const ctx = errorCanvas.getContext('2d');
        if(ctx) ctx.drawImage(originalImg,0,0);
        return errorCanvas;
    }

    // --- Prepare Sampling Canvas ---
    // This canvas is used to sample pixel colors from the original image, scaled down to character grid size.
    const samplingCanvas = document.createElement('canvas');
    samplingCanvas.width = outputWidthChars;
    samplingCanvas.height = outputHeightChars;
    const samplingCtx = samplingCanvas.getContext('2d');
    if (!samplingCtx) return document.createElement('canvas');

    samplingCtx.drawImage(originalImg, 0, 0, samplingCanvas.width, samplingCanvas.height);
    
    let imageData;
    try {
        imageData = samplingCtx.getImageData(0, 0, samplingCanvas.width, samplingCanvas.height);
    } catch (e) {
        console.error("Could not getImageData (CORS issue if image is cross-origin and canvas is tainted):", e);
        errorCanvas.width = originalImg.width; errorCanvas.height = originalImg.height;
        const errCtx = errorCanvas.getContext('2d');
        if(errCtx) errCtx.drawImage(originalImg, 0,0); // Return original image sketch on error
        return errorCanvas;
    }
    const data = imageData.data;

    // --- Create and Prepare Final Output Canvas ---
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = outputWidthChars * charDisplayWidth;
    outputCanvas.height = outputHeightChars * charDisplayHeight;
    const outputCtx = outputCanvas.getContext('2d');
    if (!outputCtx) return document.createElement('canvas');

    // Fill background
    outputCtx.fillStyle = backgroundColor;
    outputCtx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);

    // Set text properties
    outputCtx.fillStyle = textColor;
    outputCtx.font = `${fontSize}px ${FONT_FAMILY_TO_USE}`;
    outputCtx.textBaseline = 'top'; // Simplifies y-coordinate setting for fillText

    // --- Generate Hangul Art ---
    // Loop through the pixels of the scaled-down sampling image
    for (let y = 0; y < outputHeightChars; y++) {
        for (let x = 0; x < outputWidthChars; x++) {
            const pixelIndex = (y * samplingCanvas.width + x) * 4;
            const r = data[pixelIndex];
            const g = data[pixelIndex + 1];
            const b = data[pixelIndex + 2];
            // Alpha (data[pixelIndex + 3]) is ignored for this tool.

            // Calculate grayscale value (luminance)
            let gray = 0.299 * r + 0.587 * g + 0.114 * b;

            if (invert) {
                gray = 255 - gray;
            }

            // Map grayscale value to a character in the character set
            let charIndex;
            if (chars.length === 1) {
                charIndex = 0;
            } else {
                // Map gray value [0, 255] to an index in [0, chars.length - 1]
                charIndex = Math.floor((gray / 256) * chars.length);
                // Clamp index to be safe, though Math.floor(255/256 * L) should be L-1 max
                charIndex = Math.min(chars.length - 1, Math.max(0, charIndex));
            }
            
            const selectedChar = chars[charIndex];

            if (selectedChar && selectedChar !== "") { // Draw character if it's not an empty string
                outputCtx.fillText(selectedChar, 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 Hangul Character Art Converter is a web tool that transforms images into artistic representations using Hangul characters. Users can upload an image, and customize the output by specifying the width in characters, font size, character set, and the colors for both the text and background. This tool is particularly useful for creative projects, educational purposes, or simply for generating unique visual content that combines both imagery and text in a fun and engaging way.

Leave a Reply

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