Skip to content

Performance ​

Numbers measured on the project's reference device: AMD Ryzen 9 5900X, NVIDIA GeForce GTX 1060 6 GB, Windows 11, a 1920 × 1080 display at 120 Hz, Chrome 154, Edge 154 and Firefox 155. The full method, every browser/backend row and the raw result files are in docs/performance.md.

WorkloadTargetMeasured
Pan and zoom over 10 000 visible shapes at 1080pp95 frame ≤ 16.7 msevery frame presented at 120 Hz (p95 8.4 ms, 0 dropped) on WebGPU and WebGL2 in Chrome, Edge and Firefox
Hit-test and snap on the same scenep95 ≤ 8 ms≤ 1 ms round trip to the engine worker (mean 0.05–0.09 ms)
Editing a 200-variable sketch (linkages, polygons, tangent circles, plugins)p95 ≤ 50 ms, hard rules holdp95 23 ms, every hard rule satisfied
Opening a 10 000-object .dotl≤ 2 s0.1–0.19 s
100 000 objectsopens, navigates, a long solve can be cancelledopens in 1.1–1.3 s; cancellation takes effect within 0.1–0.4 s and leaves the document unchanged
Repeated open/closeno leak after warm-upheap and renderer memory flat over 20 cycles

Download size (brotli): engine 469 KiB, renderer 770 KiB. An editor starts in about 0.3 s on a cold page (both WebAssembly modules, the worker and the GPU device).

What keeps it fast ​

  • Worker engine. Solving, hit-testing and snapping run in a Web Worker; the main thread only renders and handles input.
  • Incremental, chunked rendering. The renderer keeps GPU meshes per chunk of at most 256 items, culls chunks outside the view and re-tessellates only changed items or chunks whose level of detail changed. Frames are drawn on demand.
  • Spatial index. Pointer queries never scan the whole document.
  • Sparse solver. The numeric solver uses sparse factorizations ordered for the chain-like structure of sketches and includes constraint curvature, so it needs few iterations even when a linkage moves far.

Measuring your own app ​

Viewport.gpuIdle() resolves when the GPU has finished the frames submitted so far (useful for input → frame measurements), Viewport.memoryStats() reports renderer memory, engine.memory() the engine's WebAssembly memory, and every commit carries CommitReport.solver (iterations, attempts, variables, rules). Firefox reports GPU completion late; measure presented frames with requestAnimationFrame there.

Memory grows with the document: about 2 KB of engine memory per simple object (document, cached world-space geometry and indexes) plus the renderer's meshes.

MIT OR Apache-2.0