About
Ansi.md is my brain dump for creating modern command line apps. My intent is to gradually expand this guide with hard-won tricks, recommended apps and homegrown tools. My name is Adam Doppelt, feedback welcome at [email protected].
AI statement: I like my writing voice and I do not use LLMs to write.
Why
I don't claim to be an expert on command line apps, but I've written a few popular tools (like tennis) and it's not easy. I've also built literally hundreds of internal cli apps for various projects and I'm inordinately obsessed with color, progress bars, and spinners.
Why is this stuff so complicated? In order to answer this question, you have to dig into the history of the terminals and text output. Your little cli app runs inside something like Apple Terminal, Ghostty, iTerm, Alacritty, or even good ol' xterm. If you are using a web-based terminal like the thing inside Google Cloud, you are running a javascript terminal.
Terminals have a shared lineage dating back to the classic VT100 from 50 years ago. My dorm room actually had one of these. Your lovingly crafted app emits bytes like "hello world" and the terminal displays them.
Vintage hardware terminals were relatively straightforward, displaying text on screen and looking for \n to advance to the next line. Similar to typewriters of that era. In the pre-internet days we used modems to access our apps and data remotely using new kinds of software terminals. But modern terminals are capable of more, so much more. Too much, probably.
We've got truecolor RGB. We've got themes that are "light" and "dark". We've got mouse control, layout, and hints for double buffering. We've got window titles. We've got strange graphics protocols, built-in tiny progress bars, and hacks to make the icon bounce in the Dock.
Modern terminals carefully parse the stream of bytes printed from your app in order to support these features. Terminals also set environment variables to give hints to your app about what's supported. Tools like ssh or tmux have to understand these environment variables too, otherwise apps running over ssh have no idea what's supported. There are huge databases that try to capture which features are supported by which terminals. Brave apps can also query the terminal directly using obscure invisible codes.
I'll be honest with you, this system is a beautiful mess and probably should be burned in a fire. The complexity is made worse due to the various strata in the system - it's quite easy to encounter docs painstakingly written for older terminals, but in many cases you won't need that stuff. Software evolves quickly, and devs are eager to move on to modern terminals.
In the meantime, cli and library authors need to understand the ecosystem if they want to create nice cli apps. That's why I created ansi.md, to collect the latest and greatest ansi knowledge in one place.
Pages
- Homeyou are here
- ENV & CapabilitiesOMG why is this so hard
- Color Depthall about color palettes
- CLI Best Practicesthe difference between meh and awesome cli
- Progress Bars & Spinnersthe fun stuff
- The TUIpretty apps
- ANSI Escape Codesjust the basics, ha
- Advanced ANSI Stuffdon't read this
- Recommended Apps & Libsstuff I personally like
Tools
- Nearest Colornearest ANSI 256 and Tailwind matches
- Giant Contrast Tablesee lots of colors on light and dark
Palettes
- ANSI 256 Color Cubeansi 256 names-as-code
- Catppuccin ColorsCatppuccin-as-code
- D3 Ordinal Scalesd3 scales-as-code
- Tailwind Colorstailwind colors-as-code
Special Thanks
- design inspo from Fil's lovely Fil-C doc site
- nak for reading early (bad) drafts
- site built with astro, thanks guys
Changelog
Aug 2026 - first public release