Kitty graphics protocol
Kitty graphics protocol is a modern terminal graphics protocol that transmits images using base64-encoded data within APC (Application Program Command) sequences. It supports multiple image formats, random-access placement, chunked transfers, and persistent image storage by ID.
Syntax
APC G key=value,key=value ; payload ST Kitty graphics command
kitty-graphics = 0x1b, "_G", control-data, [ ";", payload ], ( 0x1b, "\\" ) ;
control-data = key-value, { ",", key-value } ;
key-value = key, "=", value ;
key = letter, { letter } ;
value = char, { char } ;
payload = base64-char, { base64-char } ;
base64-char = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
| "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
| "U" | "V" | "W" | "X" | "Y" | "Z"
| "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j"
| "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t"
| "u" | "v" | "w" | "x" | "y" | "z"
| "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
| "+" | "/" | "=" ;
letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j"
| "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t"
| "u" | "v" | "w" | "x" | "y" | "z" ;
char = letter | digit ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;Parameters
Action keys (a):
| Value | Action | Description |
|---|---|---|
t | Transmit | Upload image data to the terminal without displaying it |
T | Transmit + Display | Upload and immediately display the image |
p | Display | Display a previously transmitted image by its ID |
q | Query | Test whether the terminal supports the protocol |
d | Delete | Remove images from the terminal's memory or screen |
f | Frame | Animation frame control (see animation) |
Transmission keys:
| Key | Description | Values |
|---|---|---|
f | Pixel format | 24 (RGB), 32 (RGBA), 100 (PNG) |
t | Transmission medium | d (direct/inline), f (file path), t (temp file), s (shared memory) |
s | Image width in pixels | Integer |
v | Image height in pixels | Integer |
S | Total data size in bytes | Integer |
m | Chunked transfer flag | 0 (last chunk or no chunking), 1 (more chunks follow) |
o | Compression | z (zlib-compressed payload) |
Image identification keys:
| Key | Description |
|---|---|
i | Image ID (32-bit unsigned integer) |
I | Image number (client-side identifier for deduplication) |
Display/placement keys:
| Key | Description |
|---|---|
p | Placement ID |
x | X offset within the image (source region left) |
y | Y offset within the image (source region top) |
w | Source region width (0 = full width) |
h | Source region height (0 = full height) |
c | Display width in terminal columns |
r | Display height in terminal rows |
X | Horizontal pixel offset within the cell |
Y | Vertical pixel offset within the cell |
z | Z-index for layering |
C | Cursor movement policy: 0 (move cursor) or 1 (do not move cursor) |
Delete keys (when a=d):
Value of d | Description |
|---|---|
a or A | Delete all images (visible only / all) |
i or I | Delete by image ID (visible only / all placements) |
p or P | Delete by placement ID |
c or C | Delete by column |
r or R | Delete by row |
x or X | Delete by column range |
y or Y | Delete by row range |
z or Z | Delete by z-index |
Response
When querying (a=q) or when a transmission error occurs, the terminal responds with:
APC G i=<id> ; OK ST Success
APC G i=<id> ; <error> ST Failure
Description
The Kitty graphics protocol, introduced by the Kitty terminal emulator, provides a full-featured system for displaying images in the terminal. Unlike Sixel, which encodes pixel data character-by-character, the Kitty protocol transmits complete image files (PNG, raw RGB, or raw RGBA) as base64-encoded payloads.
The protocol separates image transmission from image display. An application can upload an image once and display it multiple times at different positions and sizes. Images are identified by a numeric ID (i key), which allows persistent storage and later recall.
Chunked transfers
Large images can be split across multiple APC sequences using the m (more) key. Set m=1 on every chunk except the last, which uses m=0 (or omits m). This avoids overwhelming the terminal's input parser with extremely long sequences.
Image placement
When displaying an image, the application can specify which portion of the source image to show (using x, y, w, h) and how many terminal cells it should occupy (using c, r). The terminal scales the image to fit the requested cell dimensions.
Query mode
Applications can detect protocol support by sending a query command. A supporting terminal will respond with an OK message; a non-supporting terminal will silently ignore the APC sequence.
The Kitty graphics protocol uses APC (ESC _) as the introducer rather than the more common CSI or OSC. Some terminal multiplexers strip or ignore APC sequences, which can prevent the protocol from working through tmux or screen.
Examples
# Query protocol support (transmit a 1x1 PNG and check for response)
printf '\e_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\e\\'
# Transmit and display a small 2x2 red PNG image inline
printf '\e_Gf=100,a=T;iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAADklEQVQI12P4z8BQDwAEgAF/QualzQAAAABJRU5ErkJggg==\e\\'
# Transmit image with ID 1 without displaying
printf '\e_Ga=t,i=1,f=100;iVBORw0KGgoAAAANSUhEUg...\e\\'
# Display previously transmitted image ID 1
printf '\e_Ga=p,i=1\e\\'
# Display image ID 1 scaled to 10 columns by 5 rows
printf '\e_Ga=p,i=1,c=10,r=5\e\\'
# Delete all visible images
printf '\e_Ga=d,d=a\e\\'
# Chunked transfer (two chunks)
printf '\e_Gf=100,a=T,m=1;iVBORw0KGgoAAAAN...\e\\'
printf '\e_Gm=0;...remaining-base64...\e\\'
Specifications
| Specification | Section |
|---|---|
| Kitty Graphics Protocol | — |
Terminal support
| Terminal | Support | Version | Notes |
|---|---|---|---|
| Terminal Emulators | |||
| Alacritty | ✗ | No | Not implemented |
| Bobcat | ✗ | No | No APC G sequences found |
| contour | ✗ | No | APC parsing exists but no Kitty graphics implementation |
| foot | ✗ | No | APC sequences are ignored (state_sos_pm_apc_string only ignores) |
| Ghostty | ✓ | v1.0.0 | |
| iTerm2 | ✓ | v20260216-nightly | APC G protocol via KittyImageCommand |
| Kitty | ✓ | 0.19.3 | |
| Konsole | ✓ | Yes | APC G protocol with image cache support |
| mintty | ✗ | No | APC sequences (including Kitty graphics) are explicitly ignored |
| mlterm | ✗ | No | APC sequences parsed but no Kitty graphics implementation |
| PuTTY | ✗ | No | APC sequences recognized but explicitly ignored (terminal.c:3244-3246) |
| Rio | ✗ | No | Implementation exists in development branch (zero-dot-three) but not yet released |
| rxvt-unicode | ✗ | No | No APC sequence handler |
| st | ✗ | No | APC sequences recognized but not processed |
| terminology | ✗ | No | |
| VT100 | ✗ | No | |
| VTE | ✗ | No | APC sequences are parsed but no Kitty graphics protocol handler exists |
| WezTerm | ✓ | 20220101-133340-7edc5b5a | |
| Windows Terminal | ✗ | No | No APC G handler found. Only Sixel graphics are implemented |
| xterm | ✗ | No | Not implemented - APC sequences ignored |
| xterm.js | ✓ | 0.9.0 | Requires @xterm/addon-image addon. Added in addon version 0.9.0 (unreleased, currently in development). |
| Multiplexers | |||
| cy | ✗ | No | |
| GNU Screen | ✗ | No | APC only handles status strings, not graphics |
| tmux | ✗ | No | APC strings parsed but only used for window titles, not graphics |
| tuios | ✓ | v0.6.0 | |
| Zellij | ✗ | No | |
See also
- Sixel graphics -- Legacy bitmap graphics protocol
- iTerm2 inline images -- Alternative inline image protocol
Unicode placeholders
Unicode placeholders allow applications to place images at specific cell positions by emitting a special Unicode character (U+10EEEE) into the terminal's text grid. The terminal replaces these placeholder characters with the corresponding region of a previously transmitted image.
Placeholder syntax
U+10EEEE [+ diacritics] Unicode placeholder character
placeholder = 0x10EEEE, [ row-diacritic ], [ col-diacritic ] ; row-diacritic = ? U+0305 to U+0CF4 ? ; (* diacritic encoding the row index *) col-diacritic = ? U+0305 to U+0CF4 ? ; (* diacritic encoding the column index *)
Placeholder characters use combining diacritical marks to encode positioning information. Each diacritic maps to a numeric index representing the row or column of the image tile that should be rendered in that cell.
| Component | Description |
|---|---|
| U+10EEEE | The placeholder base character (Private Use Area, Plane 16) |
| Row diacritic | Combining character encoding the image row index (0-based) |
| Column diacritic | Combining character encoding the image column index (0-based) |
The diacritic-to-index mapping uses a predefined table of combining Unicode characters starting from U+0305. The first diacritic (U+0305) represents index 0, the next represents index 1, and so on.
The image ID is communicated by setting the foreground color of the placeholder cell to the image ID value. The placement ID can be specified through additional attributes.
How placeholders work
Standard Kitty graphics commands display images at the current cursor position or at absolute coordinates, which works well for full-screen applications but creates problems for text-mode programs that rely on the terminal's text reflow, scrolling, and cursor movement. Unicode placeholders solve this by embedding image references directly into the text stream.
When the terminal encounters U+10EEEE in its text grid, it looks up the associated image ID (encoded in the cell's foreground color) and renders the appropriate tile of that image in place of the character. The row and column diacritics tell the terminal which portion of the image to show in each cell.
- Transmit the image using the standard Kitty graphics protocol (
a=tora=T), assigning it an image ID. - Emit placeholder characters into the terminal at the desired cell positions. Set the foreground color to the image ID, and use diacritics to specify which tile of the image each cell should display.
- The terminal replaces the placeholder characters with the corresponding image regions during rendering.
This approach preserves text-grid semantics. Placeholders reflow with surrounding text when the terminal is resized, scroll naturally with the screen buffer, and can be overwritten like any other character.
Unicode placeholders require the image to be transmitted first via the Kitty graphics protocol. The placeholder character alone does not contain any image data -- it only references a previously uploaded image by ID.
# Step 1: Transmit a 2-cell-wide, 1-cell-tall image with ID 42
printf '\e_Ga=t,i=42,f=100;iVBORw0KGgoAAAANSUhEUg...\e\\'
# Step 2: Place the image using Unicode placeholders
# Set foreground color to image ID 42 via SGR 38;5;42,
# then emit U+10EEEE with row=0,col=0 diacritic and row=0,col=1 diacritic
printf '\e[38;5;42m\U0010EEEE\u0305\u0305\U0010EEEE\u0305\u0306\e[39m'
# Step 3: Display the placement
printf '\e_Ga=p,i=42,U=1\e\\'
Terminal support — Unicode placeholders
| Terminal | Support | Version | Notes |
|---|---|---|---|
| Terminal Emulators | |||
| Alacritty | ✗ | No | Not implemented |
| Bobcat | ✗ | No | |
| contour | ✗ | No | |
| foot | ✗ | No | No Unicode placeholder U+10EEEE support found |
| Ghostty | ✓ | v1.0.0 | Unicode placeholder U+10EEEE with diacritic encoding for row/col |
| iTerm2 | ✓ | v20260216-nightly | Unicode placeholder U+10EEEE supported |
| Kitty | ✓ | 0.28.0 | |
| Konsole | ✗ | No | No evidence of U+10EEEE unicode placeholder handling |
| mintty | ✗ | No | |
| mlterm | ✗ | No | |
| PuTTY | ✗ | No | Graphics protocol not supported |
| Rio | ✗ | No | Implementation exists in development branch but not yet released |
| rxvt-unicode | ✗ | No | No graphics protocol support |
| st | ✗ | No | No Unicode placeholder handling for graphics |
| terminology | ✗ | No | |
| VT100 | ✗ | No | |
| VTE | ✗ | No | |
| WezTerm | ✗ | No | |
| Windows Terminal | ✗ | No | Kitty graphics protocol not implemented |
| xterm | ✗ | No | |
| xterm.js | ? | ? | Kitty graphics protocol is implemented via addon, but Unicode placeholder support is uncertain from source inspection. |
| Multiplexers | |||
| cy | ✗ | No | |
| GNU Screen | ✗ | No | Kitty graphics protocol not implemented |
| tmux | ✗ | No | |
| tuios | ✗ | No | U+10EEEE unicode placeholders not implemented |
| Zellij | ✗ | No | |
Animation
Animation extends the Kitty graphics protocol with frame-based animation support. Applications can define multiple frames for an image, control inter-frame timing, and specify how frames are composited together.
Animation syntax
APC G a=f,... ; payload ST Animation frame command
APC G a=a,... ST Animation control command
animation-frame = 0x1b, "_G", frame-data, [ ";", payload ], ( 0x1b, "\\" ) ;
animation-control = 0x1b, "_G", control-data, ( 0x1b, "\\" ) ;
frame-data = "a=f", { ",", key-value } ;
control-data = "a=a", { ",", key-value } ;
key-value = key, "=", value ;
key = letter, { letter } ;
value = char, { char } ;
payload = base64-char, { base64-char } ;
base64-char = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
| "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
| "U" | "V" | "W" | "X" | "Y" | "Z"
| "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j"
| "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t"
| "u" | "v" | "w" | "x" | "y" | "z"
| "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
| "+" | "/" | "=" ;
letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j"
| "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t"
| "u" | "v" | "w" | "x" | "y" | "z" ;
char = letter | digit ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;Frame editing keys (when a=f):
| Key | Description | Default |
|---|---|---|
i | Image ID to add frames to | (required) |
r | Frame number to edit or create | Append new frame |
x | X offset of the frame data within the image canvas | 0 |
y | Y offset of the frame data within the image canvas | 0 |
c | Composition mode for this frame (see below) | 0 |
z | Gap in milliseconds before showing the next frame | 0 (use previous gap) |
f | Pixel format (24 for RGB, 32 for RGBA, 100 for PNG) | 32 |
s | Frame data width in pixels | Image width |
v | Frame data height in pixels | Image height |
Composition modes (c):
| Value | Mode | Description |
|---|---|---|
0 | Overwrite | The frame data completely replaces the canvas at the specified offset |
1 | Blend (alpha composite) | The frame data is alpha-composited over the existing canvas content |
Animation control keys (when a=a):
| Key | Description | Values |
|---|---|---|
i | Image ID to control | (required) |
s | Animation state | 1 (stop), 2 (run/loop), 3 (run once and show last frame) |
r | Frame to jump to | Frame number (1-based) |
z | Default gap in milliseconds | Integer |
v | Number of loops | 0 (infinite), or a positive integer |
Frame model
An animated image is a single image ID with multiple frames. The first frame is the base image transmitted with the standard a=t or a=T action. Additional frames are added using the a=f action.
Each frame consists of pixel data that is drawn onto the image canvas at a specified offset (x, y). The composition mode determines how the new frame data interacts with the existing canvas:
- Overwrite (
c=0): The frame's pixels replace the corresponding region of the canvas. - Blend (
c=1): The frame's pixels are alpha-composited over the canvas, allowing partial updates where only changed regions are sent.
Timing and playback
The gap (z) specifies the delay in milliseconds before advancing to the next frame. Each frame can have its own gap value, or a default can be set via the animation control command. A gap of 0 means the frame inherits the previously set gap value.
After all frames are defined, the animation can be controlled using the a=a action:
- Set
s=2to start looping the animation - Set
s=3to play through once and stop on the last frame - Set
s=1to stop the animation - Set
r=Nto jump to a specific frame - Set
v=Nto limit the number of loops
Animations consume terminal resources proportional to the number and size of frames. Applications should delete animated images when they are no longer needed using the standard Kitty graphics delete command (a=d).
# Step 1: Transmit the base image (frame 1) with ID 7
printf '\e_Ga=T,i=7,f=32,s=100,v=100;...base64-frame1...\e\\'
# Step 2: Add frame 2 with a 100ms gap
printf '\e_Ga=f,i=7,z=100,f=32,s=100,v=100;...base64-frame2...\e\\'
# Step 3: Add frame 3 with a 100ms gap, using alpha blending
printf '\e_Ga=f,i=7,z=100,c=1,f=32,s=100,v=100;...base64-frame3...\e\\'
# Start the animation (loop forever)
printf '\e_Ga=a,i=7,s=2\e\\'
# Play the animation once
printf '\e_Ga=a,i=7,s=3\e\\'
# Stop the animation
printf '\e_Ga=a,i=7,s=1\e\\'
# Jump to frame 2 and set gap to 200ms
printf '\e_Ga=a,i=7,r=2,z=200\e\\'
# Loop 3 times then stop
printf '\e_Ga=a,i=7,s=2,v=3\e\\'
Terminal support — Animation
| Terminal | Support | Version | Notes |
|---|---|---|---|
| Terminal Emulators | |||
| Alacritty | ✗ | No | Not implemented |
| Bobcat | ✗ | No | |
| contour | ✗ | No | |
| foot | ✗ | No | No Kitty graphics protocol support |
| Ghostty | ✗ | No | Explicitly listed as unimplemented in TODO comments |
| iTerm2 | ✓ | v20260216-nightly | Animation frames and control supported |
| Kitty | ✓ | 0.20.0 | |
| Konsole | ✗ | No | No animation frame parameters handled |
| mintty | ✗ | No | |
| mlterm | ✗ | No | |
| PuTTY | ✗ | No | Graphics protocol not supported |
| Rio | ? | ? | Unable to determine from source code review |
| rxvt-unicode | ✗ | No | No graphics protocol support |
| st | ✗ | No | No graphics protocol support |
| terminology | ✗ | No | |
| VT100 | ✗ | No | |
| VTE | ✗ | No | |
| WezTerm | ✓ | 20220101-133340-7edc5b5a | |
| Windows Terminal | ✗ | No | Kitty graphics protocol not implemented |
| xterm | ✗ | No | |
| xterm.js | ✗ | No | The README explicitly states 'No animation support' for image formats. |
| Multiplexers | |||
| cy | ✗ | No | |
| GNU Screen | ✗ | No | Kitty graphics protocol not implemented |
| tmux | ✗ | No | |
| tuios | ~ | v0.6.0 | Animation commands acknowledged but not yet implemented |
| Zellij | ✗ | No | |