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. Hard reset vs. soft reset —
\x1bc(RIS) and\x1b[!p(DECSTR)Two flavours of "put this terminal back where it started".
\x1bcis 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[!pis 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 pagedecstr-side-effectsenumerates exactly which modes each one touches — refer to it before betting that a particular bit survives.2. Tab stops —
\x1bH(HTS) sets,\x1b[g(TBC) clearsDefault 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[gremoves the stop at the current column,\x1b[3gremoves every stop on the line. The pair gives you proper column-aligned output without manually padding spaces — useful incolumn(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 tbcare the terminfo wrappers;stty tab0zeros every stop so your shell receives raw\tbytes.3. Keypad mode —
\x1b=/\x1b>(DECKPAM / DECKPNM)The numeric keypad has two personalities. Default (DECKPNM,
\x1b>) is **numeric mode** — pressing7sends the byte7, pressing+sends+. Send\x1b=(DECKPAM, application mode) and the same physical keys emit SS3-prefixed escapes instead:\eOqfor1,\eOrfor2,\eOMfor Enter,\eOkfor+. That's whyvim,less,readline,tmux, and basically every full-screen TUI flips on application mode at startup (terminfo'ssmkxcap,rmkxto 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 producingOkon the prompt, you're stuck in application mode —printf '\033>'(DECKPNM) puts it back. Cross-ref/keymapfor the broader input-side picture.5. Locking shifts + the 8-bit C1 bridge —
\x1bn/\x1bo/\x1b|and\x80-\x9FBefore 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-\x9Fis the historical companion: every two-byteESC Xintroducer (\x1b[CSI,\x1b]OSC,\x1bPDCS,\x1b\\ST,\x1bNSS2,\x1bOSS3) 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 withLANG=C.UTF-8mostly silently drop them. **Always emit the 7-bit\x1bXform**; the 8-bit form is read-only legacy you'll only meet in old VT manuals and CTF challenges.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>\x9cin 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 slugdcs-kitty-graphicsis 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_Gliteral is the signature of broken Kitty graphics renders.
All sequences in this family
- RIS — Reset to Initial State (full terminal reset)
\x1bcHard reset the terminal: clear screen + scrollback, reset every mode and SGR, home the cursor.
- HTS — Horizontal Tab Set (ESC H)
\x1bHSet a tab stop at the current cursor column — partner to TBC.
- C1 controls — 8-bit single-byte equivalents of ESC sequences (0x80–0x9F)
\x9b CSI \x9d OSC \x9c ST \x90 DCS \x85 NEL \x88 HTS \x84 IND \x8d RIThe 8-bit C1 control bytes (0x80..0x9F) — CSI, OSC, ST, DCS, NEL, HTS, IND, RI — and their 7-bit ESC <letter> equivalents.
- DECKPAM / DECKPNM — Keypad application / numeric mode (ESC = / ESC >)
\x1b= (DECKPAM) \x1b> (DECKPNM)Switch the numeric keypad between sending application escape sequences (\eOM / \eOj …) and plain ASCII digits — the foundation of vim / less / readline keypad behaviour.
- DECDHL / DECDWL / DECSWL — Double-height / double-width lines (ESC # 3 / # 4 / # 5 / # 6)
\x1b#3 (DHL top) \x1b#4 (DHL bottom) \x1b#5 (DECSWL) \x1b#6 (DECDWL)Mark the current line as double-height (top or bottom half) or double-width / single-height — DEC's per-line big-banner primitives, used by VT100 splash screens.
- DECALN — Screen alignment pattern (ESC # 8)
\x1b#8Fill the entire screen with capital `E` glyphs — DEC's CRT alignment test, the canonical 'is the page buffer wired up right' smoke test.
- Single Shift / Locking Shift family — SS2 / SS3 / LS2 / LS3 / LS1R / LS2R / LS3R
\x1bN SS2 \x1bO SS3 \x1bn LS2 \x1bo LS3 \x1b~ LS1R \x1b} LS2R \x1b| LS3RISO 2022 character-set switching: invoke G2 / G3 once (SS2 / SS3), or lock GL / GR to a different G-set (LS2 / LS3 / LS1R / LS2R / LS3R). Pairs with `\x1b(` / `\x1b)` / `\x1b*` / `\x1b+` G-set designators.
- APC — Application Program Command (`ESC _ … ESC \\` / `\x9F … \x9C`)
\x1b_<body>\x1b\\ (7-bit) \x9f<body>\x9c (8-bit C1)The string introducer ECMA-48 reserves for application-to-application messages. Modern use: Kitty graphics protocol, VS Code shell integration, Windows Terminal API patches. Sibling to DCS / OSC / PM / SOS.