Skip to main content
ansicode
ESC (bare escape sequences)

ESC escape codes — bare two-byte sequences

Bare escape sequences. The `ESC X` family where `X` is a single byte in `0x20–0x7e` and there is no CSI / OSC framing — `ESC c` (full reset / RIS), `ESC 7` / `ESC 8` (DECSC / DECRC cursor save and restore), keypad mode toggles, character-set designations. Older than the CSI grammar and predate ECMA-48 in some cases.

8 sequences

ESC cookbook — reading the bare-escape family in six stops

Before CSI existed there was ESC X — a single byte after \x1b that meant something on its own. Most of this family predates ECMA-48 by a decade and survives because xterm honours every VT100 ancestor. Six stops cover the codes you'll actually still emit in 2026: hard reset vs. soft reset, tab stops, keypad mode, the double-width / double-height VT100 banner family, the locking-shift bridge to 8-bit C1 controls, and the APC envelope that modern protocols like Kitty graphics now ride on.

  1. 1. Hard reset vs. soft reset — \x1bc (RIS) and \x1b[!p (DECSTR)

    Two flavours of "put this terminal back where it started". \x1bc is RIS (Reset to Initial State) — the nuclear option. It clears the primary AND alternate screen, drops scrollback on every emulator that has one, resets every SGR + DEC private mode + cursor style, re-enables wraparound + auto-repeat, restores the default charset, and homes the cursor. Use it when a TUI has crashed and the user's prompt is now an unreadable lattice of colour. \x1b[!p is DECSTR (soft terminal reset) — same idea, fewer side effects. DECSTR keeps the screen contents, the scrollback, and the alt-screen pointer untouched; it only resets the mode flags, SGR, margins, cursor-shape, and Origin Mode. Reach for DECSTR inside a long-running TUI to clean up state without flashing the screen black. The companion page decstr-side-effects enumerates exactly which modes each one touches — refer to it before betting that a particular bit survives.

  2. 2. Tab stops — \x1bH (HTS) sets, \x1b[g (TBC) clears

    Default terminals come with tab stops every 8 columns — \t (\x09, HT) jumps to the next one. To override that grid, place the cursor at column N and emit \x1bH (HTS) — that column is now a tab stop until further notice. To clear: \x1b[g removes the stop at the current column, \x1b[3g removes every stop on the line. The pair gives you proper column-aligned output without manually padding spaces — useful in column(1)-style tools and in TUI status bars where the right edge needs to line up regardless of left-side content length. Pre-Linux-2.6 console honoured them but had only 8-column granularity; every modern emulator (xterm, kitty, wezterm, iTerm2, Windows Terminal, ghostty, alacritty) honours them at single-column granularity. tput hts / tput tbc are the terminfo wrappers; stty tab0 zeros every stop so your shell receives raw \t bytes.

  3. 3. Keypad mode — \x1b= / \x1b> (DECKPAM / DECKPNM)

    The numeric keypad has two personalities. Default (DECKPNM, \x1b>) is **numeric mode** — pressing 7 sends the byte 7, pressing + sends +. Send \x1b= (DECKPAM, application mode) and the same physical keys emit SS3-prefixed escapes instead: \eOq for 1, \eOr for 2, \eOM for Enter, \eOk for +. That's why vim, less, readline, tmux, and basically every full-screen TUI flips on application mode at startup (terminfo's smkx cap, rmkx to revert) — they want a unique byte signature for each keypad key so they can bind <kp-Enter> independently of <Enter>. If you exit a TUI abnormally and your + key starts producing Ok on the prompt, you're stuck in application mode — printf '\033>' (DECKPNM) puts it back. Cross-ref /keymap for the broader input-side picture.

  4. 4. Double-width / double-height lines + alignment test — \x1b#3-#6 and \x1b#8

    The VT100 banner-headline family. \x1b#3 marks the current line as the **top half** of a double-height row, \x1b#4 the **bottom half** — write the same text on both lines and the glyph spans two cell-rows at double size. \x1b#6 makes the current line **double-width** (single-height — each glyph occupies two columns), \x1b#5 restores single-width single-height (the default). Companion \x1b#8 (DECALN) fills the entire screen with a grid of E characters — the legendary VT100 alignment test pattern; modern terminals still honour it for emulation verification. Niche in 2026 application code but still used by tmux-style status banners and the boot-message ASCII art on Linux-on-VT100 setups. xterm + every fork honours all four; macOS Terminal partial; Linux console no-op; Windows Terminal ignores. Safe to emit — non-supporting emulators render single-width.

  5. 5. Locking shifts + the 8-bit C1 bridge — \x1bn / \x1bo / \x1b| and \x80-\x9F

    Before UTF-8 took over, terminals juggled four character-set slots (G0 / G1 / G2 / G3) and selected one as the active GL / GR half via locking shifts: \x1bn (LS2) makes G2 the GL half until further notice, \x1bo (LS3) selects G3, \x1b| / \x1b} / \x1b~ are the GR-half locking variants (LS3R / LS2R / LS1R). Designate a charset into a slot with \x1b( / ) / * / + followed by a final byte (B = ASCII, 0 = DEC special graphics, A = UK, etc.). The 8-bit C1 row \x80-\x9F is the historical companion: every two-byte ESC X introducer (\x1b[ CSI, \x1b] OSC, \x1bP DCS, \x1b\\ ST, \x1bN SS2, \x1bO SS3) has a single-byte 8-bit alias (\x9b, \x9d, \x90, \x9c, \x8e, \x8f). In a UTF-8 locale every byte 0x80-0xBF is a continuation byte, so emitting raw C1 codes corrupts the stream — modern emulators with LANG=C.UTF-8 mostly silently drop them. **Always emit the 7-bit \x1bX form**; the 8-bit form is read-only legacy you'll only meet in old VT manuals and CTF challenges.

  6. 6. APC — the modern application-to-application envelope \x1b_ … \x1b\\

    APC (Application Program Command) is the ECMA-48 sibling of DCS / OSC reserved for **application-to-application** messages — the terminal is supposed to ferry the body opaquely to a peer listener, not interpret it. The envelope is \x1b_<body>\x1b\\ (or \x9f<body>\x9c in 8-bit), same ST family as DCS / OSC. The original "tunnel between thin-client and host application" use case is dead, but the byte shape has been **re-colonised** by modern protocols: **Kitty's graphics protocol** is the headline user — \x1b_G<key>=<val>,…;<base64-pixels>\x1b\\ (Kitty, Konsole, WezTerm, Ghostty implement it; the slug dcs-kitty-graphics is a misnomer — the actual byte family is APC). **VS Code shell integration** uses \x1b_633;…\x1b\\ for markers it doesn't want to expose via OSC 133. **Windows Terminal** is experimenting with \x1b_<JSON>\x1b\\. Treat APC as "will reach the peer that knows the first-byte tag, or vanish completely" — never as a guaranteed-visible channel. Parser bug to watch for: terminals that implement OSC + DCS but skip APC dispatch will **emit _G… to the screen** instead of consuming it — that stray _G literal is the signature of broken Kitty graphics renders.

All sequences in this family

Browse other families