ASCII art in READMEs, CLIs, and terminals: a practical guide
Every popular open-source project has an ASCII banner in its README or CLI startup. Here's how to make one that looks right, keeps its shape in Markdown, and doesn't break in narrow terminals.
Why ASCII banners still work
Every serious CLI ships one โ docker, npm, aws, gcloud, bun, deno, ollama. Every popular open-source README has one at the top. They're doing three things at once:
- Brand recognition. A distinctive banner is memorable in a way a plain heading isn't.
- A moment of anticipation. During startup or install, the banner shows the tool is doing something.
- A cheap dopamine hit. Programmers reading a README have seen a thousand plain "# Project Name" headings. A banner stands out.
They cost nothing at runtime, and they've been part of the developer aesthetic since Figlet in 1991.
The three font families
For any banner text, you're picking from three visual styles:
Block letters. Solid, chunky, high visual impact. Best for short names (3-8 characters).
Banner style. Wider, sparser, easier to fit long names. Trades density for readability.
Shadow / 3D. Adds a drop-shadow row of dimmer characters. Reads more polished but takes twice the vertical space.
ASCII Art Generator has all three built-in โ type your name, pick a font, copy.
The Markdown gotcha
The single most common mistake: pasting the banner into a README without a code fence. Markdown collapses runs of spaces into single spaces, breaking the alignment:
# WRONG โ this renders as garbage:
โโโโโ โโโโโ โโโโโ
โ โ โ
# RIGHT โ inside a fenced code block:
\`\`\`
โโโโโ โโโโโ โโโโโ
โ โ โ
\`\`\`
Same rule for GFM tables and blockquotes โ anywhere Markdown reformats whitespace, wrap the banner in a code fence.
If you're publishing to a static site, Markdown to HTML previews the exact rendered output so you can catch alignment loss before deploying.
Where to put the banner
READMEs: at the very top, above the tagline. Center it with an HTML <pre align="center"> if your renderer supports it.
CLI startup: the first line of stdout on a successful boot. Skip when --quiet or when stdout isn't a TTY (piped, redirected).
if (process.stdout.isTTY && !process.argv.includes("--quiet")) {
console.log(banner);
}
Terminal MOTDs: /etc/motd on Linux servers. First thing anyone sshing in sees. Keep it short โ big banners annoy people who ssh 50 times a day.
Comment banners: at the top of huge modules ("BILLING ENGINE โ DO NOT TOUCH WITHOUT PAGING ALEX"). Contentious style choice, but legitimate for anchor points in massive files.
The width constraint
Most terminal windows are 80 characters wide. Older tools, tmux status bars, and CI logs sometimes cap at 60-72. Two rules:
- Test at 60 columns. If it fits there, it fits anywhere. Resize your terminal to
stty cols 60to preview. - Break long names.
MY-COMPANY-NAMEas one banner is too wide; considerMY-CO\nCOMPANY\nNAMEon separate lines, or ship a shorter mark.
Modern terminals wrap gracefully, but wrapped ASCII art looks catastrophic. Design for the narrowest.
Colored banners
Adding ANSI color codes:
const RED = "\x1b[31m";
const RESET = "\x1b[0m";
console.log(RED + banner + RESET);
Rules:
- Never color banners printed to a non-TTY (log files, CI systems). ANSI codes appear as
^[[31mgarbage. - Respect
NO_COLORโ the env var users set to disable all color output. Checkprocess.env.NO_COLOR. - Prefer 4-bit colors (31-37) for widest terminal support. 24-bit RGB (
\x1b[38;2;R;G;Bm) works in modern terminals but breaks in old ones.
Libraries like chalk (Node), click.style (Python), and termcolor (Rust) handle NO_COLOR and TTY detection for you.
Detecting the terminal
You'll want to know:
- Is stdout a TTY?
process.stdout.isTTYin Node,sys.stdout.isatty()in Python. - How wide is the window?
process.stdout.columnsorshutil.get_terminal_size(). - Are we in CI?
process.env.CIis set by GitHub Actions, GitLab CI, CircleCI, and most others.
Combine: show the banner only when TTY + not CI + width โฅ banner_width.
When to skip the banner
Two situations:
Machine-consumed output. myctl get pods -o json โ the caller wants JSON, not a banner. Silence when stdout is piped or when --json, -o, or --quiet is used.
High-frequency invocation. A tool called from a shell hook or a git alias should never print a banner โ it's called 100x a day and the banner becomes noise.
The one-minute recipe
For any new CLI or README:
- Type the name (short, uppercase) into ASCII Art Generator.
- Try all three fonts โ one usually clicks visually.
- Copy the output and paste it inside a
\``` fence in the README. - For CLI startup, wrap in a TTY +
NO_COLORcheck. - Preview at 60 columns in a resized terminal.
Total time: 3 minutes. The result: your project now has the same production polish as the tools you install every week.
Related workflows
- Markdown to HTML โ for previewing the README render.
- HTML Beautifier โ if you're inlining the banner in an HTML
<pre>. - Slugify โ for the project slug that appears next to the banner.
Tools mentioned in this post
Related reading
User-Agent strings in 2026: parsing, Client Hints, and privacy
The User-Agent string is a mess โ Chrome pretends to be Safari, Edge pretends to be Chrome. But it's still what your analytics reads. Here's how to parse it, when to trust it, and where Client Hints are taking us.
SEO meta tags that actually matter in 2026 (and the ones you can skip)
Half the meta tags in every 'ultimate SEO checklist' were obsolete a decade ago. Here's the short list that Google, social platforms, and AI crawlers actually read.
Markdown flavors explained: CommonMark, GFM, MDX, and why your table doesn't render
Markdown looks universal until you paste a GitHub table into a Discord post. Here's what each flavor supports, and how to pick one for your project.
Shan builds 712 Tools. He holds a Master's degree in Mechanical Engineering and now works as a Software Engineer, shipping browser-based developer utilities out of Ontario, Canada. Learn more ยท 712studiogames@gmail.com