SGR 38;5;n — 256-color foreground
Pick a foreground color from the 256-color xterm palette.
Byte forms
Every common string-literal form so you can paste-and-search either direction.
\x1b[38;5;Nm\033[38;5;Nm\e[38;5;NmESC [ 3 8 ; 5 ; N m1b 5b 33 38 3b 35 3b <N> 6dLive preview
Rendered in your browser via the same tokeniser the decoder uses — no terminal needed.
Description
SGR 38;5;N picks a foreground colour from the xterm 256-colour palette. N is 0–255: 0–7 map to the 8 basic colours, 8–15 to their bright variants, 16–231 form a 6×6×6 RGB cube indexed by 16 + 36*r + 6*g + b where r, g, b each take 0–5, and 232–255 form a 24-step grayscale ramp from near-black to near-white. The background equivalent is \x1b[48;5;Nm. Spec lineage: the parameter shape comes from xterm-ctlseqs (not ECMA-48 directly — ECMA-48 reserves 38 + 48 for 'future use' and lets implementations pick the sub-parameter grammar). Most parsers accept either the semicolon form \x1b[38;5;Nm (xterm legacy) or the ECMA-48-compliant colon form \x1b[38:5:Nm — emit semicolons by default; colons are safer only when you know the consumer (some libraries) handles them.
Portability is the whole story for 256-colour. The Linux console / fbcon ignores \x1b[38;5;Nm entirely (the palette is fixed at the 16 basic + bright entries) — every 256-indexed colour silently quantizes to the nearest of those 16. Apple's /usr/bin/screen (still v4.00.03 from 2006 as of macOS Sequoia) and Ubuntu 22.04 / Debian Bookworm's screen 4.09 also drop unknown 256-colour bytes; the multiplexer interprets the sequence before forwarding, and old screen versions never learned the form. Windows conhost.exe (legacy cmd host, not the modern Windows Terminal) quantizes to the 16 console-palette slots — ConPTY on 1809+ passes the bytes through unchanged, but the renderer above quantizes. Inside tmux, \x1b[38;5;Nm survives the multiplexer (it's only truecolor 38;2;… that needs the Tc/RGB terminfo cap). The portable feature-detect is $TERM containing 256color plus tput colors ≥ 256 — don't trust $COLORTERM for 256-colour (it's the truecolor flag).
Disable foreground with \x1b[39m — restores terminal default without touching the background or other attributes. The surgical close pair is \x1b[38;5;Nm…\x1b[39m; reaching for \x1b[0m clobbers any bold / italic / bg you also set. Don't forget to reset before newlines so colour doesn't bleed into the user's prompt (see color-bleed). For arbitrary RGB control beyond the 256-palette, see sgr-fg-truecolor (24-bit, no quantization on modern terminals); for the 16 basic + bright slots that render everywhere from Linux console to legacy conhost, see sgr-fg-basic; for the matching background side, see sgr-bg-256. The 256-colour cube is the right default when you need theme-independent colour but want maximum portability — it's the largest palette that survives most multiplexer + legacy-terminal hops without quantization, with truecolor as the modern-stack upgrade.
Spec citation: xterm-ctlseqs (256-color extension)
Examples
for n in 16 51 196 226 51 21 201; do printf "\033[38;5;${n}m■\033[0m"; done; echofor n in [16, 196, 226, 21, 51, 201]: print(f'\x1b[38;5;{n}m■\x1b[0m', end='')for _, n := range []int{16,196,226,21,51,201} { fmt.Printf("\x1b[38;5;%dm■\x1b[0m", n) }[16,196,226,21,51,201].forEach(n => process.stdout.write(`\x1b[38;5;${n}m■\x1b[0m`))int n[] = {16,196,226,21,51,201}; for (int i=0;i<6;i++) printf("\x1b[38;5;%dm■\x1b[0m", n[i]);Used in
Real-world tools that emit this sequence — anchors the bytes to commands you've already used.
- batsyntax themes — Solarized, OneDark, Monokai use the 256-colour palette
- eza (modern ls)
- deltagit pager — language tokens use 256-colour syntax theme
- tig (git TUI)
- vim, neovim (default for 256-colour terminals)
Frequently asked
Short answers to the questions developers actually search for this sequence.
- How do I map a hex colour to the nearest xterm-256 palette index?
- For the 6×6×6 RGB cube, quantize each channel to 0–5 with
round(c * 5 / 255), then compute the index as16 + 36*r + 6*g + b. For near-greyscale targets,232 + round(luma * 23 / 255)against the grayscale ramp often gives a better match than the cube. The cube doesn't cover every hex evenly — near-blacks and near-whites land more cleanly on the ramp. Production libraries (chalk,colorama,rich) ship optimised lookups that consider perceptual distance against the actual rendered palette. - Why does my 256-palette gradient render banded?
- The 6×6×6 cube gives only 6 levels per channel, so a smooth blue-to-cyan run quantizes to a handful of distinct slots regardless of how many cells you generate — adjacent cells collapse onto the same index. The fix is one of: (1) emit truecolor (
\x1b[38;2;R;G;Bm— 256 levels per channel) when$COLORTERM=truecoloris set, with a 256-cube fallback otherwise; or (2) pre-quantize your gradient steps to distinct cube indices so each cell jumps a slot instead of stuttering.
Terminal support
- xterm
- yes
- Linux console (fbcon)
- no
- macOS Terminal.app
- yes
- iTerm2
- yes
- Windows Terminal
- yes
- cmd.exe / ConPTY
- partial
- kitty
- yes
- alacritty
- yes
- WezTerm
- yes
- Ghostty
- yes
- GNOME Terminal
- yes
- Konsole
- yes
- tmux
- yes
- GNU screen
- yes
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | no | yes | yes | yes | partial | yes | yes | yes | yes | yes | yes | yes | yes |