Code and demos for windfoil, a method for filling 2D vector shapes built from quadratic-Bézier contours, by computing winding and analytic anti-aliasing per-pixel in a fragment shader. The algorithm was designed to improve upon the public Slug Algorithm for certain uses cases.
🚩 This builds on prior work (Slug, Vello, Pathfinder, font-rs and others), and may or may not be a novel algorithm. The core of the algorithm was designed by Code Claude on Fable after a ~1.5 hour uninterrupted session within a larger codebase I am working on, although it required several more hours and design/code iterations to tune performance and bugs (see How This Came About). It's entirely possible this algorithm collides with an existing technique in its memory, or perhaps even a patent. After searching, I haven't been able to find any other algorithms or code that approaches vector rendering in the same way, but more research would be needed to be sure, so use this at your own risk.
You can see a demo of this here:
https://texel-org.github.io/windfoil/
Requires Deno 2.x on a machine with a WebGPU-capable GPU.
# renders a PNG in output/
deno task render
# compare against point-sampled box filter and Skia
deno task validate
# serve the demo, then open http://localhost:8080/
deno task serveNote: this repo refers to "Skia" but is actually using @napi-rs/canvas as a reference, which uses Skia under the hood.
This README was written by me, but most of the code and other documentation in the repo was produced by agents at my direction.
docs/ALGORITHM.md— the algorithmdocs/NOTES.md— some additional properties of the algorithm I've found interesting for my own usessrc/windfoil.wgsl— the shader: the winding-integral box filter + the row-band gathersrc/bands.js— the row-band acceleration structuresrc/font.js— glyph outlines + metrics from the bundled font (using opentype.js)bench/— a windfoil-vs-Slug WebGPU benchmarks and acceleration notes
To provide a little context, I have been working for some weeks now on a 2D vector engine that uses the Slug Algorithm (which is great, but primarily developed for text and icons). My goal has been "perfectly" anti-aliased vector graphics at all resolutions for high quality print artwork (e.g. 30k x 30k 16-bit PNG files), while supporting a range of complex paths, strokes, and per-pixel effects for generative art. You can see two demos of the engine here and here, alongside some other posts I've made on X/Twitter about it.
I tasked Fable with solving some problems I was encountering, and it was able to work within the large codebase I had been developing (I had a number of profilers, fuzzers and harnesses already in place across Rust/WASM, JS, Figma's renderer, and Skia via @napi-rs/canvas). Some notes & findings:
- Slug uses a dual-ray approach, splitting shapes into horizontal and vertical bands (each pixel walks two bands), and in some complex scenes I was finding performance bottlenecks in shader reads and storage. The single-ray winding integral approach brought the bands buffer from ~1.54 MB to ~0.84 MB in the tiger test SVG. The new algorithm performed similarly (or faster) in many views and scenes I tested.
- This repo now includes benchmarks against Eric's reference implementation of Slug, see
./bench/README.md. Windfoil performs comparably, sometimes slower, sometimes faster, depending on the scene and view.
- This repo now includes benchmarks against Eric's reference implementation of Slug, see
- Broadly, Slug's AA assumes ~one edge per pixel, so self overlapping strokes would sometimes render with degraded AA. To correct this, I had a CPU prepass (sweep-union) to ensure each fragment received a single edge. Since windfoil's integral handles overlap for free, I was able to remove most of the CPU processing and just operate on the curves directly. This was a huge performance win for some generative art scenes with hundreds of thousands of complex and overlapping strokes, and also meant that more of my shapes became naturally scale invariant (as the CPU preprocessing was flattening curves at a device scale).
- Both Slug and Skia's anti-aliasing could be described as approximations of the box filter, but windfoil computes it (nearly) exactly in all cases I tested and care about. The algorithm beats Slug and Skia by this metric. And, tested across a ~1000 shape corpus, it matched Skia more closely than Slug: Δ 0.164/255 for windfoil vs 0.189 for Slug, and every outlier improved. It tied Slug only on the pathological pixels containing three-plus overlapping winding levels, which as far as I understand no single-sample approach can handle.
- I was struggling to compute a per-cell "backdrop" across shapes with Slug's dual-ray approach, to further optimise complex scenes. This algorithm makes the implementation much simpler: everything outside a cell reduces to a single winding integer I can precompute (the "backdrop").
- This algorithm also opens some other interesting new avenues for my renderer that were not easy with Slug, such as plugging in new AA kernels (e.g. tent, Mitchell–Netravali) and computing curve normals, see
docs/NOTES.md
I've left much of the LLM-generated documents and code alone in this repo, although it has gone through many rounds of iterations (the first draft of the algorithm had various bugs and issues). If this is truly a novel approach, which I am still not entirely sure of, it does present to me an interesting example of AI interpolating and recombining existing ideas to form something new, and as such it seems fitting to let the AI mostly speak for itself in some of the code & documents (em dashes and all). The original name that Fable gave the algorithm was "Area Algorithm," which felt a little dull. I've gone with windfoil, a sport I've never done but would like to try one day. I also like the tie-in with the winding computation, and the light and airy properties the term evokes in my mind.
See the ./bench folder for full results, but at a glance: Slug is often faster when rendering minified shapes (i.e. zooming out, drawing small things), and windfoil tends to be marginally faster when shapes take up more of the viewport (i.e. zoomed in).
- font-rs — Raph Levien's signed-area scanline rasterizer (writeup)
- Pathfinder — Patrick Walton's GPU vector/font rasterizer
- Vello — GPU compute-centric 2D renderer (Linebender)
- Slug — Eric Lengyel's dual-ray analytic per-pixel coverage (JCGT 2017)
- Analytic Rasterization of Curves with Polynomial Filters — Manson & Schaefer (Eurographics 2013), exact prefiltered curve coverage under polynomial filters
- Easy Scalable Text Rendering on the GPU — Evan Wallace's atlas-free outline gather
- GPU text rendering with vector textures — Will Dobbie (2016)
- Random-Access Vector Graphics — Nehab & Hoppe, per-cell curve lattice (2008)
- Valve SDF — Chris Green, distance-field text (2007)
- msdfgen — Viktor Chlumský, multi-channel SDF atlases
If you reference this work, you can cite it as:
Matt DesLauriers. Windfoil: per-pixel winding and analytic anti-aliasing for 2D vector shapes. 2026. https://github.com/texel-org/windfoil
@misc{deslauriers2026windfoil,
author = {DesLauriers, Matt},
title = {Windfoil: per-pixel winding and analytic anti-aliasing for 2D vector shapes},
year = {2026},
howpublished = {\url{https://github.com/texel-org/windfoil}}
}Apache-2.0 — see LICENSE. This repository bundles the Lato font (SIL OFL 1.1), see
assets/OFL.txt.
