# Sixel Graphics

> Draw a bitmap image in the text, six pixels high at a time, from a device control string.

- **Sequence:** `DCS P1 ; P2 ; P3 q s…s ST`
- **Defaults:** P1 = 0, P2 = 0
- **VT330/VT340:** [§14.2.1 Device Control String](https://vt100.net/docs/vt3xx-gp/chapter14.html#S14.2.1)
- **xterm:** [Sixel Graphics](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

A sixel is a column of six pixels, and a sixel image is a string of them. It
is sent in a [device control string](https://control-codes.page/esc/dcs/index.html.md) whose final byte is `q`, with
no intermediate byte. Each data character from `?` to `~` is one sixel: its
value less 63 gives six bits, the least significant at the top. The
VT330/VT340 manual defines the format in its chapter 14,
[§14.2](https://vt100.net/docs/vt3xx-gp/chapter14.html#S14.2). The
parameters are:

- **P1**, the pixel aspect ratio: `0`, `1`, `5` and `6` (and the default)
  make each pixel twice as tall as it is wide, `2` five times, `3` and `4`
  three times, and `7` to `9` square. The raster attributes below override
  it.
- **P2**, what a `0` bit means: with `0` or `2` (the default), it is drawn in
  the background color; with `1`, it is left as it was, so the image is
  transparent there.
- **P3**, the horizontal grid size, which the VT300 ignores.

Within the data, a few characters are controls rather than sixels
([§14.3](https://vt100.net/docs/vt3xx-gp/chapter14.html#S14.3)):

| Character | Function |
| --- | --- |
| `! Pn s` | repeat the sixel `s` `Pn` times |
| `" Pan ; Pad ; Ph ; Pv` | raster attributes: the aspect ratio as a fraction, and the image size |
| `# Pc` | draw in color register `Pc` from now on |
| `# Pc ; Pu ; Px ; Py ; Pz` | define color register `Pc`, in HLS (`Pu` = 1) or RGB (`Pu` = 2) |
| `$` | graphics carriage return: back to the left of the same sixel row |
| `-` | graphics new line: down six pixels, to the left |

Where the image goes, and where the text cursor is left when it ends, depend
on [DECSDM](https://control-codes.page/modes/decsdm/index.html.md), mode 80.

The VT330 and the VT340 have sixel graphics, and so did the VT240 before
them; the VT510 does not. xterm has them too, but not as a default xterm.
Sixel support is compiled in unless `configure` is given
`--disable-sixel-graphics`
([`configure.in`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/configure.in#L1101-L1110)),
and it is then turned on only when xterm is a graphics terminal, a 240, 241,
330, 340 or 382
([`ptyx.h`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/ptyx.h#L2517-L2526)).
That is the `decGraphicsID` resource if it names one of those, and
`decTerminalID` otherwise
([`charproc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L11756-L11769)).
Both default to a VT420
([`ptyx.h`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/ptyx.h#L398-L400)),
so `begin_sixel` sets nothing up
([`charproc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L2252-L2274)),
the string is collected like any other, and at ST it is dropped
([`misc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L5365-L5366)).
Nothing is drawn and the cursor does not move, and that is what the cases
expect, as the [sources](https://control-codes.page/sources/index.html.md) page explains. Started as
`xterm -ti vt340`, the same xterm draws the image. The case runner sees
characters, not pixels, so even there it could check only where the cursor
ends up.

The answer to [DA](https://control-codes.page/csi/da/index.html.md) is how a program can tell: a terminal with
sixel graphics includes `4` in it, and a default xterm does not.

## Not in libghostty-vt

libghostty-vt has no sixel graphics. Its DCS handler in `dcs.zig` recognizes
`q` only after `+` (XTGETTCAP) or `$` (DECRQSS), and drops any other device
control string, so a sixel image is dropped too. That is what a default
xterm does, so both cases pass. Ghostty draws images with the kitty graphics
protocol instead.

## Support

| Terminal | Version | Support | Notes |
| --- | --- | --- | --- |
| VT220 | [manual](https://vt100.net/docs/vt220-rm/), 2nd ed., 1984 | No | the VT220 has no graphics |
| VT340 | [manual](https://vt100.net/docs/vt3xx-gp/), vol. 2, 2nd ed., 1988 | Yes | [§14 Sixel Graphics](https://vt100.net/docs/vt3xx-gp/chapter14.html) |
| VT510 | [manual](https://vt100.net/docs/vt510-rm/), 1st ed., 1993 | No | the VT510 manual has no sixel graphics |
| xterm | patch 412, [xterm-411a](https://github.com/ThomasDickey/xterm-snapshots/tree/xterm-411a) | No | consumed and ignored: compiled in, but on only as a VT240, VT330, VT340 or VT382, and a default xterm is a VT420 |
| libghostty-vt | [83edd49](https://github.com/ghostty-org/ghostty/tree/83edd491e3024ae5e50393d62877b8897da1cccd) | No | the string is dropped |
| Konsole | 26.11.70, [a24c3d71](https://invent.kde.org/utilities/konsole/-/tree/a24c3d71be3684f24d030aa2dbe9bd723d985233) | Yes | draws them ([`Vt102Emulation.cpp`](https://invent.kde.org/utilities/konsole/-/blob/a24c3d71be3684f24d030aa2dbe9bd723d985233/src/Vt102Emulation.cpp#L694-L700)), and answers DA with `62;1;4` ([`Vt102Emulation.cpp`](https://invent.kde.org/utilities/konsole/-/blob/a24c3d71be3684f24d030aa2dbe9bd723d985233/src/Vt102Emulation.cpp#L2712-L2713)) |
| ConEmu | build 230724, its [documentation](https://conemu.github.io/en/AnsiEscapeCodes.html) | No | not in ConEmu's list, which has no DCS; [ANSI escape codes](https://conemu.github.io/en/AnsiEscapeCodes.html) |

## Validation

### SIXEL-1: Nothing is drawn, and the cursor does not move

Input, one step per line:

```text
A
\eP0;0;8q"1;1;4;12\#1;2;100;0;0\#1~~~~-~~~~\e\\   # a red 4×12 image
B
```

Expected screen:

```text
|AB____|
|______|
cursor 1,3
reply none
```

### SIXEL-2: Text before ST is part of the image

Input, one step per line:

```text
\ePq~~~~   # never terminated
ABC        # sixels, like the rest
```

Expected screen:

```text
|______|
cursor 1,1
```

---

This is the Markdown version of <https://control-codes.page/dcs/sixel/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.

The validation cases are written in the notation described in <https://control-codes.page/notation/index.html.md>.
