# Pixie: a 2D graphics library for Nim, from paths to typesetting

> Pixie is a 2D graphics library written entirely in Nim, comparable in ambition to Cairo and Skia, covering CPU rasterization, text layout, paths, paints, masking, blending and image codecs, with SIMD accelerated operations. It is aimed at Nim developers who want to render or edit images without leaving the language.

**treeform/pixie** — Full-featured 2d graphics library for Nim.

- Repository: https://github.com/treeform/pixie
- Stars: 813 · Forks: 38
- Language: Nim
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/treeform-pixie

## A rasterizer written in the language that calls it

Pixie is a full featured 2D graphics library for Nim, described as similar to Cairo and Skia but written entirely in Nim. The About section lists what that means concretely: CPU rasterization, image codecs, text layout, paths, paints, masking, blending, and SIMD accelerated operations.

That list is the scope. This is not a wrapper around a native graphics engine. The rasterizer, the codec decoders, the text layout, the path machinery and the compositing operators are all in the repository, in src/, and the library is published to the Nim package index as pixie with a nimble file of its own.

The README states that the library is being actively developed and that the authors would be happy for you to use it. The activity is real in the release history, with 6.1.0 on 2026-05-25 following 6.0.0 on 2026-04-16 and 5.1.0 on 2025-09-19, and the last push to the repository was on 2026-10-19.

The audience is Nim developers. There is no binding here for another language, and the API is Nim shaped, with value types like vec2 and rect, a context object with fillStyle and strokeStyle properties, and a separate Image type that you draw into. The repository adds a bindings/ directory, experiments/, tools/, docs/ and a large examples/ tree, plus a separate Pixie Book repository for longer form documentation and an API reference hosted on GitHub Pages.

## Paths, paints, masks and blend modes

The drawing model has two layers, and the lower one is a path engine in the Cairo sense.

Paths are constructed from SVG style path commands, and the README has a table of what is supported: M m for move to, L l for line to, H h for horizontal line to, V v for vertical line to, C c S s for cubic curve to, Q q T t for quadratic curve to, A a for arc to, and z for close path. Every one of them is marked as supported. Both fill rules are available, with even-odd and non-zero windings.

On top of paths sit the paint objects. The gradient example creates a paint with newPaint(RadialGradientPaint) and then assigns gradientHandlePositions and gradientStops, which is how a gradient is positioned and coloured. Strokes carry the usual join and cap vocabulary: Butt, Round and Square caps, and Miter with a miter angle limit, Bevel and Round joins.

The compositing side is where the library goes beyond a basic 2D API. There are seventeen listed blend modes, from Normal, Darken, Multiply and ColorBurn through Lighten, Screen, Color Dodge, Overlay, Soft Light and Hard Light to Difference, Exclusion and the non-separable Hue, Saturation, Color and Luminosity set. Masking is separate from blending, with Mask, Overwrite, Subtract Mask, Intersect Mask and Exclude Mask, so you can combine them as in the masking example where lines are drawn through a mask with the MaskBlend mode.

The visual extras are also present: shadows, glows and blurs, and the README claims pixel-perfect anti-aliasing quality throughout.

## Text is a first class feature, not an afterthought

Typesetting and rasterizing text is listed as a headline feature, including styled rich text via spans, and the examples show two ways of doing it.

The simple path reads a font, sets a size, typesets a string into a layout and fills it into the image:

```nim
var font = readFont("examples/data/Roboto-Regular_1.ttf")
font.size = 20

let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."

image.fillText(font.typeset(text, vec2(180, 180)), translate(vec2(10, 10)))
```

The rich text path builds a list of spans, each with its own font, and typesets the whole list at once:

```nim
let typeface = readTypeface("examples/data/Ubuntu-Regular_1.ttf")

proc newFont(typeface: Typeface, size: float32, color: Color): Font =
  result = newFont(typeface)
  result.size = size
  result.paint.color = color
```

The font side of the library reads TTF, OTF and SVG font files, and the API separates the typeface, which is the file level object, from the font, which is the size and paint level object used for typesetting. That separation is what makes the span API possible, since each span carries its own font and therefore its own size and colour.

Two of the three video links on the project page are about internals rather than demos, one on Pixie 5.0 performance improvements and one on the details of JPEG decoding in Nim, which tells you where the author thinks the interesting work is.

## Codecs: rich on reading, narrower on writing

The image format table is the fastest way to know whether Pixie fits your pipeline, and it is asymmetric.

PNG is read and written. BMP is read and written. QOI is read and written. PPM is read and written. JPEG is read only. GIF is read only. SVG is read only. The features list phrases the direction of travel as PNG, BMP, JPG, SVG plus more in development.

So if your pipeline is decode and render, or reads a reference image and writes PNG, the library covers it. If you need to encode a JPEG or a GIF, it does not, and that is a hard boundary rather than a configuration detail.

The font side is narrower in the same way, with TTF, OTF and SVG all supported for reading. There is no writing, which is unsurprising since fonts are not usually written by an application.

The examples directory shows the file side of the library in use, with image_tiled.nim and tiger.nim among the entries, and the JPEG decoding video suggests the codec work is more than a thin wrapper over an existing library.

## Installing with nimby and running the examples

Installation is a single command, and it is the Nim package manager rather than a conventional build:

```bash
nimby install pixie
```

The test suite runs the same way, as a Nim script:

```bash
nim r tests/tests.nim
```

