Escape Codes and the Search for Standards

ANSI escape codes are how terminal emulators and the programs running inside them exchange information that doesn't fit into normal text. They handle everything from cursor movement and text color to mouse reporting, clipboard access, and bracketed paste.

These codes have a reputation for being unreliable and hard to debug. Part of the reason is that escape codes are invisible — when they fail, you see nothing, or worse, you see stray text like ^[[D when you press the left arrow key. The other reason is that they aren't fully standardized.

What an Escape Code Is

Escape codes begin with the escape character, written variously as ESC, \x1b, \E, \033, or ^[. There are two categories:

  • Input codes — sent by the terminal emulator for keypresses and mouse events that don't map to Unicode. The left arrow key is ESC[D; Ctrl+left arrow might be ESC[1;5D; mouse clicks can be something like ESC[M :3.
  • Output codes — printed by programs to color text, move the cursor, clear the screen, enable mouse reporting, set the window title, or copy text to the system clipboard (a capability known as OSC 52).

ECMA-48: The Baseline Standard

The foundational standard for escape codes is ECMA-48, first published in 1976. It does two things:

  1. Defines general formats for escape sequences — CSI codes (ESC[ plus content) and OSC codes (ESC] plus content).
  2. Defines specific codes, such as "cursor left" (ESC[D) and SELECT GRAPHIC RENDITION for setting text attributes like color (ESC[31m for red).

The formats are extensible, leaving room for other parties to define new codes. Many codes in common use today, such as those for mouse input, are not defined in ECMA-48.

xterm: The De Facto Reference

Beyond ECMA-48, many widely used escape codes come from xterm and are documented in XTerm Control Sequences. This document covers mouse reporting, bracketed paste, OSC 52 for clipboard access, and more. It is not a formal standard, but xterm's influence has made it an important reference that other terminal emulators largely follow.

terminfo: Solving the Compatibility Problem

In the 1980s, terminal emulators varied enormously in which escape codes they supported. The terminfo database was created to manage this variance. Its standard is X/Open Curses, which defines the database format and a C library interface ("curses") for accessing it.

The database contains the escape codes for every terminal type the system knows. On most modern systems, terminfo is managed by ncurses. A program can query the database based on the TERM environment variable to find the correct codes for the current terminal emulator.

Two Approaches: terminfo vs. Hardcoded Common Codes

Terminal applications generally take one of two approaches to escape codes:

  1. Use the terminfo database to generate the correct codes based on TERM — for example, fish does this.
  2. Hardcode a "single common set" of escape codes believed to work across enough terminal emulators.

Several well-known projects have moved toward approach #2, including kakoune, python-prompt-toolkit, linenoise, libvaxis, and chalk. One fish maintainer has written an extended argument against terminfo, acknowledging that the terminfo authors did important work that "no longer is" relevant.

The Case for terminfo

Despite the trend away from terminfo, it still has value in 2025:

  • The TERM variable is a convention that lets users control program behavior, as with TERM=dumb. There is no standard replacing it.
  • Terminal variation still exists: graphical terminals, the Linux framebuffer console, serial console sessions, and Emacs shell mode all behave differently.
  • There is currently no agreed-upon definition of the "single common set." Some programs employ codes that aren't widely supported.

The User Agent Analogy

Using TERM to select escape codes resembles how web servers used browser user agents to serve different content. Terminals have responded similarly to browsers: iTerm2 identifies itself as xterm-256color, just as Safari's user agent claims to be Mozilla-compatible.

On the web, the industry moved away from user agent detection toward standardization — serving the same HTML/CSS to every browser. Whether an equivalent shift happens for the terminal is unclear. The terminal ecosystem is more fragmented than the web's, and less well funded.

Additional Documents and Standards

Several other documents touch on escape codes without being formal standards:

The terminal is sometimes dismissed as outdated, but incremental standards work could change that. The web went through a similarly chaotic period before HTML and CSS conventions became reliable enough for cross-browser development. ECMA-48 has been around for almost five decades, and the landscape is still fragmented. Whether that changes depends on whether the community can coordinate more than it has so far.