712Tools
4 min read

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.

By
Software Engineer ยท M.Sc. Mechanical Engineering ยท Ontario, Canada

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:

  1. Brand recognition. A distinctive banner is memorable in a way a plain heading isn't.
  2. A moment of anticipation. During startup or install, the banner shows the tool is doing something.
  3. 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:

  1. Test at 60 columns. If it fits there, it fits anywhere. Resize your terminal to stty cols 60 to preview.
  2. Break long names. MY-COMPANY-NAME as one banner is too wide; consider MY-CO\nCOMPANY\nNAME on 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 ^[[31m garbage.
  • Respect NO_COLOR โ€” the env var users set to disable all color output. Check process.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.isTTY in Node, sys.stdout.isatty() in Python.
  • How wide is the window? process.stdout.columns or shutil.get_terminal_size().
  • Are we in CI? process.env.CI is 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:

  1. Type the name (short, uppercase) into ASCII Art Generator.
  2. Try all three fonts โ€” one usually clicks visually.
  3. Copy the output and paste it inside a \``` fence in the README.
  4. For CLI startup, wrap in a TTY + NO_COLOR check.
  5. 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

Written by Shan

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