Syxel
Early alpha release. Interfaces and output may still change, and some rough edges remain. Bug reports (and any other feedback) are very welcome: please open an issue at github.com/luispedro/syxel/issues.
SIXEL in Python: display images directly in your terminal.
syxel is a small, pure-Python package that converts images to the
SIXEL escape-sequence protocol and writes
them to standard output. If your terminal speaks SIXEL, the image appears inline,
no image viewer required.
It ships a command line tool, imcat, and a matplotlib backend, so that
plt.show() draws your plots in the terminal.
Installation
pip install syxel
Or, from a checkout, for development (the imcat script then tracks your
working copy):
pip install -e .
Requires Python 3.11 or later.
The base install only depends on numpy, which is enough to convert arrays (or matplotlib figures) to SIXEL. Reading image files and the matplotlib backend each come as an extra:
pip install 'syxel[imcat]' # the imcat command line tool
pip install 'syxel[matplotlib]' # the matplotlib backend
pip install 'syxel[imcat,matplotlib]'
Dependencies
- numpy
- imread — optional (
imcatextra), image loading for the command line tool - matplotlib — optional (
matplotlibextra), only for the backend
Usage
imcat image.png
Several images can be given at once; each is written followed by a newline, so they stack vertically:
imcat one.png two.jpg three.tiff
Images larger than 800x1200 are subsampled (halved repeatedly) until they fit. Override the limits with:
imcat --max-height 400 --max-width 600 image.png
Full option list:
| Option | Meaning |
|---|---|
--max-height N |
subsample until the image is at most N pixels high (default: 800) |
--max-width N |
subsample until the image is at most N pixels wide (default: 1200) |
--version |
print the version and exit |
--help |
print usage and exit |
Terminal support
You need a terminal emulator with SIXEL support, such as
foot,
WezTerm,
mlterm, or
xterm started with SIXEL enabled (xterm -ti vt340). In a terminal without
SIXEL support you will just see a wall of escape-sequence bytes.
Since imcat writes to sys.stdout.buffer, you can also capture the raw byte
stream instead of rendering it:
imcat image.png > image.six
matplotlib backend
syxel.backend_sixel is a matplotlib backend that draws figures into the terminal
instead of opening a window. Select it from the environment:
MPLBACKEND=module://syxel.backend_sixel python plot.py
or from Python, before importing pyplot:
import matplotlib
matplotlib.use('module://syxel.backend_sixel')
import matplotlib.pyplot as plt
plt.plot([1, 4, 9])
plt.show() # the figure appears in the terminal
With matplotlib 3.9 or later the short name matplotlib.use('sixel') also works,
via an entry point.
The figure is scaled to fill the terminal: its dpi is raised or lowered so that it
fits the window, which redraws it at the right resolution rather than resampling
it. Terminals that do not report their size in pixels (some multiplexers) fall
back to 1200x800; set SYXEL_MAX_WIDTH and SYXEL_MAX_HEIGHT to override.
savefig gains a sixel format, which writes at the figure's own size:
fig.savefig('plot.sixel') # or
fig.savefig(sys.stdout.buffer, format='sixel')
Each plt.show() prints the figures drawn so far and then drops them, so a script
with several show() calls does not reprint earlier figures.
Using it as a library
The conversion is available directly, independently of the command line tool:
import sys
from syxel.imcat import load_image
from syxel.sixel import rgb_to_palette, write_sixel
rgb = load_image('image.png') # (M,N,3) uint8 array
active, data = rgb_to_palette(rgb) # palette (P,3) and indexed image (M,N)
write_sixel(sys.stdout.buffer, data, active)
write_sixel accepts any object with a write method taking bytes, so an
io.BytesIO works for testing or for building the sequence in memory.
The same is true of figures, without going through the backend machinery:
from syxel.backend_sixel import write_figure
write_figure(sys.stdout.buffer, fig)
How it works
The pipeline has three stages:
-
Load (
load_image) — read the file withimread, subsample by[::2,::2]until it fits the size limits, expand greyscale to three channels and drop any alpha channel. The result is an (M,N,3) uint8 array. -
Quantize (
rgb_to_palette) — SIXEL supports at most 256 colour registers, so colours are counted and the 255 most frequent are kept. If those do not cover at least half of the image, a fixed 5x9x5 RGB cube is used instead. Every distinct source colour is then mapped to its nearest palette entry by squared Euclidean distance. -
Emit (
write_sixel) — write the escape sequences. The image is processed in bands of six rows, with one pass per colour present in the band; each output byte encodes one column's six-pixel bitmask. A final band shorter than six rows only sets the bits of the rows that exist.
The matplotlib backend replaces the first stage: it renders the figure through Agg and hands the resulting pixels to the same last two stages.
Development
Tests use pytest and hypothesis, and are run through pixi:
pixi run -e test test
CI runs the suite on Python 3.11 through 3.14.
Releases are cut by pushing a v<version> tag matching syxel/syxel_version.py
(for example git tag v0.1 && git push origin v0.1), which builds the sdist and
wheel and uploads them to PyPI through
trusted publishing.
License
MIT (see COPYING.MIT).
Copyright (c) 2024–2026 Luis Pedro Coelho
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file syxel-0.1.tar.gz.
File metadata
- Download URL: syxel-0.1.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/5.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4b081ed0fdad4d4c3d615deb57c327ebc5554a788d49b4b0bb8224421b85bf2
|
|
| MD5 |
7dc22c45711d8bec0652dfc377d6713a
|
|
| BLAKE2b-256 |
aaf4e6eb3938d03203a896b6a08b50c43c0932a8e6cec9b6ffafcd576d0877c0
|
File details
Details for the file syxel-0.1-py3-none-any.whl.
File metadata
- Download URL: syxel-0.1-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/5.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
606c047596e32a2ea196de03b6ab13d617f1e7e6b8fbbcd0b1c554a1e3871bb5
|
|
| MD5 |
ea1a66b22ba98b12baa14842a1121137
|
|
| BLAKE2b-256 |
77dc4feb1ca699b29ce19a7c5580702ea83d35b3d9cf6de4ac87a467a69334c0
|