ASCII Art Generator: From Character Mapping to Terminal Art
ASCII Art Generator: From Character Mapping to Terminal Art#
You’ve seen those big banner texts in terminals—welcome messages when SSH-ing into servers, npm package logos, CLI tool headers. That’s ASCII Art. Let me walk you through building one.
The Core: Character Mapping Table#
ASCII Art is essentially simulating pixels with monospace characters. Each character is defined as a 5×7 or 5×5 matrix where each position is either a solid block (█) or empty space.
const ASCII_FONTS = {
standard: {
'A': [
' █████ ', // Row 1
'██ ██', // Row 2
'███████', // Row 3
'██ ██', // Row 4
'██ ██' // Row 5
],
'B': [
'██████ ',
'██ ██',
'██████ ',
'██ ██',
'██████ '
],
// ... other character definitions
}
}
Key design decisions:
- Row-based storage: Each row is a string for easy horizontal concatenation
- Uniform height: All characters are 5 rows tall for baseline alignment
- Monospace rendering: Uses full-width
█and spaces for consistent display
Rendering Algorithm: Horizontal Concatenation#
The generation process horizontally concatenates each character’s “pixel matrix”:
function generateAsciiArt(text: string, font: string): string {
const fontData = ASCII_FONTS[font]
const upperText = text.toUpperCase()
const lines: string[] = ['', '', '', '', ''] // 5 empty rows
for (const char of upperText) {
const charArt = fontData[char]
if (charArt) {
// Append each row of current character to corresponding line
charArt.forEach((line, i) => {
lines[i] += line + ' ' // Add space between characters
})
} else {
// Fill unsupported characters with blank
lines.forEach((_, i) => {
lines[i] += ' '
})
}
}
return lines.join('\n')
}
Example with input "AB":
Initial state:
lines = ['', '', '', '', '']
Processing 'A':
lines = [' █████ ', '██ ██', '███████', '██ ██', '██ ██']
Processing 'B':
lines = [' █████ ██████ ', '██ ████ ██', '████████████ ', '██ ████ ██', '██ ████████ ']
Final output:
█████ ██████
██ ████ ██
████████████
██ ████ ██
██ ████████
Font Design: Pixel to Character Translation#
Designing an ASCII font means “translating” pixel art into character grids. Two common approaches:
1. Manual Drawing#
Draw each character’s 5×5 matrix in a monospace editor:
Designing 'A':
1. Draw pixel map (. = empty, # = solid):
###
# #
#####
# #
# #
2. Convert to characters:
███
█ █
█████
█ █
█ █
3. Store in data structure:
'A': [' ███ ', '█ █', '█████', '█ █', '█ █']
2. Automatic Conversion from Images#
A more advanced approach reads vector fonts or images, then downsamples to 5×5 grids:
function imageToAscii(imageData: ImageData, width: number, height: number): string[] {
const lines: string[] = []
const cellWidth = width / 5
const cellHeight = height / 5
for (let y = 0; y < 5; y++) {
let line = ''
for (let x = 0; x < 5; x++) {
// Calculate average brightness in current cell
const brightness = getAverageBrightness(
imageData,
Math.floor(x * cellWidth),
Math.floor(y * cellHeight),
Math.floor(cellWidth),
Math.floor(cellHeight)
)
line += brightness > 128 ? '█' : ' '
}
lines.push(line)
}
return lines
}
Multi-Font Support and Extensibility#
Real projects often need multiple font styles. Here’s an extensible font system:
// Font interface definition
interface AsciiFont {
name: string
height: number // Font height in rows
charset: Record<string, string[]>
}
// Font registry
const FONT_REGISTRY: Record<string, AsciiFont> = {
standard: {
name: 'Standard',
height: 5,
charset: { /* A-Z, 0-9 */ }
},
block: {
name: 'Block',
height: 5,
charset: { /* Different style */ }
},
banner: {
name: 'Banner',
height: 7, // Taller font
charset: { /* Large font */ }
}
}
// Render function supports dynamic height
function render(text: string, fontName: string): string {
const font = FONT_REGISTRY[fontName]
const lines: string[] = Array(font.height).fill('')
for (const char of text.toUpperCase()) {
const charArt = font.charset[char]
if (charArt) {
charArt.forEach((line, i) => {
lines[i] += line + ' '
})
}
}
return lines.join('\n')
}
Performance: Handling Large Text#
Rendering can slow down with long inputs (100+ characters). Optimization strategies:
1. Result Caching#
const cache = new Map<string, string>()
function generateWithCache(text: string, font: string): string {
const key = `${font}:${text}`
if (cache.has(key)) {
return cache.get(key)!
}
const result = generateAsciiArt(text, font)
cache.set(key, result)
return result
}
2. Virtual Rendering#
For very long outputs, only render the visible area:
function VirtualAsciiOutput({ text, font, viewportHeight }: Props) {
const fullOutput = useMemo(() => generateAsciiArt(text, font), [text, font])
const lines = fullOutput.split('\n')
// Only render visible rows
const [startLine, setStartLine] = useState(0)
const visibleLines = lines.slice(startLine, startLine + viewportHeight)
return (
<div onScroll={handleScroll}>
{visibleLines.map((line, i) => (
<pre key={i}>{line}</pre>
))}
</div>
)
}
Practical Applications#
1. CLI Tool Banners#
// Print logo when CLI tool starts
console.log(`
██████╗ ███████╗ ██████╗ ██████╗ ███████╗
██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝
██║ ███╗█████╗ ██║ ██║██║ ██║█████╗
██║ ██║██╔══╝ ██║ ██║██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝
`)
2. README Headers#
Those big text titles on GitHub project pages—one-click generation with ASCII Art.
3. Code Comments#
/*
* ██████╗ ██████╗ ██████╗ ███████╗
* ██╔════╝██╔═══██╗██╔══██╗██╔════╝
* ██║ ██║ ██║██║ ██║█████╗
* ██║ ██║ ██║██║ ██║██╔══╝
* ╚██████╗╚██████╔╝██████╔╝███████╗
* ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
* Module: Core Algorithm
*/
Edge Cases#
1. Unsupported Characters#
Lowercase letters, special symbols? Convert or show blank:
function renderChar(char: string, font: AsciiFont): string[] {
const upper = char.toUpperCase()
return font.charset[upper] || Array(font.height).fill(' ')
}
2. Monospace Font Dependency#
ASCII Art displays correctly only with monospace fonts. In non-monospace environments (some web pages), set <pre> tag to font-family: monospace.
3. Wide Character Issues#
Chinese characters occupy two character widths, causing alignment issues in ASCII Art. Recommend supporting only ASCII character set, or handle specially during rendering.
The Result#
Based on these ideas, I built: ASCII Art Generator
Features:
- Supports A-Z, 0-9 characters
- Two font styles (Standard / Block)
- Real-time preview, one-click copy
- 20 character limit (prevents performance issues)
The code isn’t complex, but getting character mapping, multi-font support, and edge cases right makes a usable tool.
Related: Image to ASCII | Code Share