Chafa 1.14: All-singing, all-dancing

Dear friends, comrades, partners in crime and moderate profit! I am pleased to announce the immediate availability of Chafa 1.14.0. There are release notes, but who's got time for that? All the fun stuff is in this post. You should be reading it instead.

Pixel perfection

Images can now be padded instead of stretched to fit their cell extent exactly –

Yehat terminal no longer blurry

– so I'll no longer have to explain why our 50kLOC monstrosity couldn't do pixel-perfect output, while sixcat (300LOC) did so effortlessly. Naturally, I had to make this as hard as possible for myself by splicing in padding before and after each row as they're processed for channel reordering and such, but on the plus side, this maximizes cache friendliness and parallelism behind a nice and homogeneous internal API.

You can get the old behavior back with --exact-size off. It defaults to auto, which will do the right thing (i.e. pad if image fits in viewport without scaling and scaling wasn't explicitly requested).

Another improvement in this vein is that sRGB is properly linearized in scaling operations now. This is pipelined along with everything else, and should be suitably fast.

Multiplexer passthrough

Previously, it was impossible to do better than character art inside multiplexers like tmux and GNU Screen. This is no longer the case; kitty has a new trick that allows for persistent raster image placement in these, which we implement. The above mentioned multiplexers have slight differences in their passthrough protocols; we support both with the new --passthrough argument.

We also support sixel passthrough. Sixels will be wrecked by multiplexer updates, so this is off by default. You can enable it with e.g. -f sixel --passthrough screen. tmux tends to dispose of the image immediately after we exit, so it may be a good idea to use --passthrough tmux -d 5 there so you get a chance to look at it first.

To my knowledge, Chafa is the only terminal graphics toolkit to offer passthrough for all four combinations of sixel/kitty and tmux/screen. I think the iTerm2 protocol would be doable too, if only.

MS Windows compatibility

It runs pretty well on Windows. You can use it in PowerShell. @oshaboy added support for ConHost, so it can technically be used on very old Windows versions – although this hasn't gotten much testing yet.

Python bindings

Strawberry sssnek, courtesy of Erica

Erica Ferrua Edwardsdóttir's amazing Python bindings have been around for a while now, yet nary a peep from me on my blag. This shameful deficit stands in contrast to the stunning professionalism of her work. You need to do this:

pip install

Do it now. Everything's well structured, the documentation is entertaining and well written, and there's even a tutorial. It's rare to see a project that simultaneously delivers and channels the spirit of F/OSS this well. You'll also find it on GitHub.

JavaScript enablement

Héctor Molinero Fernández started doing WebAssembly builds, which means you can now use the Chafa API from JavaScript. Like so:

npm install chafa-wasm

He also made a cool web app to show off all the bells and whistles!

Cheerleading aside, I've had no hand in either of these projects. The glory belongs to their respective maintainers, along with all of the praise, stars, code submissions and issue reports, heh heh.


@clort81 sent me this picture:

What's special about it? Well, it's character art!

You have to zoom in a bit before it becomes obvious; there're only two colors per character cell.

The glyphs are something else, though. You see, @clort81 wasn't happy with the limited offering of block drawing symbols in Unicode, and set out to create Blapinus, a 6125-glyph (!) hand-pixeled 6×12 PCF font with all the shapes you could ever want.

Best PUA

Plug that into your terminal (and Chafa), et voilà:

sudo cp blapinus.pcf.gz /usr/share/fonts/misc/
xterm -font -blap-*

Note that your fonts may live somewhere else, so relocate accordingly. Then inside XTerm, run:

chafa -f symbols --glyph-file blapinus.pcf.gz --symbols imported hello.png

If you see strange symbols in your output, you can try excluding some wide and ambiguous code points, e.g. --symbols imported-wide-ued00..uffff.

You can probably set this up in other terminals and display servers too, but know that it could be a long and winding path you're going down. Traveler beware. Or push the pedal to the metal and write a trip report. I'd love to read it.

If your terminal renders the font correctly, this can even have somewhat practical qualities:

First, it integrates perfectly with tmux and GNU Screen. Redraw, scrollback and editing just works, no passthrough tricks required.

Second, albeit lossy, the compression ratio is surprisingly good. Assuming four bytes per pixel, a 6×12 cell is 288 bytes uncompressed. We turn this into 39 bytes at most (a maximum of 36 bytes for the direct color sequence plus 3 bytes for the UTF-8 character), or an 86% reduction. Not bad, considering the compression dictionary is a 100kB font file.

Deflate will further compress this kind of data by 3/4, so if you're running this in a compressed ssh session, you can expect the total gain to be about 95%.

Prior art!

I've mentioned Mo Zhou's work before, but I'd be remiss not to bring it up here; they focused their considerable ML skills on a generator that takes a lot of the pain out of the font creation process. Just point it at an image collection, and by the magic of k-means clustering and a minimal increase in your carbon footprint, out pops a new font brimming with delectable puzzle pieces. You get scalable TTF, with SVG as an intermediate format, which is more agreeable with modern rendering stacks. Here it is in VTE:

This ML-generated font makes for a more organic look. There are unfortunately still some artifacts caused – presumably – by VTE's cell padding. The generator has offset hacks to work around it, but it's hard to make custom connective glyphs look perfect in every terminal.

You can read more about it in one of our longest-running GitHub issues. We're taking it all the way.

Cool applications

Chafa's found its way into the nooks and crannies of many a sweet application by now. I'd especially like to mention these three here:

ANSIWAVE BBS, the brainchild of Zach Oakes, is written in Nim and contains an embedded build of Chafa for character graphics generation. As a one-time (and sometimes) BBS sysop and denizen, this hits me right in the feels.

Felix is a nice file browser by Kyohei Uto written in Rust. It uses Chafa as an external image previewer.

kew, a terminal music player by the mysterious @ravachol, is written in C and uses the native Chafa API to generate cover previews. Development on this has been moving very quickly.

A laid-back place to chat about all this stuff

Issue trackers are formal and supposedly high-SNR. If you'd like a more relaxed place to chat about Chafa, your own programs, terminal graphics (modern or ancient), graphics programming in general or artistic expression related to any of these, drop by our secret business Matrix channel, We'll be waiting.

It's the 90s. Go for it.

I'm also enjoying Mastodon these days. Occasional announcements and amusements go there. It's good.


Last, but not least: a big thank you to everyone who wrote code, reported bugs, filed suggestions and helped test this time around. And an extra big thanks to all the packagers and distributors who make the F/OSS world turn. You're the best.


  1. Hurray!!! Thanks to you and contributors for all the work on chafa, to Erica for the Python bindings and @clort for Blapinus.

Leave a Reply

Your email address will not be published. Required fields are marked *