Please bookmark this page to avoid losing your image tool!

Image To Scalable Kaomoji Converter With Decorative Symbols

(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,
    charCols = "150",
    colorized = "1",
    invertBrightness = "0",
    bgColor = "#ffffff",
    fontColor = "#000000",
    charString = "  ⠄·・゜*゚▽ヮω◇д☆※❀✿♡★●♥■▓█"
) {
    const cols = parseInt(charCols);
    const useColor = parseInt(colorized) !== 0;
    const invert = parseInt(invertBrightness) !== 0;
    const symbols = Array.from(charString);
    
    const fontSize = 14;
    const fontFamily = "monospace";
    const font = `bold ${fontSize}px ${fontFamily}`;
    
    // Measure character dimensions dynamically to maintain aspect ratio and grid alignment
    // We measure both a standard ASCII char and a double-width Japanese/decorative symbol 
    // to ensure the grid cell can encompass the maximum width perfectly.
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d');
    tempCtx.font = font;
    
    const cw1 = tempCtx.measureText("M").width;
    const cw2 = tempCtx.measureText("✿").width;
    const cellW = Math.max(cw1, cw2);
    const cellH = fontSize * 1.25; // standard line-height approximation
    
    const fontAspectCorrection = cellW / cellH;
    const rows = Math.max(1, Math.floor((originalImg.height / originalImg.width) * cols * fontAspectCorrection));
    
    // Downsample the image to determine character mapping limits
    const imgCanvas = document.createElement('canvas');
    imgCanvas.width = cols;
    imgCanvas.height = rows;
    const imgCtx = imgCanvas.getContext('2d');
    
    // Draw image onto smaller canvas to get pixel luminance and color data
    imgCtx.drawImage(originalImg, 0, 0, cols, rows);
    const imgData = imgCtx.getImageData(0, 0, cols, rows).data;
    
    // Prepare offscreen canvas holding the actual cached text art
    const offscreen = document.createElement('canvas');
    offscreen.width = cols * cellW;
    offscreen.height = rows * cellH;
    const offCtx = offscreen.getContext('2d');
    
    offCtx.fillStyle = bgColor;
    offCtx.fillRect(0, 0, offscreen.width, offscreen.height);
    
    offCtx.font = font;
    offCtx.textAlign = "center";
    offCtx.textBaseline = "middle";
    
    for (let y = 0; y < rows; y++) {
        for (let x = 0; x < cols; x++) {
            const idx = (y * cols + x) * 4;
            const r = imgData[idx];
            const g = imgData[idx + 1];
            const b = imgData[idx + 2];
            const a = imgData[idx + 3];
            
            // Interpret fully transparent pixels as maximum luminance (acts like background mapping)
            let luminance = 0.299 * r + 0.587 * g + 0.114 * b;
            if (a < 128) luminance = 255; 
            
            let normalized = luminance / 255;
            if (invert) {
                normalized = 1 - normalized;
            }
            
            // Map luminance to character index (0 = lightest/space, length-1 = darkest/densest element)
            let charIdx = Math.floor((1 - normalized) * (symbols.length - 1));
            charIdx = Math.max(0, Math.min(symbols.length - 1, charIdx));
            
            const char = symbols[charIdx];
            if (char === ' ') continue; // Optimization: skip spaces
            
            if (useColor) {
                offCtx.fillStyle = `rgb(${r},${g},${b})`;
            } else {
                offCtx.fillStyle = fontColor;
            }
            
            // Render perfectly at the center of the assigned grid cell
            offCtx.fillText(char, x * cellW + cellW / 2, y * cellH + cellH / 2);
        }
    }
    
    // Create interactive viewport canvas returned to the caller
    const canvas = document.createElement('canvas');
    canvas.width = 1200; // Default logical dimensions (responsive to CSS)
    canvas.height = 800;
    
    canvas.style.width = "100%";
    canvas.style.height = "100%";
    canvas.style.minHeight = "400px";
    canvas.style.backgroundColor = bgColor;
    canvas.style.cursor = "grab";
    canvas.style.display = "block";
    canvas.style.touchAction = "none"; // Prevents entire page from scrolling when zooming and panning
    canvas.style.boxShadow = "inset 0px 0px 10px rgba(0,0,0,0.1)";
    
    const ctx = canvas.getContext('2d');
    
    let scale = 1;
    // Fit completely in view initially
    if (offscreen.width > canvas.width || offscreen.height > canvas.height) {
        scale = Math.min(canvas.width / offscreen.width, canvas.height / offscreen.height) * 0.95;
    }
    
    let offsetX = (canvas.width - offscreen.width * scale) / 2;
    let offsetY = (canvas.height - offscreen.height * scale) / 2;
    
    const redraw = () => {
        ctx.fillStyle = bgColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(offscreen, offsetX, offsetY, offscreen.width * scale, offscreen.height * scale);
    };
    
    redraw();
    
    // Helper to translate responsive CSS space into true internal canvas bounds
    function getCanvasPos(e) {
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        return {
            x: e.clientX * scaleX,
            y: e.clientY * scaleY
        };
    }
    
    // Implements the Scalable Wheel Scroll zooming 
    canvas.addEventListener('wheel', (e) => {
        e.preventDefault();
        
        const pos = getCanvasPos(e);
        const zoomAmount = 0.15; 
        const zoomFactor = e.deltaY < 0 ? (1 + zoomAmount) : (1 - zoomAmount);
        const newScale = scale * zoomFactor;
        
        // Boundaries to prevent zooming into the pixel atom or out excessively
        if (newScale < 0.05 || newScale > 200) return;
        
        // Math to zoom exactly towards the mouse position
        offsetX = pos.x - (pos.x - offsetX) * zoomFactor;
        offsetY = pos.y - (pos.y - offsetY) * zoomFactor;
        scale = newScale;
        
        redraw();
    }, { passive: false });
    
    // Implements Drag to Pan Interaction
    let isDragging = false;
    let startMouseX = 0;
    let startMouseY = 0;
    
    canvas.addEventListener('mousedown', (e) => {
        isDragging = true;
        canvas.style.cursor = "grabbing";
        const pos = getCanvasPos(e);
        startMouseX = pos.x - offsetX;
        startMouseY = pos.y - offsetY;
    });
    
    canvas.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        e.preventDefault();
        const pos = getCanvasPos(e);
        offsetX = pos.x - startMouseX;
        offsetY = pos.y - startMouseY;
        redraw();
    });
    
    const stopDrag = () => {
        isDragging = false;
        canvas.style.cursor = "grab";
    };
    
    canvas.addEventListener('mouseup', stopDrag);
    canvas.addEventListener('mouseleave', stopDrag);
    
    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