The examples need a clone of the repository, which the README gives as git clone https://github.com/treeform/pixie, and each example is compiled and run with nim c -r followed by the example file.

The square example is short enough to show the shape of the API, with a context created over an image, a fill style set, a rectangle described by two vectors, and the fill applied:

```nim
let ctx = newContext(image)
ctx.fillStyle = rgba(255, 0, 0, 255)

let
  pos = vec2(50, 50)
  wh = vec2(100, 100)

ctx.fillRect(rect(pos, wh))
```

The rounded rectangle and heart examples show the two other common shapes of call. The heart is drawn by passing an SVG path string directly to fillPath with a parsed colour, which is the quickest way to get a complex shape into the library, and the rounded rectangle uses fillRoundedRect with a corner radius.

The examples folder also holds the rendered output next to each script, so examples/square.nim sits beside examples/square.png. That is a small thing, but it means the repository doubles as a visual regression reference for what the library is supposed to produce.

## SIMD on the CPU, and a separate answer for GPUs

Many of the operations in Pixie are SIMD accelerated, and the project has a video about Pixie 5.0 performance improvements. That is the answer for the CPU, and it is the reason the library describes itself as a CPU rasterizer.

For the GPU the answer is elsewhere. The README has a section titled Pixie plus GPU that points at Boxy, a separate project by the same author, for learning how to use Pixie for realtime graphics with GPU.

That split is worth understanding before you commit. Pixie is a 2D rasterizer that runs on the CPU, with the vectorised parts written to use SIMD where the operation allows it. Realtime rendering on a GPU is a separate project with its own API and its own release cycle. If your requirement is to draw a few thousand sprites per frame, this is not the library for it, and if your requirement is to produce an image, a chart, a PDF-like page or a processed asset, it very well might be.

The larger context is that Pixie is one of the more complete pure language reimplementations of a graphics stack, and the repository layout backs that up. There is src/ for the implementation, tests/ for the suite, docs/ for the documentation, tools/ for command line utilities, experiments/ for work in progress, and bindings/ for exposing pieces to other languages.

## Where Pixie is the wrong tool

Three cases point elsewhere.

The first is a pipeline that must write JPEG or GIF. The codec table is explicit, and both formats are read only. If your output contract includes either, Pixie cannot satisfy it today, and you would need to transcode after rendering, which adds a step and a dependency.

The second is a project that is not Nim. The library is written entirely in Nim and published as a Nim package, and although the repository carries a bindings/ directory, the API surface is Nim shaped, with Nim value types and Nim procedures. Binding it to another language means either reimplementing the API in that language or shipping a foreign function interface layer, and the second option is exactly the dependency that a pure Nim implementation was meant to avoid.

The third is complex script layout. The rich text model is span based, where each span carries a font, a size and a colour, and the examples show mixed sizes and colours in one paragraph. That covers styled inline text well. It is not a full shaping and bidi layout pipeline, and for scripts that require complex shaping, the layout result has to be verified against what you actually need.

There is one more practical point. The library is a moving target, with a 6.0 release in April 2026 and a 6.1 in May, and the API is versioned with the library rather than separately. If you adopt it, pin a version and expect to read the release notes when you upgrade.

## Conclusion

Adopt Pixie if you are writing Nim and need to rasterize, typeset or composite images without pulling in a native 2D engine, especially for a command line tool, a server side image job or a game that renders 2D content. Do not adopt it if you need to write JPEG or GIF files, since only PNG, BMP, QOI and PPM are writable, or if you need a mature text layout engine for complex scripts, since the rich text model here is span based rather than a full shaping pipeline. Verify three things first: that your required codecs and font formats are covered by the format tables, in particular that JPEG and GIF are read only and that fonts are read from TTF, OTF or SVG; how you will install it, since the documented path is nimby install pixie; and whether the performance videos for version 5.0 match the workload you care about. The licence is MIT, the newest release is 6.1.0 dated 2026-05-25, and the last push was on 2026-10-19.

## FAQ

### What is Pixie and what language is it written in?

Pixie is a full featured 2D graphics library for Nim, similar in scope to Cairo and Skia and written entirely in Nim. It covers CPU rasterization, image codecs, text layout, paths, paints, masking, blending and SIMD accelerated operations.

### How do I install Pixie and run its tests?

Install it with nimby install pixie, and run the test suite with nim r tests/tests.nim. To try the examples, clone https://github.com/treeform/pixie and run each one with nim c -r followed by the example file.

### Which image formats can Pixie write?

PNG, BMP, QOI and PPM can be read and written. JPEG, GIF and SVG are read only according to the format table, and the features list notes that more formats are in development.

### What text features does Pixie support?

It typesets and rasterizes text, including styled rich text via spans where each span carries its own font, size and colour. Fonts are read from TTF, OTF and SVG files, and the typeface and font types are separate so that sizes and paints can vary per span.

### Does Pixie use the GPU?

Pixie is a CPU rasterizer, and many of its operations are SIMD accelerated. For realtime graphics with GPU the README points at a separate project, Boxy, by the same author.

## Sources

- [Issues](https://github.com/treeform/pixie/issues)
- [License: MIT](https://github.com/treeform/pixie/blob/master/LICENSE)
- [README](https://github.com/treeform/pixie/blob/master/README.md)
- [Releases](https://github.com/treeform/pixie/releases)
- [treeform/pixie on GitHub](https://github.com/treeform/pixie)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/treeform-pixie
