97%

BENCHMARKS

Not peak throughput. Not best-of-N. Not cache-warmed. Real frames, real GC, real pipeline — measured the way your app actually runs.

97%
CELLS SKIPPED
per scroll frame
2.3ms
FULL FRAME
reconcile + layout + paint + diff
3.4×
WASM ACCELERATED
optional Rust diff renderer
4.9µs
ZERO-CHANGE DIFF
per frame, skip path
RENDERING BACKEND

Buffer & Diff Engine

Typed-array cell buffer with cell-level diff. WASM-accelerated when available.

Buffer Operations 200×50

Create
0.04ms
Write 10K cells
0.08ms
Clear
4.0µs
Clone
0.02ms

Diff Renderer 200×50

No changes
0.02ms
3 rows (scroll)
0.11ms
Full (10K cells)
0.19ms
charWidth 100K
0.26ms · 390M/s
FULL PIPELINE

Frame Breakdown

What happens in a single 2.3ms frame — every stage measured.

Single Frame — 2.28ms total
React + Layout + Paint 2.21ms
Diff 0.07ms
~0
96%
3%
1%
2.21ms
Reconcile + Layout + Paint
96% of frame
0.07ms
Cell Diff (3 rows)
3% of frame
0.02ms
Diff (no changes)
skip path
2.28ms
total frame time
430+
internal FPS
headroom above 60fps
7,000+
backend-only FPS
16.6ms budget at 60fps — Storm uses 2.28ms. Backend FPS = buffer → diff → write without React.
LAYOUT ENGINE

Pure TypeScript Flexbox + Grid

Zero native dependencies. Incremental caching for unchanged subtrees.

Full Computation cache invalidated

10
9µs
100
0.04ms
1,000
0.21ms
10,000
4.1ms
50,000
13.2ms
Grid 20×20
0.26ms
50-deep nest
0.15ms
Children count on left — real layout time on right. Cache invalidated every iteration.

Incremental Cache unchanged subtrees

8.6M
ops/sec for 1,000 children
~0.1µs per layout pass when tree hasn't changed
Same constraints + same props = instant skip. Most scroll frames hit this path.
SYNTAX HIGHLIGHTING

WASM Tokenizer + Auto-Virtualization

DFA-based Rust tokenizer with automatic line virtualization. Only visible lines create React elements.

Auto-Virtualized Rendering

Only visible lines create React elements. Tokenizes everything, renders the viewport.

10,000 lines121ms
50,000 lines265ms
100,000 lines458ms
500,000 lines2.7s
~50 React elements regardless of total code size. Scroll is instant.

Cached Re-render

Same code, second render — tokens cached in ref.

100 lines3.7ms
1,000 lines15ms
5,000 lines240ms

First paint (cold)

100 lines51ms
1,000 lines510ms
SCROLL OPTIMIZATION

DECSTBM Scroll Regions

Terminal-native scroll regions for pure scroll operations. The terminal shifts pixels — Storm writes only the new row.

Without DECSTBM
736 bytes/frame
With DECSTBM
159 bytes/frame
faster scroll
78% fewer bytes to stdout
Activates for full-width single ScrollView with delta ≤ 5 lines
ACCELERATION

Optional WASM Modules

Rust-compiled WebAssembly. Loads automatically. Falls back to TypeScript silently. Zero configuration.

3.4×

Diff Renderer

Rust render_line() for ANSI string generation. Eliminates JS string concatenation overhead.

DFA

Syntax Tokenizer

Deterministic finite automaton in Rust. Zero-copy tokenization — returns byte offsets, not strings.

~35KB

Total Binary

Both modules in a single WASM binary. Ships with the npm package. No separate install.

MEMORY

Near-Zero GC Pressure

Typed-array buffers eliminate ~30,000 Cell objects per frame. No object allocation in the render hot path.

Allocation Cost

100 × 300×80 buffers+18 MB
10,000 layout nodes+2.5 MB
500K highlighted lines (tokens)~1.6 GB

Why It Matters

Traditional frameworks allocate a JS object per cell — a 200×50 terminal creates 10,000 objects every frame.

Storm uses Int32Array + Uint8Array — flat typed arrays. One allocation for the entire buffer. No per-cell GC pressure.

~30,000 fewer objects per frame.

VIRTUALIZATION

ScrollView at Scale

Full React render with all children materialized. For extreme lists, use VirtualList.

ScrollView renderToString

100
4.3ms
1,000
18ms
10,000
27ms
50,000
203ms

ScrollView materializes all children as React elements. For lists beyond 1,000 items, use VirtualList — it renders only visible rows.

<VirtualList
  items={data}
  height={20}
  renderItem={(item) => ...}
/>
ARCHITECTURE COMPARISON

Why It's Fast

Storm's architecture is fundamentally different from traditional terminal frameworks.

Feature Traditional Storm
Render strategy Full repaint every frame Cell-level diff — 97% skipped
Buffer model JS object per cell 30K+/frame Typed arrays — zero Cell objects
ANSI generation String concat O(n) allocs WASM render_line — 3.4× faster
Layout caching Full recompute every render Incremental — 8.6M ops/sec cached
Scroll optimization Full repaint per scroll DECSTBM — 78% fewer bytes
Animation path Same pipeline as structural Dual-speed — requestRender() skips React
DEEP DIVE

Three Rendering Paths

Storm picks the fastest path per frame. Most frames use the direct cell path — the others exist for correctness.

12.9µs
DIRECT CELL

Spinner, cursor blink, single character change. Skip layout, skip clean rows, diff one cell.

14.5µs
INCREMENTAL

One dirty row repainted. Clean subtrees skipped entirely. Diff scans only painted rows.

49µs
FULL REPAINT

10 rows changed. Full clear + repaint + diff. Still under 50µs on a 300×80 terminal.

Per-Row Damage Tracking

Each row tracks which columns were written. The diff scans only damaged columns, not the full width.

Full width scan [0, 300)
0.79µs
Narrow damage scan [150, 151)
0.04µs
95% of column scans eliminated
METHODOLOGY

How We Benchmark

No fake cache hits. Layout cache is explicitly invalidated between iterations. Every measurement does real computation.

GC pressure included. No manual GC calls between measurements. GC spikes are detected and flagged.

p50/p99 tracked. Extreme benchmarks report median and 99th percentile, not just averages.

WASM noted. Results indicate when WASM acceleration is active vs pure TypeScript.

Real pipeline. "Full frame" includes React reconciliation, layout, paint, and diff — not just one stage.

Reproducible. Run them yourself:

npx tsx examples/benchmarks.ts
npx tsx examples/benchmarks-extreme.ts