ASCII, ANSI and Unicode art explained
Character brightness ramps, why monospace is non-negotiable, how terminal colour actually works, and the platform formatting problems that break text art.
Published
Three things get called “ASCII art” and only one of them is ASCII. The distinction is not pedantry — it decides where your art will display correctly and where it will arrive as a column of question marks.
- ASCII art uses the 95 printable characters of the 1963 US-ASCII set. Displays essentially anywhere.
- ANSI art adds escape sequences that tell a terminal to change colour. The characters are still ASCII; the colour is instructions.
- Unicode art uses block, shading and box-drawing characters — ▓ ░ ─ ┌ — which give far smoother results wherever the displaying font actually contains them.
The brightness ramp
Image-to-text conversion rests on one substitution: a character’s ink
coverage stands in for a pixel’s brightness. @ fills most of its cell, .
fills very little, and a sequence ordered by coverage becomes a greyscale.
A short ramp people use constantly:
@%#*+=-:.
And a longer one, from Paul Bourke’s much-copied page, giving about seventy levels:
$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,"^`'.
Two things about ramps are not obvious.
The order is not the ASCII order, and it is not stable across fonts.
Whether & is darker than W depends on the typeface. Ramps are hand-tuned
for typical monospace faces, and a ramp that is merely “some punctuation in an
arbitrary order” produces noise rather than an image.
The ramp should end in a space. Pure white is extremely common — any
photograph with a sky or a highlight — and it must map to blank. A ramp
without a trailing space renders highlights as . and the whole image
acquires a grey haze.
The aspect-ratio trap
This is the single most common mistake, and it is invisible until someone points at it.
A monospace character cell is roughly twice as tall as it is wide. Sample one square region of the image per character and the output comes out stretched to double height — a face becomes a long oval and nobody can quite say why.
The fix is to sample regions about twice as tall as they are wide, or equivalently to compute the row count as:
rows = (imageHeight / imageWidth) × columns ÷ 2.1
The exact ratio varies by font; anything between 1.8 and 2.2 looks right.
Monospace is not a preference
Every character must occupy an identical cell. In a proportional font an i
is much narrower than an m, so each line ends at a different place and the
image collapses into diagonal mush.
This is why almost every “why does my ASCII art look broken” question resolves to the destination not using a monospace font. The practical answer is therefore not to choose a font but to use a code block, which forces one.
Even in monospace, one hazard remains: characters outside the basic Latin range are frequently not monospaced in the same font, because they come from a fallback font with different metrics. Emoji are the worst offender — most render double-width, and a single emoji shifts an entire line.
Terminal colour, and what ANSI actually is
ANSI art has no colour information in its characters. Colour comes from escape sequences: a control character followed by a code the terminal interprets rather than prints. The relevant ones are standardised in ECMA-48.
Three generations coexist:
- 16 colours — the original, universally supported.
- 256 colours — a fixed palette, near-universal today.
- 24-bit true colour —
ESC[38;2;R;G;Bm, supported by most modern terminals but not all.
Crucially, the escape sequence set includes far more than colour: sequences that move the cursor, clear the screen, and on some terminals set the window title. A file of escape sequences from an untrusted source is therefore not inert, which is a real if uncommon category of trick. A generator that emits only colour and reset codes is making a deliberate safety choice, and it is worth knowing whether the one you use does.
To view an ANSI file, cat it. Opening it in a text editor shows the escape
codes as literal garbage, because an editor prints them rather than
interpreting them.
Unicode blocks: better tone, worse portability
The Unicode Block Elements range gives four solid shading levels — █ ▓ ▒ ░ — which produce visibly smoother gradients than punctuation ever will, because each glyph really is a uniform block of a given density rather than a shape that happens to cover some proportion of its cell.
Half-blocks are the clever trick: ▀ and ▄ let one character cell hold two independently coloured pixels, doubling vertical resolution. Combined with 24-bit colour this is how modern terminal image viewers get results that look genuinely like photographs.
Box-drawing characters (─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼) are the other useful range, and are what tables and frames in terminal interfaces are built from.
The cost is portability. These characters need a font that contains them. Falling back means substituted glyphs with different widths, and the alignment is gone. If the destination is unknown, plain ASCII is the safer answer, which is why a “plain ASCII only” option is worth having.
Platform formatting problems
Markdown and GitHub. Use a fenced code block. Without one, Markdown
collapses runs of spaces and may interpret *, _ and # as formatting —
all of which appear in ramps.
Backticks inside the art. Art containing ``` closes its own fence and the remainder renders as markup. The fix is to use a fence longer than any run of backticks in the content; a generator that does not do this will eventually produce output that breaks the page it is pasted into.
Discord. Code blocks work, and messages are capped at 2000 characters, which ASCII art reaches quickly. A 100-column image of 40 lines is already 4000 characters. Reduce the width or send a file.
HTML. Use <pre>, and escape the content. This is not theoretical: the
long ramp above contains <, > and &, so unescaped output is a live
injection point the moment someone pastes it into a page.
Source-code comments. A block comment containing */ ends early and spills
the rest of the art into the file as syntax errors. Line comments (#, //)
have no such problem and are the safer choice.
Email and chat. Most clients use proportional fonts by default and will destroy the alignment regardless of what you do. Send an image instead.
When to convert to an image
There is a point at which fighting the destination is not worth it. If the art must survive a proportional font, an unknown Unicode font, a character limit or a platform that re-wraps text, render it to PNG in a monospace font and send that. You lose selectability and searchability, and you gain the guarantee that it looks the same everywhere.
Sources and further reading
- ECMA-48 — Control Functions for Coded Character Sets, the standard behind ANSI escape sequences
- The Unicode Standard — Block Elements (U+2580–U+259F) and Box Drawing (U+2500–U+257F)
- Unicode Standard Annex #11 — East Asian Width, which governs double-width characters
- The FIGfont specification — the .flf format, layout modes and smushing rules
- Paul Bourke — character representation of grey scale images
- OWASP — Cross Site Scripting Prevention Cheat Sheet, on escaping generated HTML
Last reviewed 31 July 2026.