Pilates 2.0: Pure-TypeScript Flex Layout Engine Beats WASM Yoga on All Benchmarks
Pilates, a flex layout engine for terminal UIs written entirely in TypeScript, now outperforms the WASM-compiled Yoga library (used by Ink) across all nine benchmark scenarios. The latest release (2.0.1) shows a 1.7x to 10x speedup over Yoga, depending on workload. The key insight: for small, frequently-updated trees typical of terminal UIs, the JS-WASM crossing cost dominates, and algorithmic improvements in pure TS can flip the performance equation.
The Numbers
Benchmarks on win32-x64, Node 22, using tinybench with bootstrap CI95:
| Scenario | Pilates | yoga-layout (WASM) | Ratio |
|---|---|---|---|
| tiny (10 nodes) | 4.5µs | 19.0µs | 4.2x faster |
| realistic (~100) | 121µs | 328µs | 2.7x faster |
| stress (~1000) | 601µs | 1.94ms | 3.2x faster |
| big (~5000) | 3.32ms | 9.17ms | 2.8x faster |
| huge (~10000) | 8.62ms | 18.5ms | 2.1x faster |
| hot-relayout | 16.3µs | 83.0µs | 5.1x faster |
| hot-relayout + boundaries | 15.8µs | 77.8µs | 4.9x faster |
| hot-relayout (text mutation) | 8.9µs | 90.6µs | 10x faster |
| hot-structural | 71.3µs | 118.3µs | 1.7x faster |
Caveat: these are 9 hand-picked scenarios, not a universal claim. Reproduce with pnpm bench (~5 min).
Why Pure TS Beats WASM Here
Terminal UIs are a hostile workload for WASM engines. Trees are small (10–10,000 nodes), but updates are frequent — every keystroke, tick, or frame. The per-call crossing cost from JS to WASM is a few microseconds, comparable to Yoga's compute kernel itself. A pure-TS engine pays no crossing cost. The hot-structural workload — where Yoga's native algorithm previously led by ~5x — was the toughest test. Pilates now wins that too.
How Hot-Structural Went from ~450µs to ~70µs
Two algorithmic changes:
- Linear-recurrence main-axis positions — The original rule was a cumulative sum: each cell's position depended on every prior sibling. A 100-cell row meant ~300 dependency edges. The new rule reads only the previous cell:
// New: each cell only reads the previous one
mainPos[N] = mainPos[N-1] + prev.mainSize + prev.marginEnd + me.marginStart + gap
Reverse-direction (row-reverse/column-reverse) keeps the cumulative-sum fallback.
- Fold default-valued style inputs — Roughly half of input fields were at default values (margin: 0, minWidth: 0, etc.), but still consumed dirty-flag slots and propagated through dependents. Phase 17 folds these into compile-time constants. Each cell went from ~15 fields to ~7.
Combined, hot-structural dropped from ~450µs to ~70µs.
Why Not a Native Rewrite?
The author considered porting to a native-compiled-to-WASM language before doing the algorithmic work. Glad they didn't. Yoga's advantage wasn't arithmetic speed — its C++ kernel is fast, but that wasn't the bottleneck. The bottleneck was the structural-mutation algorithm. A native port would inherit the same algorithmic shape and reach parity at best. The fix was algorithmic, and it worked in TypeScript.
Validation and a Hotfix Story
- 1,470 unit + integration tests pass
- Structural-differential fuzzer green at 3,000 runs
- 33 Yoga oracle fixtures (cell-for-cell comparison)
- Byte-identical cached-vs-cold differential mode at 833 runs
Within hours of publishing 2.0.0, the fast-check property fuzzer caught a real bug: createStyleDirtier threw on a node whose entire style had been folded out. 2.0.1 shipped same day with a fix and pinned regression test. "Property-based fuzzing earns its keep," the author notes.
API Stability
Public calculateLayout() is byte-identical between 1.x and 2.x. The SemVer-major bump reflects internal changes: typed-array runtime, unbounded LayoutPool, per-property dirty bitmask, and the algorithmic changes above. Upgrade for transparent speedup.
Try It
git clone https://github.com/pilatesjs/pilates
cd pilates
pnpm install
pnpm bench # ~5 min
Or install the engine:
npm install @pilates/core
Full React stack:
npm install @pilates/react @pilates/widgets react
Adversarial benchmarks welcome — the project wants to find where this approach breaks down.
Why It Matters
Terminal UIs are increasingly built with React-like frameworks (Ink, React-blessed). A faster layout engine means snappier interactive tools, from dev servers to monitoring dashboards. Pilates proves that pure TypeScript can be competitive with native code for this workload, opening the door to simpler, more maintainable toolchains without WASM dependencies.



