Measurements

Generated by mise run bench (scripts/bench.ts) on 2026-09-28. Raw data: bench/results/.

Machine: Apple M4 Max, 16 cores, 48 GB, darwin 25.6.0. Runtimes: Bun 1.4.1, Node v26.10.0.

Runtime numbers come from the built package (dist/), each measure in a fresh process. Medians unless noted.

Startup (cold, fresh process)#

Importing the entry point; for the sandbox this includes instantiating QuickJS (WebAssembly).

bun median (p95) node median (p95)
sandboxed 14.9 ms (15.3 ms) 13.6 ms (15.3 ms)
trusted 1.55 ms (1.59 ms) 2.55 ms (2.77 ms)

Compiling charts#

Parsing (happy-dom's DOMParser in these runtimes; browsers use their own) and compiling + validating. Compiling is the library's cost.

chart states transitions parse (bun) compile (bun) parse (node) compile (node)
gatekeeper 17 14 258 µs 67 µs 352 µs 89 µs
fulfillment 59 72 703 µs 193 µs 979 µs 244 µs
support-desk 333 954 6.52 ms 1.66 ms 11.9 ms 2.50 ms
generated-1000 1001 2064 8.51 ms 2.89 ms 14.7 ms 6.02 ms
generated-5000 5001 10384 46.9 ms 22.6 ms 87.4 ms 41.2 ms

Sessions: creation and memory#

Creating and starting 500 gatekeeper sessions (one machine) and 20 support-desk systems (a root that invokes 14 child machines on start), after one warm-up session. Memory is the growth per session after a full GC: RSS, JS heap, and the QuickJS WebAssembly memory (which grows in large steps, so it often reads 0 for small counts).

model chart create+start (bun) RSS / heap per session (bun) create+start (node) RSS / heap per session (node)
sandboxed gatekeeper 270 µs 171.8 KB / 139.4 KB + 65.5 KB wasm 311 µs 105.9 KB / 7.9 KB + 65.5 KB wasm
sandboxed support-desk 4.95 ms 3196.8 KB / 2099.4 KB + 886.4 KB wasm 5.73 ms 2513.6 KB / 277.7 KB + 886.4 KB wasm
trusted gatekeeper 47 µs 110.2 KB / 74.0 KB 179 µs 217.4 KB / 170.4 KB
trusted support-desk 1.15 ms 2890.4 KB / 1318.7 KB 3.42 ms 4360.0 KB / 2721.7 KB

Throughput#

External events: 20k send()s processed by a two-state machine that assigns a counter per transition. Microsteps: one macrostep of 20k eventless transitions with a guard and an assignment each. Median of 5 runs.

model events/s (bun) microsteps/s (bun) events/s (node) microsteps/s (node)
sandboxed 93k 101k 56k 85k
trusted 172k 189k 76k 132k

Leak check#

1k cycles of create → start → send → dispose (a 40-state chart), after 100 warm-up cycles; memory after a full GC at four checkpoints. Growth per cycle between the first and last checkpoint (≈ 0 means no leak; RSS includes allocator noise).

model heap / RSS / wasm per cycle (bun) heap MB at checkpoints (bun) heap / RSS / wasm per cycle (node) heap MB at checkpoints (node)
sandboxed 357 B / 7384 B / 0 B 43.28 → 43.46 → 43.54 → 43.54 139 B / 218 B / 0 B 21.74 → 21.79 → 21.80 → 21.84
trusted 270 B / 5090 B / 0 B 9.82 → 10.01 → 9.98 → 10.01 107 B / 896 B / 0 B 20.14 → 20.17 → 20.17 → 20.22

Browser#

Chrome 154.0.0.0, viewport 1500×950, trusted sessions. Times run until the content is in the DOM (checked once per animation frame, so anything under ~17 ms means "within a frame"). Median of 3.

<scxml-explorer>#

chart states first render tree rows in DOM tree scroll: rows, median / p95 frame long tasks while scrolling
gatekeeper 17 12.3 ms 11 17 rows, 16.8 ms / 16.8 ms 0
fulfillment 58 13.9 ms 10 51 rows, 17.0 ms / 17.4 ms 0
support-desk 332 18.0 ms 31 314 rows, 16.7 ms / 17.3 ms 0
generated-1000 1001 4.10 ms 31 1001 rows, 16.7 ms / 17.6 ms 0
generated-5000 5001 10.3 ms 31 5001 rows, 16.7 ms / 17.3 ms 0

<scxml-view>#

session = a host-provided, running session. source = SCXML text with the trusted attribute: parse, compile, load the engine, lay out and draw.

chart state boxes drawn render (session) render (source)
gatekeeper 17 13.2 ms 15.2 ms
fulfillment 59 13.6 ms 14.8 ms
support-desk 76 17.0 ms 38.3 ms
generated-1000 137 44.5 ms 57.7 ms

Charts over the view's max-states (150) are folded: large compound states become single boxes.