Limelight
Limelight is a WebGL2 wrapper with a focus on making high-performance WebAssembly graphics code easier to
write and maintain.
Specifically, it:
- Provides a functional interface that abstracts away the statefulness of WebGL. It accomplishes this by using a shadow GPU that tracks the GPU's state, diffs it with the desired state, and sends only the necessary instructions to WebGL.
- Provides abstractions for buffers and uniforms that defer GPU data transfer until the next draw cycle.
- Provides a typed interface to uniforms and buffers, and automatically generates bindings
between shader attributes and Rust
structs through a derive macro.
Getting started
See the examples directory for runnable examples.
This tutorial assumes you're familiar with basic WebGL terminology, like vertex and fragment shaders, uniforms, and buffers.
Drawing a triangle
This example demonstrates the three main steps to produce an image with limelight:
- Create a
Programobject. AProgramin limelight contains the vertex and fragment shader pair (aWebGLProgramobject), and also contains program-specific state. - Create a
Renderer. After we have initialized all of our programs with the GL context, we transfer ownership of the GL context into aRenderer, which then becomes responsible for all GL-side state transitions. - We call
renderer.render(program, buffer), which causes the triangle to be drawn. We have not attached a vertex attribute buffer in this example, and instead use the vertex shader to generate the vertices. We still need to tell WebGL how many vertices (3) we want to generate, so we pass in aDummyBufferof size3.
use WebGl2RenderingContext;
use ;
Using buffers
Buffers enable arbitrary vertex attribute data to be passed into the shaders. Limelight provides a
procedural macro (attribute) for mapping from a Rust-side struct to a GPU-side set of
vertex attributes. To use this macro, your crate will also have to depend on bytemuck and its derive feature.
use WebGl2RenderingContext;
use ;
// This attribute macro derives a number of traits, including `VertexAttribute`, which
// is required for a type to be used in an `Buffer`.
Uniforms
Uniforms are values that can be used in both shader and fragment programs. They can vary
between render calls, but for a given render call each uniform has a constant value
across all vertices and fragments.
use ;
use WebGl2RenderingContext;
Animation
The previous examples have rendered static images, so we haven't had a need to separate code
that sets up the initial data structures from code that updates GPU-side data and triggers an
animation. In this example, we separate the code into a new() method that is called once,
and a render method that is called on every frame.
limelight is not a framework, and in order to integrate with other frameworks, it is not opinionated as to how you structure your code. This example shows one way you might choose to structure code for a simple animation (see the full code to see how it can be integrated with the Yew web framework).
buffer.set_data and uniform.set_data are lazy: they do not result in any GPU activity until
the next time the buffer is used in a render call. (See WebGL Insights
section 14.2, Deferring until the Draw Cycle.) If a buffer or uniform is unchanged between render
calls, it is not re-written to the GPU.
use ;
use WebGl2RenderingContext;


