build system · real-time graphics · v2.4

Every pass
you can name.

AXIOM compiles a real-time graphics scene into an explicit pass list. You declare the passes; the compiler orders them, allocates the targets, and hands you a frame budget before you run it.

The frame beside this column is a Navier–Stokes solver running in this tab, stepped by the pass list below it. Drag across the ink to stir it.

$ npm i -D @axiom/core
  added 1 package — 0 runtime deps
solver passes
/ frame
—
draw calls
/ frame
—
network
requests
—
frame
time
—

This machine has no WebGL 2 context, so the solver cannot run.
The rest of the page — the shell, the spec, the pass list — is unaffected.

passes 0 frame — dye — ink drag to stir

fig. 1 is drawn from the solver's own pass table, so it needs the page script.

fig. 1 — every draw call the frame above costs, in the order they run.

interactive

The shell is the interface.

Type into it. Tab completes, ↑↓ walk the history, Esc clears the scrollback and leaves the field. spec prints the table below, passes prints the pass list, bench times this machine, theme moves the ground band.

axiom — zsh live

spec

Spec

Column 3 names where each value comes from. Anything marked live is read out of the running page each time you look.

live canvas
PropertyValueSource
Solver passes per frame—live pass table
Draw calls per frame—live draw counter
Runtime dependencies—live DOM query
Network requests after load—resource timing
WebGL context—live context query
Solver grid—live target allocation
Dye grid—live target allocation
Canvas backing store—live context query
Device pixel ratio—window.devicePixelRatio
Respects reduced motion—matchMedia
P1

Orders passes by data dependency

Not by the order you wrote them. A velocity pass that samples pressure that no earlier pass wrote this frame is rejected with the offending edge, not rendered as a black screen at 3 a.m. Ping-pong targets are allocated for you, and reallocated on resize without a flicker.

P2

Allocates framebuffers to a budget, not a guess

The solver grid and the dye grid are fixed at build time from the tier you declare, not from devicePixelRatio. Only the canvas backing store follows the display: its CSS box times min(devicePixelRatio, 2). The live numbers are in the spec table above, and targets in the shell prints them.

P3

Carries state across a resize

The simulation grids are independent of the canvas backing store, so a rotation reallocates the default framebuffer and nothing else. The velocity, pressure and dye fields keep the contents they had — there is no copy pass to get wrong and no black flash to debug.

P4

Fails the build on a read-before-write

Every pass goes through one entry point that binds program, framebuffer, uniforms and textures in a fixed order. Bind a destination after a draw and it is still attached to a texture unit; the driver rejects that as a feedback loop and skips the draw without saying so. One funnel, and the order stops being a thing you can get wrong.

the system

Palette

Every colour is a token in :root. The solver reads the same tokens for its ink, so the canvas is on the spine by construction. Click a swatch to copy its name.

← Lab