Valo
Valo is a WebGPU-native 2D render engine built with Rust and wgpu. It follows the architecture of Flutter's Impeller that guarantees no shader compilation needed at draw time.
To use valo, you can use the valo crate directly from Rust on native platforms or in a browser through WebAssembly. For JavaScript applications running in browser, you can use valo-web through valo's native API or its Canvas2D-compatible adapter.
Valo is built on top of wgpu and thus supports all platforms that wgpu supports, including Windows, macOS, Linux, iOS, Android and the web.
Resources:
Which package to use
| Interface | Package | Runs on | Use it when |
|---|---|---|---|
| Rust | valo |
Native and browser | Your application is written in Rust |
| JavaScript | valo-web/raw |
Browser | You want raw power and control over the rendering engine |
| Canvas2D adapter | valo-web |
Browser | Bringing existing Canvas2D-shaped code |
| C API | valo-capi |
Native | When C ABI required |
Rust
Drawing with valo is done in two steps:
- First record a
DisplayListwhich is a plain record of drawing commands and can be reused between frames. - Then submit that list to a
valo::Contextto trigger real rendering on the GPU. The following example draws a green rectangle:
use ;
let mut builder = new;
builder.draw_rect;
let display_list = builder.build;
Render to the GPU
Rendering requires:
- A
valo::Context, created from a wgpu device and queue. See the device setup for native Rust or Rust in the browser. - A
RenderTargetto draw into. Acquire one from aSurfacebacked by a winit window or an HTML canvas, or useOffscreenwhen no display is needed.
Render and present an acquired surface frame:
let mut context = new;
if let Some = surface.acquire
JavaScript and TypeScript
Valo's engine API exposes the same recording model to JavaScript:
import {
DisplayListBuilder,
Paint,
createDevice,
initializeValo,
} from 'valo-web/raw';
await initializeValo();
const device = await createDevice();
const renderer = device.attach(document.querySelector('canvas')!);
const builder = new DisplayListBuilder();
const paint = new Paint(0.78, 1, 0.24, 1);
builder.drawRect(40, 40, 240, 140, paint);
const list = builder.build();
const stats = renderer.render(list, true, 0.04, 0.04, 0.05, 1);
// Valo's resources are allocated in WebAssembly memory and need to be freed manually.
stats?.free();
list.free();
builder.free();
paint.free();
renderer.free();
device.free();
For Canvas2D-shaped code, use the adapter:
import { createValoCanvas } from 'valo-web';
const context = await createValoCanvas(document.querySelector('canvas')!);
context.fillStyle = '#c8ff3d';
context.fillRect(40, 40, 240, 140);
The default WebAssembly build requires WebGPU. For older browsers, use
valo-web/compat, which can fall back to WebGL2. See Browser requirements.
Development
Contributing and AI
Valo is developed with strong assistance from AI agents, with humans leading the design, reviewing changes, testing, and remain responsible for correctness.
AI-assisted contributions are welcome as long as the contributor fully reviews the work, understands how it fits the architecture, manually reviews code changes and tests the work. Code that the contributor cannot explain, verify, or maintain should not be submitted.
License
MIT licensed. Third-party test fonts under assets/ retain their own licenses.