ASCII, ANSI and Unicode art: choosing the right format
Compare plain ASCII, terminal colours and Unicode blocks. Learn how fonts, spacing and code blocks affect the way character art is displayed.
By ToolsNow · Published
Character art can use plain ASCII, terminal colour codes or a wider set of Unicode symbols. The right choice depends on where you want to paste it.
For a message or README, start with a monospace code block and check the spacing. For a terminal, ANSI colour may be useful. The ASCII art generator lets you preview both text banners and image conversions.
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 aren’t obvious.
The order isn’t the ASCII order, and it isn’t stable across fonts.
Whether & is darker than W depends on the typeface. Ramps are
hand-tuned for typical monospace faces, and a ramp that’s just “some
punctuation in an arbitrary order” gives you noise instead of an image.
The ramp should end in a space. Pure white is extremely common, since
any photograph with a sky or a highlight has plenty of it, and it needs to
map to blank. A ramp without a trailing space renders highlights as . and
the whole image picks up a grey haze.
The aspect-ratio trap
This is the single most common mistake, and it’s invisible until somebody 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 your 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.
Use a monospace font
Every character has to occupy an identical cell. In a proportional font an
i is much narrower than an m, so every line ends somewhere different
and the image collapses into diagonal mush.
That’s why almost every “why does my ASCII art look broken” question resolves to the destination not using a monospace font. Rather than trying to pick a font, 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 at all. Colour comes from escape sequences: a control character followed by a code the terminal interprets instead of printing. 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 of them.
The escape sequence set includes a great deal more than colour, though. There are 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 isn’t 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, so check 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 instead of
interpreting them.
Unicode blocks: better tone, worse portability
The Unicode Block Elements range gives you four solid shading levels, █ ▓ ▒ ░, which produce visibly smoother gradients than punctuation ever will. Each glyph genuinely is a uniform block of a given density, instead of 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 your vertical resolution. Combine that with 24-bit colour and you get the modern terminal image viewers whose output looks remarkably like photographs.
Box-drawing characters (─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼) are the other useful range, and they’re what tables and frames in terminal interfaces are built from.
The cost is portability. These characters need a font that contains them, and falling back means substituted glyphs with different widths, at which point the alignment is gone. When 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 read *, _ and # as formatting, all
of which appear in ramps.
Backticks inside the art. Art containing ``` closes its own fence, and everything after that renders as markup. The fix is a fence longer than any run of backticks in the content. A generator that doesn’t do this will eventually produce output that breaks whatever page it’s pasted into.
Discord. Code blocks work, and messages cap at 2000 characters, which ASCII art reaches fast. 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. That’s not theoretical: the
long ramp above contains <, > and &, so unescaped output is a live
injection point the moment somebody 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 default to proportional fonts and will destroy your alignment regardless of what you do. Send an image.
When to convert to an image
There’s a point where fighting the destination stops being worth it. If the art has to 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.
More help with this topic
- Word frequency and keyword density: useful counts and limits
Use word and phrase counts to spot repetition in a draft. Understand the settings behind the percentages and why there is no target density for SEO.
- Floyd–Steinberg, Atkinson and Bayer dithering compared
Compare dithering methods for photos, gradients and animation. See how error diffusion, ordered patterns and halftone dots change a limited-colour image.
- How to choose colours from an image
Compare dominant, balanced and saturated colour palettes. Learn why extractors differ and how to check sampled colours for contrast and practical use.
Published by ToolsNow. Read how tools and sources are checked.
Found a mistake or an outdated detail? Send a correction with the article title and the detail to review.