This tool converts images into artistic kaomoji and decorative symbol art. By mapping the luminance and color of an uploaded image to a custom string of symbols (such as stars, hearts, and Japanese characters), it creates a unique stylized representation of your photo. Users can customize the output by adjusting the number of columns, toggling colorization, inverting brightness, and selecting custom background or font colors. The tool features an interactive, scalable viewer that allows you to zoom in and out or pan across your creation to inspect the fine details of the symbol-based artwork. It is ideal for creating unique social media avatars, digital art, or stylized aesthetic graphics.

Leave a Reply

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

Other Image Tools:

Kingdom Hearts SVTFOE Gameplay Image Viewer

Kingdom Hearts SVTFOE Gameplay Video Player

Image To Video Content Description Tool

Big Hero 6 2014 Tubi TV June 30 2027 Video Screenshot

No tool description provided

Big Hero 6 2014 Tubi Jun 30 2027 Photo

San Diego Comic-Con Image Gallery

Movie Studio Intro YTP Collab Style Image Maker

Movie Studio Music Fanfare Logo Maker

Movie Studio Music Fanfare Logo Generator

Kurdish Dubbing Audio to Image Visualizer Tool

Kurdish Dubbed Audio Video Tool

Kurdish Dub Audio to Image Converter

Kurdish Dub Audio Overlay Tool

Warner Bros Discovery Animation Studio Divisions Image Viewer

Image To Character Voice Actor Idea Generator

Image To Character Voice Actor Suggestion Tool

Image Color Adjustment Tool

Dingbats Logo Compilation Image Generator

Image Color and Opacity Adjustment Tool

The Lion King VHS Mar 3 1995 Image

The Lion King Hamtaro Character Cast Reimaginer

Audio Transcription and Identification Tool

The Lion King Hamtaro Character Role Swap Image Generator

Audio to Image Fanfare Visualizer Tool

Audio Clip of Universal Pictures Fanfares

Audio File to Image Converter

Universal Pictures Fanfare Audio Identifier

Universal Pictures Fanfare Audio Identification Tool

Universal Pictures Fanfare Audio Comparison Tool

Universal Pictures Fanfare Audio Search Tool

Universal Pictures Fanfare Audio Player

Universal Pictures David Newman Fanfare Audio Player

Universal Pictures Mar 15 2002 David Newman Fanfare Audio Player

AI Movie Trailer Generator

Anna Pavlova Experiment Photo Viewer

See All →