I’ve made various attempts over the years to produce a robust line rendering library, at least robust and flexible enough for my needs. Something particularly interesting to me has been getting details of joins and caps right without sacrificing flexibility. I want custom attributes, custom shaders, in-shader projection and line width computation. Though I understand why it’s critical in some places, perfect handling of complex joins and self-intersections is much less interesting to me.
I’ll spare you details of all the intermediate attempts; webgpu-instanced-lines is a library for high-performance, customizable line rendering for WebGPU using instanced triangle strips. It’s mostly a direct WebGPU port of regl-gpu-lines. This notebook is where I originally developed the implementation, but the source now lives on GitHub.
Interactive Demo
Explore the line rendering options with interactive demo below. Drag handles to edit vertices, drag the background to pan, and scroll to zoom. Enable Debug view to see the underlying triangle strip structure. Alternating colors indicate separate instances.
Rationale
Drawing lines on the GPU is surprisingly difficult. The hardware primitive LINE_STRIP produces lines exactly one pixel wide, which is rarely what you want. To get variable-width lines with proper joins and caps, you need to expand lines into triangles yourself.
This library takes a pragmatic approach: it uses instanced rendering with triangle strips to draw one segment per instance, including half of each join on either end. This avoids the complexity of full stroke expansion algorithms while still producing good results for most use cases.
The tradeoff is that the library does not handle self-intersecting lines correctly. For scientific visualization, data plots, and UI elements, this is usually acceptable. For complex vector graphics with many overlapping strokes, you may need a more sophisticated solution.
The shader interface is designed to be flexible. Rather than prescribing a specific data format, you write a vertex function that fetches positions from wherever you store them (buffers, textures, procedural computation) and returns clip-space coordinates plus any per-vertex data you need. The library handles the geometry expansion, and your fragment shader receives a lineCoord varying that makes it easy to implement SDF-based anti-aliasing and stroke effects.
For background on GPU line rendering, see Matt DesLauriers’ Drawing Lines is Hard and Rye Terrell’s Instanced Line Rendering.