Skip to content

Performance and delivery evidence

Freshness-gated benchmark and package-size evidence for Sheetwrite.
On this page

Every number on this page comes from a validated local protocol artifact captured on a clean tree; nothing is published from an unvalidated or protocol-mismatched artifact. Every expected cell carries either a validated timing or its recorded failure - a run that did not complete is shown as a failure, never converted into a timing. Results come first; the capture, the regression method, and the memory API follow them.

Render benchmark

Validated evidence. 1120/1120 engine/scenario/round runs completed across 4 workbook sizes; every completed run passed its correctness checkpoints.

Both engines drive identical scripted interactions in a controlled browser. Pick a workbook size and a metric:

Captured
2026-10-17 05:18 UTC
Commit
49e05e298c3f clean worktree
Engines
Sheetwrite 0.5.0 release candidate · Handsontable 18.1.2
Environment
Bun 1.4.3 · Node compatibility 26.3.0 · Chromium 153.0.8010.12 · 12th Gen Intel(R) Core(TM) i9-12900H
Protocol
10 rounds · raw artifact bench/results/render-scale.json
interactionlog scale — every tick is 10× — shorter is fastermedianp95
Scroll down · top left14.6× faster
Sheetwrite1.07 ms1.15 ms
Handsontable15.6 ms15.8 ms
Scroll down · middle4.0× faster
Sheetwrite1.02 ms1.08 ms
Handsontable4.02 ms4.13 ms
Scroll smooth · same window21.4× faster
Sheetwrite0.39 ms0.42 ms
Handsontable8.28 ms8.34 ms
Scroll right · top left43.3× faster
Sheetwrite0.26 ms0.30 ms
Handsontable11.3 ms11.5 ms
Edit open · top left2.1× faster
Sheetwrite0.45 ms0.46 ms
Handsontable0.95 ms0.99 ms
Edit open · middle2.0× faster
Sheetwrite0.48 ms0.53 ms
Handsontable0.99 ms1.01 ms
Edit open · bottom right3.5× faster
Sheetwrite0.40 ms0.45 ms
Handsontable1.40 ms1.42 ms
Edit commit · middle6.6× faster
Sheetwrite0.70 ms0.75 ms
Handsontable4.60 ms4.87 ms
Altering · insert 5 rows top6.5× faster
Sheetwrite0.83 ms0.91 ms
Handsontable5.40 ms5.70 ms
Altering · remove 5 rows top6.5× faster
Sheetwrite0.89 ms1.01 ms
Handsontable5.74 ms5.89 ms
Arrow down · top left2.6× faster
Sheetwrite0.54 ms0.58 ms
Handsontable1.38 ms1.44 ms
Arrow right · middle2.3× faster
Sheetwrite0.56 ms0.58 ms
Handsontable1.25 ms1.32 ms
Formatted paint · top left9.1× faster
Sheetwrite0.37 ms0.39 ms
Handsontable3.38 ms3.57 ms
Merge heavy · paint1,112× faster
Sheetwrite4 µs4 µs
Handsontable4.02 ms4.09 ms
interactionrenderer heap after interaction — linear — shorter is leanerfootprintfaded = change
Scroll down · top left1.5× leaner
Sheetwrite10.0 MB0.0 MB
Handsontable14.9 MB2.9 MB
Scroll down · middle1.5× leaner
Sheetwrite10.5 MB0.9 MB
Handsontable15.3 MB0.0 MB
Scroll smooth · same window1.5× leaner
Sheetwrite11.0 MB0.4 MB
Handsontable16.4 MB3.7 MB
Scroll right · top left1.2× leaner
Sheetwrite12.4 MB2.2 MB
Handsontable15.4 MB0.0 MB
Edit open · top left1.7× leaner
Sheetwrite10.9 MB0.0 MB
Handsontable18.8 MB3.5 MB
Edit open · middle1.5× leaner
Sheetwrite11.1 MB0.7 MB
Handsontable17.0 MB1.3 MB
Edit open · bottom right1.9× leaner
Sheetwrite11.0 MB0.4 MB
Handsontable20.7 MB1.1 MB
Edit commit · middle1.9× leaner
Sheetwrite12.5 MB2.4 MB
Handsontable23.6 MB7.9 MB
Altering · insert 5 rows top1.9× leaner
Sheetwrite12.4 MB0.5 MB
Handsontable23.1 MB0.6 MB
Altering · remove 5 rows top2.0× leaner
Sheetwrite12.0 MB0.2 MB
Handsontable23.3 MB8.6 MB
Arrow down · top left2.1× leaner
Sheetwrite11.6 MB0.0 MB
Handsontable24.7 MB4.6 MB
Arrow right · middle1.8× leaner
Sheetwrite12.0 MB0.5 MB
Handsontable21.4 MB2.3 MB
Formatted paint · top left2.5× leaner
Sheetwrite13.2 MB1.9 MB
Handsontable32.4 MB11.1 MB
Merge heavy · paint1.4× leaner
Sheetwrite15.3 MB0.3 MB
Handsontable22.2 MB0.0 MB
interactionlog scale — every tick is 10× — shorter is fastermedianp95
Scroll down · top left14.2× faster
Sheetwrite1.10 ms1.14 ms
Handsontable15.6 ms15.6 ms
Scroll down · middle3.5× faster
Sheetwrite1.04 ms1.09 ms
Handsontable3.68 ms4.00 ms
Scroll smooth · same window20.9× faster
Sheetwrite0.40 ms0.41 ms
Handsontable8.28 ms8.33 ms
Scroll right · top left42.6× faster
Sheetwrite0.27 ms0.31 ms
Handsontable11.5 ms11.6 ms
Edit open · top left2.3× faster
Sheetwrite0.42 ms0.45 ms
Handsontable0.99 ms1.01 ms
Edit open · middle1.8× faster
Sheetwrite0.51 ms0.54 ms
Handsontable0.92 ms0.98 ms
Edit open · bottom right3.3× faster
Sheetwrite0.41 ms0.44 ms
Handsontable1.34 ms1.40 ms
Edit commit · middle6.4× faster
Sheetwrite0.72 ms0.75 ms
Handsontable4.60 ms5.09 ms
Altering · insert 5 rows top6.5× faster
Sheetwrite0.90 ms1.10 ms
Handsontable5.83 ms6.12 ms
Altering · remove 5 rows top6.4× faster
Sheetwrite0.92 ms1.02 ms
Handsontable5.83 ms6.03 ms
Arrow down · top left2.5× faster
Sheetwrite0.56 ms0.59 ms
Handsontable1.39 ms1.43 ms
Arrow right · middle2.4× faster
Sheetwrite0.55 ms0.58 ms
Handsontable1.33 ms1.54 ms
Formatted paint · top left9.3× faster
Sheetwrite0.37 ms0.39 ms
Handsontable3.48 ms3.56 ms
Merge heavy · paint1,050× faster
Sheetwrite4 µs4 µs
Handsontable3.76 ms3.79 ms
interactionrenderer heap after interaction — linear — shorter is leanerfootprintfaded = change
Scroll down · top left1.5× leaner
Sheetwrite11.0 MB0.0 MB
Handsontable16.8 MB4.1 MB
Scroll down · middle1.5× leaner
Sheetwrite11.3 MB0.9 MB
Handsontable17.3 MB0.9 MB
Scroll smooth · same window1.5× leaner
Sheetwrite12.3 MB1.3 MB
Handsontable18.8 MB1.4 MB
Scroll right · top left1.2× leaner
Sheetwrite13.4 MB2.3 MB
Handsontable15.6 MB0.5 MB
Edit open · top left1.5× leaner
Sheetwrite11.2 MB0.0 MB
Handsontable17.3 MB2.1 MB
Edit open · middle2.2× leaner
Sheetwrite10.7 MB0.4 MB
Handsontable23.7 MB9.1 MB
Edit open · bottom right2.1× leaner
Sheetwrite11.6 MB1.2 MB
Handsontable24.5 MB5.8 MB
Edit commit · middle1.4× leaner
Sheetwrite14.3 MB2.1 MB
Handsontable20.2 MB0.0 MB
Altering · insert 5 rows top3.3× leaner
Sheetwrite12.9 MB0.8 MB
Handsontable42.3 MB22.0 MB
Altering · remove 5 rows top4.0× leaner
Sheetwrite12.3 MB0.0 MB
Handsontable49.6 MB6.6 MB
Arrow down · top left3.3× leaner
Sheetwrite11.7 MB0.0 MB
Handsontable38.2 MB7.3 MB
Arrow right · middle3.7× leaner
Sheetwrite12.0 MB0.6 MB
Handsontable43.9 MB5.4 MB
Formatted paint · top left3.6× leaner
Sheetwrite14.2 MB2.8 MB
Handsontable51.7 MB5.1 MB
Merge heavy · paint2.3× leaner
Sheetwrite18.0 MB2.2 MB
Handsontable42.0 MB7.5 MB
interactionlog scale — every tick is 10× — shorter is fastermedianp95
Scroll down · top left13.8× faster
Sheetwrite1.12 ms1.18 ms
Handsontable15.5 ms15.8 ms
Scroll down · middle3.9× faster
Sheetwrite1.04 ms1.11 ms
Handsontable4.06 ms4.27 ms
Scroll smooth · same window20.7× faster
Sheetwrite0.40 ms0.41 ms
Handsontable8.28 ms8.33 ms
Scroll right · top left41.0× faster
Sheetwrite0.28 ms0.31 ms
Handsontable11.4 ms11.5 ms
Edit open · top left2.2× faster
Sheetwrite0.44 ms0.48 ms
Handsontable0.97 ms1.01 ms
Edit open · middle1.9× faster
Sheetwrite0.52 ms0.58 ms
Handsontable1.01 ms1.04 ms
Edit open · bottom right3.4× faster
Sheetwrite0.42 ms0.47 ms
Handsontable1.43 ms1.46 ms
Edit commit · middle7.0× faster
Sheetwrite0.73 ms0.80 ms
Handsontable5.08 ms5.19 ms
Altering · insert 5 rows top8.2× faster
Sheetwrite1.70 ms1.79 ms
Handsontable13.9 ms15.6 ms
Altering · remove 5 rows top8.9× faster
Sheetwrite1.42 ms1.69 ms
Handsontable12.7 ms13.6 ms
Arrow down · top left2.4× faster
Sheetwrite0.58 ms0.62 ms
Handsontable1.40 ms1.45 ms
Arrow right · middle2.3× faster
Sheetwrite0.58 ms0.62 ms
Handsontable1.32 ms1.37 ms
Formatted paint · top left8.7× faster
Sheetwrite0.40 ms0.43 ms
Handsontable3.52 ms3.61 ms
Merge heavy · paint931× faster
Sheetwrite4 µs4 µs
Handsontable3.39 ms3.67 ms
interactionrenderer heap after interaction — linear — shorter is leanerfootprintfaded = change
Scroll down · top left2.2× leaner
Sheetwrite20.8 MB0.0 MB
Handsontable45.4 MB2.7 MB
Scroll down · middle1.8× leaner
Sheetwrite17.3 MB0.7 MB
Handsontable31.8 MB0.0 MB
Scroll smooth · same window1.7× leaner
Sheetwrite22.2 MB5.3 MB
Handsontable37.3 MB9.0 MB
Scroll right · top left1.4× leaner
Sheetwrite22.3 MB5.2 MB
Handsontable32.0 MB0.4 MB
Edit open · top left2.2× leaner
Sheetwrite17.6 MB0.0 MB
Handsontable38.8 MB6.2 MB
Edit open · middle2.5× leaner
Sheetwrite17.7 MB0.8 MB
Handsontable45.2 MB4.9 MB
Edit open · bottom right2.4× leaner
Sheetwrite17.4 MB0.5 MB
Handsontable41.4 MB2.2 MB
Edit commit · middle2.5× leaner
Sheetwrite18.6 MB0.9 MB
Handsontable45.6 MB4.0 MB
Altering · insert 5 rows top7.0× leaner
Sheetwrite20.7 MB2.7 MB
Handsontable145.3 MB103.6 MB
Altering · remove 5 rows top8.9× leaner
Sheetwrite19.8 MB0.8 MB
Handsontable175.3 MB27.6 MB
Arrow down · top left5.9× leaner
Sheetwrite18.4 MB0.3 MB
Handsontable108.5 MB0.0 MB
Arrow right · middle5.7× leaner
Sheetwrite18.2 MB0.9 MB
Handsontable103.7 MB0.0 MB
Formatted paint · top left4.2× leaner
Sheetwrite21.0 MB2.2 MB
Handsontable88.7 MB4.3 MB
Merge heavy · paint3.5× leaner
Sheetwrite28.2 MB0.0 MB
Handsontable99.1 MB7.7 MB
interactionlog scale — every tick is 10× — shorter is fastermedianp95
Scroll down · top left12.7× faster
Sheetwrite1.22 ms1.24 ms
Handsontable15.4 ms15.7 ms
Scroll down · middle4.0× faster
Sheetwrite1.02 ms1.44 ms
Handsontable4.14 ms4.61 ms
Scroll smooth · same window20.7× faster
Sheetwrite0.39 ms0.40 ms
Handsontable8.11 ms8.28 ms
Scroll right · top left42.9× faster
Sheetwrite0.27 ms0.39 ms
Handsontable11.4 ms11.5 ms
Edit open · top left2.4× faster
Sheetwrite0.43 ms0.61 ms
Handsontable1.01 ms1.04 ms
Edit open · middle1.6× faster
Sheetwrite0.65 ms0.94 ms
Handsontable1.01 ms1.14 ms
Edit open · bottom right3.1× faster
Sheetwrite0.41 ms0.62 ms
Handsontable1.26 ms1.38 ms
Edit commit · middle5.8× faster
Sheetwrite0.82 ms0.99 ms
Handsontable4.75 ms4.80 ms
Altering · insert 5 rows top8.2× faster
Sheetwrite11.8 ms12.0 ms
Handsontable97.2 ms103 ms
Altering · remove 5 rows top7.2× faster
Sheetwrite11.8 ms12.0 ms
Handsontable84.5 ms89.6 ms
Arrow down · top left2.3× faster
Sheetwrite0.57 ms0.75 ms
Handsontable1.30 ms1.53 ms
Arrow right · middle2.4× faster
Sheetwrite0.55 ms0.79 ms
Handsontable1.31 ms1.33 ms
Formatted paint · top left7.1× faster
Sheetwrite0.48 ms0.55 ms
Handsontable3.40 ms3.46 ms
Merge heavy · paint950× faster
Sheetwrite4 µs4 µs
Handsontable3.40 ms3.45 ms
interactionrenderer heap after interaction — linear — shorter is leanerfootprintfaded = change
Scroll down · top left2.3× leaner
Sheetwrite105.5 MB0.0 MB
Handsontable240.7 MB20.6 MB
Scroll down · middle2.8× leaner
Sheetwrite75.0 MB0.0 MB
Handsontable207.9 MB0.0 MB
Scroll smooth · same window2.1× leaner
Sheetwrite98.7 MB23.7 MB
Handsontable206.3 MB0.0 MB
Scroll right · top left2.2× leaner
Sheetwrite93.4 MB0.0 MB
Handsontable205.1 MB10.0 MB
Edit open · top left2.7× leaner
Sheetwrite79.4 MB0.0 MB
Handsontable212.4 MB12.8 MB
Edit open · middle2.7× leaner
Sheetwrite73.4 MB0.0 MB
Handsontable201.0 MB5.3 MB
Edit open · bottom right2.6× leaner
Sheetwrite77.8 MB4.3 MB
Handsontable202.7 MB8.7 MB
Edit commit · middle2.5× leaner
Sheetwrite80.8 MB3.3 MB
Handsontable204.4 MB1.2 MB
Altering · insert 5 rows top11.3× leaner
Sheetwrite84.0 MB8.2 MB
Handsontable945.5 MB745.9 MB
Altering · remove 5 rows top6.9× leaner
Sheetwrite89.8 MB6.5 MB
Handsontable622.5 MB32.8 MB
Arrow down · top left7.2× leaner
Sheetwrite76.5 MB0.0 MB
Handsontable553.9 MB0.0 MB
Arrow right · middle7.0× leaner
Sheetwrite77.3 MB0.0 MB
Handsontable537.4 MB0.0 MB
Formatted paint · top left7.0× leaner
Sheetwrite79.5 MB0.7 MB
Handsontable557.7 MB17.3 MB
Merge heavy · paint4.6× leaner
Sheetwrite90.9 MB0.0 MB
Handsontable418.7 MB2.9 MB
Every bar in a panel shares the ruler's scale (speed is logarithmic - each tick is 10x), so lengths compare across rows as well as within them. Bright numbers are the median run; faded numbers are the p95 run (speed) or the interaction's heap delta (memory). Rows marked as not completed are runs the engine could not finish - the recorded failure (crash, timeout, or failed correctness checkpoint) lives in the raw artifact.
Methodology - what each scenario does

Live grid in controlled headless Chromium. Ten counterbalanced rounds; all fourteen scenarios run warm per mount, and the fixture is rebuilt after any failure so crashes cannot leak state. Every scenario must prove its effect (scroll really moved, editor really opened, rows really changed) or it fails.

Each measured sample repeats one logical action until it covers at least 100 ms of measured time, so timer resolution and scheduler jitter cannot decide a sample. Every raw sample is retained in the artifact.

Bright = median round, faded = p95 round. Did not complete = recorded crash, timeout, or failed checkpoint - never a timing. The failure table names the stage and the recorded error behind every missing cell.

scroll-down.top-left
From the origin, jump-scroll 50 px down: a fresh row band enters the viewport and must paint.
scroll-down.middle
The same 50 px jump starting from the vertical middle of the scroll range.
scroll-smooth.same-window
Scroll 1 px without changing the visible row window: pure repaint cost, zero new data.
scroll-right.top-left
Jump-scroll 50 px right: a fresh column band paints.
edit-open.top-left
Select a cell near the origin and open its editor.
edit-open.middle
Open the editor on the center cell of the workbook.
edit-open.bottom-right
Open the editor on the last row and column - the far end of every index.
edit-commit.middle
Commit a typed value into the center cell and paint the result.
altering.insert-5-rows-top
Insert five rows at the top: every following row reindexes.
altering.remove-5-rows-top
Remove those five rows again - the inverse reindex.
arrow-down.top-left
Move the selection one cell down with the arrow key, including the selection overlay repaint.
arrow-right.middle
Arrow-key selection move at the workbook center.
formatted-paint.top-left
Repaint a viewport dense with per-cell formatting.
merge-heavy.paint
Repaint a viewport dense with merged ranges; Sheetwrite additionally proves it builds exactly one merge revision index.

Reproduce and validate with:

Controlled render evidence
bun run --filter @sheetwrite/bench bench:render:prepare
bun run --filter @sheetwrite/bench bench:render:scale

Data engine benchmark

Validated evidence. Head-to-head store operations at the sizes both engines complete headlessly; Sheetwrite additionally scales to 1M rows below.
Captured
2026-10-17 05:24 UTC
Commit
49e05e298c3f clean worktree
Engines
Sheetwrite 0.5.0 release candidate · Handsontable 18.1.2
Environment
Bun 1.4.3
Raw artifact
bench/results/data-results.json
operationlog scale — every tick is 10× — shorter is fastermedianp95
Ingest440× faster
Sheetwrite0.56 ms0.62 ms
Handsontable248 ms268 ms
Window read17.8× faster
Sheetwrite6 µs0.01 ms
Handsontable0.11 ms0.12 ms
Edit32.8× faster
Sheetwrite4.46 ms5.83 ms
Handsontable146 ms217 ms
Sort728× faster
Sheetwrite0.16 ms0.21 ms
Handsontable114 ms129 ms
Filter2,258× faster
Sheetwrite0.02 ms0.05 ms
Handsontable45.4 ms50.8 ms
Aggregate17.1× faster
Sheetwrite5 µs5 µs
Handsontable0.09 ms0.11 ms
operationlog scale — every tick is 10× — shorter is fastermedianp95
Ingest479× faster
Sheetwrite4.87 ms6.03 ms
Handsontable2.33 s4.26 s
Window read16.2× faster
Sheetwrite7 µs0.01 ms
Handsontable0.12 ms0.13 ms
Edit150× faster
Sheetwrite3.57 ms4.84 ms
Handsontable535 ms1.18 s
Sort11,379× faster
Sheetwrite0.22 ms0.25 ms
Handsontable2.48 s2.57 s
Filter25,557× faster
Sheetwrite0.04 ms0.04 ms
Handsontable908 ms1.48 s
Aggregate20.3× faster
Sheetwrite0.04 ms0.04 ms
Handsontable0.77 ms0.87 ms

Sheetwrite alone at scale — Handsontable cannot complete these sizes headlessly:

operationlog scale — every tick is 10× — shorter is fastermedianp95
Ingest
100k rows53.6 ms54.5 ms
500k rows284 ms285 ms
1M rows565 ms568 ms
Window read
100k rows8 µs0.01 ms
500k rows0.01 ms0.01 ms
1M rows0.01 ms0.01 ms
Edit
100k rows4.19 ms5.02 ms
500k rows3.84 ms4.18 ms
1M rows3.94 ms4.34 ms
Sort
100k rows2.59 ms2.83 ms
500k rows12.6 ms13.1 ms
1M rows26.9 ms26.9 ms
Filter
100k rows0.40 ms0.42 ms
500k rows1.88 ms1.92 ms
1M rows3.59 ms3.72 ms
Aggregate
100k rows0.42 ms0.45 ms
500k rows1.90 ms1.97 ms
1M rows4.28 ms4.39 ms
Methodology - what each operation does

Identical columnar datasets, per-operation warmup and iteration plans. Fresh instance per ingest; window reads rotate offsets to defeat caches; sort and filter reset between runs; Handsontable edits run with rendering suspended so only its data path is timed.

Memory = JS-heap delta around one ingest in an isolated subprocess, plus Sheetwrite's WASM linear-memory delta.

ingest
Load the full dataset into a fresh engine instance.
windowRead
Read a 50x5 cell window at a rotating offset that sweeps the whole sheet.
edit
1,000 single-cell edits.
sort
Sort by the numeric amount column.
filter
Substring filter over the city column.
aggregate
Numeric aggregation over the amount column.

Reproduce with:

Data engine evidence
bun run --filter @sheetwrite/bench bench:data

Core write path

Validated evidence. 12 store and document scenarios at production sizes. Every scenario passed its own correctness checkpoint before its timing was accepted.
Grid.applyTransaction of a 1,000,000-cell setBlock
489 ms
fromCsv import of 200,000 rows by 10 columns
235 ms
Undo of a 1,000,000-cell clearRange
1.61 s
Cold initSheetwrite() through first store in a fresh Node process (7 processes)
4.24 ms
All measurements
scenariolog scale — every tick is 10× — shorter is fastermedianp95
Row bridge insertaddRows projection of 10,000 rows into a 1,000,000-row bridge
5.95 ms6.30 ms
Command state select4,096-cell range selection on a framework-style grid
6.71 ms8.84 ms
With command state handlerVariant of command state select, measured in the same run
6.71 ms8.84 ms
Without command state handlerVariant of command state select, measured in the same run
5.07 ms5.75 ms
Setblock admissionGrid.applyTransaction of a 1,000,000-cell setBlock
489 ms503 ms
Setblock detailed captureGrid.applyTransaction of a 1,000,000-cell setBlock observed through a change listener with detailed capture
564 ms568 ms
Sync offline queueGrid commit admitted into an offline sync queue with no listeners
0.67 ms0.80 ms
Rebase largeRebase of a 100,000-cell block across 50 single-row inserts
1.54 ms1.54 ms
Validation sparse100,000-cell sparse setRange with 100 rules outside the range
169 ms169 ms
Csv importfromCsv import of 200,000 rows by 10 columns
235 ms235 ms
Undo large clearUndo of a 1,000,000-cell clearRange
1.61 s1.62 s
Restore near ceilingrestoreBlock with 60,000,044 decoded bytes
1.06 s1.07 s
Paged evicted revisitRevisit of an evicted paged band: scroll back plus a fixed async settle floor (wall time is not the accept metric)
5.86 ms6.97 ms
Node cold initCold initSheetwrite() through first store in a fresh Node process (7 processes)
4.24 ms4.58 ms
Captured
2026-10-17 05:24 UTC
Commit
49e05e298c3f clean worktree
Environment
Bun 1.4.3 · Node compatibility v26.3.0 · 12th Gen Intel(R) Core(TM) i9-12900H
Raw artifact
bench/results/core-paths-results.json

Scale

ParameterValue
bridgeRows1,000,000
bridgeInsertRows10,000
selectionRows64
selectionColumns64
blockRows1,000
blockColumns1,000
offlineCommits5,000
rebaseBlockRows1,000
rebaseBlockColumns100
rebaseInsertOperations50
sparseCells100,000
validationRules100
csvRows200,000
csvColumns10
clearRows1,000
clearColumns1,000
pagedRows200,000

Samples

ScenarioSamples
row-bridge-insert2
command-state-select10
with-command-state-handler10
without-command-state-handler10
setblock-admission3
setblock-detailed-capture3
sync-offline-queue5000
rebase-large3
validation-sparse3
csv-import2
undo-large-clear3
restore-near-ceiling3
paged-evicted-revisit3
node-cold-init6

Counters

Non-time evidence recorded by the same runs.

ScenarioCounterValue
row-bridge-insertbridgeRows1,000,000
row-bridge-insertinsertedRows10,000
command-state-selectselectedCells4,096
command-state-selectcommandStateEvents13
command-state-selectwithoutHandlerMedianMs5.073
setblock-admissionblockCells1,000,000
setblock-admissiontransactions4
setblock-detailed-captureblockCells1,000,000
setblock-detailed-capturetransactions4
setblock-detailed-capturecapturedChanges1,000,000
setblock-detailed-capturevalidatedCapturedChanges1,000,000
sync-offline-queuequeuedCommits5,005
sync-offline-queueadmittedOperations5,005
rebase-largeblockCells100,000
rebase-largeinsertOperations50
rebase-largeverifiedValues100,000
validation-sparsewrittenCells100,000
validation-sparselogicalRangeCells1,000,000
validation-sparsevalidationRules100
csv-importimportedRows200,000
csv-importimportedColumns10
csv-importverifiedCells2,000,000
csv-importinputChars20,888,985
undo-large-clearclearedCells1,000,000
undo-large-clearclears4
undo-large-clearrestoredCells1,000,000
undo-large-clearcompactRestores5
undo-large-clearpeakRssBytes1,143,246,848
restore-near-ceilingrestoredCells4,000,000
restore-near-ceilingdecodedBytes60,000,044
restore-near-ceilingpeakRssBytes1,509,625,856
paged-evicted-revisitrevisitedRows3
paged-evicted-revisitareColumnsFullyLoadedCalls108
paged-evicted-revisitevictionVisits3
paged-evicted-revisitpagedRows200,000
node-cold-initlargestHeartbeatGapMs3.73
node-cold-initminimalHeartbeatTicks1
node-cold-initfreshProcesses7

Correctness checkpoints

ScenarioCheckpoint
row-bridge-insertprojected delta is one row-structure insert with every created identity present in the bridge in order
command-state-selectselection covers 4096 cells and the command-state handler observes one event per selection
setblock-admissionevery setBlock is applied and every cell of the block holds the last written value
setblock-detailed-captureevery setBlock is applied and every cell of the block holds the last written value, and its change capture reports every cell's address, previous value, and new value exactly once
sync-offline-queueevery commit is applied, the queue holds every commit at server version 0, and capacity stays available
rebase-largethe block rebases without conflict, shifts down by exactly the inserted row count, keeps every source value, and leaves the caller's operations untouched
validation-sparseevery write is applied, probe cells hold the last written values, every rule is retained, and a rule violation is still rejected
csv-importrow count matches and every imported cell equals the source value for its row and column, including declared number/text types
undo-large-clearthe clear empties the block and the undos restore every original cell value
restore-near-ceilinga valid near-ceiling compressed restore applies its complete block
paged-evicted-revisitthe band is unloaded before each revisit, the revisit reloads it with the source values, and the revisit asks areColumnsFullyLoaded at least once; areColumnsFullyLoadedCalls per revisit is the accept metric
node-cold-initevery fresh process loads the module, produces the expected engine result, and reports a plausible heartbeat gap

Reproduce with:

Core write-path evidence
bun run --filter @sheetwrite/bench bench:core-paths

Formula engine benchmark

Validated evidence. 53 recalculation workloads across dependency shapes; every workload passed the protocol's safety ceilings.
Captured
2026-10-17 06:43 UTC
Commit
49e05e298c3f clean worktree
Raw artifact
bench/results/formula-results.json
workloadlog scale — every tick is 10× — shorter is fastermedianp95
Countif 100k
100,000 cells1.13 ms1.16 ms
Criteria multi range edit
100,000 cells5.61 ms5.76 ms
Criteria range edit
100,000 cells1.65 ms1.67 ms
Cross sheet range edit
1,000 cells0.52 ms0.54 ms
Cycles
1,000 cells1.54 ms1.58 ms
Diamond edit
32 cells0.11 ms0.12 ms
Distinct range edit
1,000 cells0.01 ms0.02 ms
Error propagation
1,000 cells0.81 ms1.30 ms
Formula constant edit
100,000 cells0.03 ms0.04 ms
Incremental dependency closure edit
100,000 cells0.02 ms0.04 ms
Independent first recompute
1,000 cells1.15 ms1.17 ms
10,000 cells5.18 ms5.47 ms
100,000 cells88.6 ms90.0 ms
Independent parse load
1,000 cells1.07 ms1.27 ms
10,000 cells6.24 ms6.98 ms
100,000 cells62.8 ms64.8 ms
Iterative finance
1,000 cells3.72 ms3.86 ms
Let repeat
100,000 cells1.97 ms2.02 ms
Let reuse edit
100,000 cells0.67 ms0.68 ms
Linear chain
8 cells0.02 ms0.04 ms
16 cells0.03 ms0.03 ms
32 cells0.05 ms0.05 ms
64 cells0.08 ms0.09 ms
Lookup range edit
100,000 cells1.70 ms1.80 ms
Percentile covariance
100,000 cells3.71 ms3.80 ms
Removed sheet ref
1,000 cells0.50 ms0.78 ms
Shared range edit
1,000 cells0.55 ms0.59 ms
Spill filter resize
100,000 cells25.3 ms26.3 ms
Spill sequence admission
100,000 cells13.4 ms13.6 ms
Sum multi range
100,000 cells4.45 ms4.97 ms
Sumifs 3 criteria
100,000 cells7.59 ms7.94 ms
Sumproduct matrix edit
100,000 cells1.95 ms1.97 ms
Sumproduct vector edit
100,000 cells2.11 ms2.24 ms
Text lookup many
1,000 cells2.43 ms4.09 ms
Text lookup many edit
1,000 cells2.26 ms2.39 ms
Text lookup many sorted
1,000 cells3.83 ms3.88 ms
Text lookup many sorted edit
1,000 cells3.71 ms3.78 ms
Topology remove add
10,000 cells9.50 ms9.70 ms
Unicode text date edit
1,000 cells1.17 ms1.30 ms
Vlookup many
1,000 cells1.28 ms1.35 ms
Vlookup many edit
1,000 cells1.13 ms1.16 ms
Vlookup many sorted
1,000 cells1.65 ms1.66 ms
Vlookup many sorted edit
1,000 cells1.52 ms1.54 ms
Wide fan out edit
1,000 cells0.38 ms0.40 ms
100,000 cells60.4 ms62.1 ms
Xlookup many
1,000 cells1.28 ms1.33 ms
Xlookup many edit
1,000 cells1.14 ms1.20 ms
Xlookup many sorted
1,000 cells1.83 ms1.98 ms
Xlookup many sorted edit
1,000 cells1.76 ms1.78 ms
Scalar edit affects
0 affected3 µs4 µs
1 affected4 µs9 µs
1,000 affected0.37 ms0.38 ms
100,000 affected60.4 ms63.6 ms
Methodology - what each workload does

Each workload builds a fresh WASM cell-store of the named dependency shape and times the recalculation from one action - usually a single edit. Sizes are formula-cell counts; bright = median, faded = p95.

linear-chain
A chain A1 -> A2 -> ... -> AN; editing the head recomputes the full depth.
wide-fan-out-edit
One scalar feeds N dependent formulas; edit the scalar.
diamond-edit
Fan-out that reconverges (diamond graph); edit the apex.
shared-range-edit
N formulas aggregate one shared range; edit one cell inside it.
distinct-range-edit
Each formula owns its own range; one edit recomputes only its owner.
cross-sheet-range-edit
Summary-sheet formulas range over another sheet; edit the source.
scalar-edit-affects-N
One scalar edit invalidating exactly N dependents; N=0 touches an unrelated cell, so it prices pure dependency lookup.
topology-remove-add
Remove and re-add rows so the dependency graph itself changes shape.
cycles
Introduce a reference cycle; detection and cycle-error propagation.
removed-sheet-ref
Formulas referencing a deleted sheet must all degrade to reference errors.
error-propagation
An error value (=1/0) flows through every dependent.
criteria-range-edit
Criteria-style aggregation (SUMIF shape) over 100k cells; edit inside the criteria range.
lookup-range-edit
Lookup-shape formulas over 100k cells; edit inside the looked-up range.

Reproduce with:

Formula engine evidence
bun run --filter @sheetwrite/bench bench:formula

Choose a formula engine

Shared functions use the same implementation in both builds. Choose by function coverage and download size, not small timing differences.

Default engine

@sheetwrite/wasm

Core spreadsheet functions, with the smaller download.

250.1 KiBBrotli WASM · 856.4 KiB uncompressed
Full engine

@sheetwrite/formulas

Default functions, plus statistics, finance, LAMBDA, grouping, and other analysis functions.

336.2 KiBBrotli WASM · 1.1 MiB uncompressed

Browse functions by engine · Use the full engine

All measurements

Analysis workloads

Each workload has 1,000 formulas. It has one untimed warmup and five measured samples. Each sample uses a fresh store. The timer measures first recompute. Setup and output reads are outside the timer. Every result is read and summed as a checked checksum. The default engine does not run these analysis functions.

LINEST, SORTBY, and MAP/REDUCE use 100 input rows. DSUM uses 100 database rows, a header, and the criterion key > 0. XIRR uses two cash flows one year apart. The text workload uses three fields. MODE.MULT uses five scalar inputs. MMULT uses two 2-by-2 matrices. Array outputs are reduced to scalar results.

analysis workloadlog scale — every tick is 10× — shorter is fastermedianp95
Linest
1,000 formulas4.66 ms7.70 ms
Xirr
1,000 formulas1.54 ms1.60 ms
Dsum
1,000 formulas2.98 ms4.16 ms
Textsplit regexreplace
1,000 formulas3.07 ms3.25 ms
Sortby
1,000 formulas3.49 ms7.23 ms
Mode mult
1,000 formulas1.03 ms1.31 ms
Map reduce
1,000 formulas26.7 ms26.9 ms
Mmult
1,000 formulas1.36 ms1.43 ms

Brotli size uses quality 11 on the WASM file only. It is not the full package transfer size. The raw artifacts retain cold-initialization samples; those samples do not establish that one engine initializes faster.

Raw evidence: bench/results/full-engine-matched-results.json and bench/results/full-engine-results.json. They retain time samples, output checks, allocation samples, source hashes, and WASM hashes. The full workload captures are bench/results/formula-default-results.json and bench/results/formula-full-results.json. Their baseline gates stay blocked in output mode. A valid capture is not a passed timing regression gate.

Analysis capture
2026-10-17 06:44 UTC
Analysis commit
49e05e298c3f clean worktree

Shared-workload captures are validation data, not evidence that one package is faster.

Reproduce after bun run build:packages:

Default and full engine evidence
cd bench
bun run bench:formula:engines
bun run bench:formula:matched

XLSX codec benchmark

Validated evidence. 5 compared operations over 5 process-isolated rounds each, from one checked corpus fixture.

Current and baseline are the same codec at different commits, measured in the same session. Baseline commit: 87fadb723979.

Operation time

operationlog scale — every tick is 10× — shorter is fastermedianp95
Export rich
Current218 ms225 ms
Baseline317 ms328 ms
Export scalar
Current301 ms308 ms
Baseline385 ms395 ms
Export shared style
Current535 ms539 ms
Baseline724 ms743 ms
Export sparse
Current27.5 ms30.6 ms
Baseline102 ms111 ms
Import combined
Current913 ms941 ms
Baseline5.03 s5.13 s
Reject compression ratio
Current1.38 ms2.18 ms
Reject deep xml
Current4.78 ms5.27 ms

Peak resident memory

operationlinear scale — shorter uses less memorymedianp95
Export richMaximum peak RSS: current 151.4 MB · baseline 188.4 MB
Current142.2 MiB144.4 MiB
Baseline177.1 MiB179.5 MiB
Export scalarMaximum peak RSS: current 165.3 MB · baseline 217.2 MB
Current157.3 MiB157.6 MiB
Baseline206.4 MiB207.1 MiB
Export shared styleMaximum peak RSS: current 163.0 MB · baseline 307.6 MB
Current154.9 MiB155.4 MiB
Baseline290.7 MiB293.3 MiB
Export sparseMaximum peak RSS: current 97.0 MB · baseline 120.1 MB
Current91.6 MiB92.5 MiB
Baseline113.3 MiB114.5 MiB
Import combinedMaximum peak RSS: current 329.5 MB · baseline 3642.2 MB
Current311.6 MiB313.8 MiB
Baseline3,366.1 MiB3,463.9 MiB
Reject compression ratioMaximum peak RSS: current 49.4 MB
Current46.5 MiB47.0 MiB
Reject deep xmlMaximum peak RSS: current 52.1 MB
Current49.0 MiB49.5 MiB
All measurements
Captured
2026-10-17 08:11 UTC
Commit
49e05e298c3f clean worktree
Engines
Sheetwrite 0.5.0 release candidate · ExcelJS 4.4.0 at baseline 87fadb723979
Fixture
bench/fixtures/xlsx-codec-corpus.xlsx · 1,038,346 bytes · 2633e51a4a3c · LibreOffice 26.2.4.2 Calc MS Excel 2007 XML
Environment
Bun 1.4.3 · Node v26.3.0 · 12th Gen Intel(R) Core(TM) i9-12900H
Raw artifact
bench/results/xlsx-results.json

Reproduce with a checkout of the baseline commit, built and passed as the comparison root:

XLSX codec evidence
git worktree add ../sheetwrite-xlsx-baseline 87fadb72397947ea8c6ba576682925c7879579b8
cd ../sheetwrite-xlsx-baseline && bun install --frozen-lockfile && bun run build:packages
cd - && bun run --filter @sheetwrite/bench bench:xlsx -- --baseline-root ../sheetwrite-xlsx-baseline

Delivery size

Registry measurements for every published release. Each delta is measured against the release immediately before it.

v0.5.3v0.5.4
PackageTarballInstalled
@sheetwrite/wasm352.8 KiB0 B+0.0%1002.8 KiB0 B+0.0%
@sheetwrite/formulas471.2 KiB-9 B-0.0%1.26 MiB0 B+0.0%
@sheetwrite/core347.9 KiB-1.4 KiB-0.4%1.62 MiB-7.5 KiB-0.5%
@sheetwrite/xlsx52.3 KiB-229 B-0.4%227.4 KiB-1,012 B-0.4%
@sheetwrite/react5.9 KiB-27 B-0.4%18.5 KiB-68 B-0.4%
@sheetwrite/vue6.9 KiB-30 B-0.4%21.5 KiB-68 B-0.3%
@sheetwrite/svelte5.9 KiB0 B+0.0%16.3 KiB0 B+0.0%
v0.5.2v0.5.3
PackageTarballInstalled
@sheetwrite/wasm352.8 KiB+6.5 KiB+1.9%1002.8 KiB+39.4 KiB+4.1%
@sheetwrite/formulas471.2 KiB+7.2 KiB+1.5%1.26 MiB+39.4 KiB+3.1%
@sheetwrite/core349.3 KiB+34.0 KiB+10.8%1.62 MiB+413.0 KiB+33.1%
@sheetwrite/xlsx52.5 KiB+7.7 KiB+17.1%228.4 KiB+62.6 KiB+37.7%
@sheetwrite/react5.9 KiB+296 B+5.1%18.6 KiB+2.8 KiB+17.4%
@sheetwrite/vue6.9 KiB+253 B+3.7%21.5 KiB+2.8 KiB+15.1%
@sheetwrite/svelte5.9 KiB+20 B+0.3%16.3 KiB+40 B+0.2%
v0.5.1v0.5.2
PackageTarballInstalled
@sheetwrite/wasm346.3 KiB-5.9 KiB-1.7%963.4 KiB-39.2 KiB-3.9%
@sheetwrite/formulas464.0 KiB-7.1 KiB-1.5%1.22 MiB-40.0 KiB-3.1%
@sheetwrite/core315.3 KiB-33.5 KiB-9.6%1.22 MiB-411.7 KiB-24.8%
@sheetwrite/xlsx44.8 KiB-7.7 KiB-14.6%165.9 KiB-62.6 KiB-27.4%
@sheetwrite/react5.6 KiB-276 B-4.6%15.9 KiB-2.7 KiB-14.6%
@sheetwrite/vue6.7 KiB-234 B-3.3%18.7 KiB-2.8 KiB-13.0%
@sheetwrite/svelte5.9 KiB0 B+0.0%16.2 KiB0 B+0.0%
v0.5.0v0.5.1
PackageTarballInstalled
@sheetwrite/wasm352.2 KiB+1.0 KiB+0.3%1002.6 KiB+5.5 KiB+0.6%
@sheetwrite/formulas471.1 KiB+908 B+0.2%1.26 MiB+5.6 KiB+0.4%
@sheetwrite/core348.8 KiB+8.4 KiB+2.5%1.62 MiB+29.8 KiB+1.8%
@sheetwrite/xlsx52.5 KiB+145 B+0.3%228.4 KiB+204 B+0.1%
@sheetwrite/react5.9 KiB+176 B+3.0%18.6 KiB+1.4 KiB+7.9%
@sheetwrite/vue6.9 KiB+250 B+3.7%21.5 KiB+920 B+4.4%
@sheetwrite/svelte5.9 KiB+287 B+5.0%16.2 KiB+974 B+6.2%
v0.4.0v0.5.0
PackageTarballInstalled
@sheetwrite/wasm351.1 KiB+63.2 KiB+21.9%997.1 KiB+204.3 KiB+25.8%
@sheetwrite/formulas470.2 KiB1.26 MiB
@sheetwrite/core340.5 KiB+38.3 KiB+12.7%1.59 MiB+175.1 KiB+12.0%
@sheetwrite/xlsx52.4 KiB-33.1 KiB-38.7%228.2 KiB-201.0 KiB-46.8%
@sheetwrite/react5.7 KiB-3.4 KiB-37.5%17.2 KiB-14.7 KiB-46.1%
@sheetwrite/vue6.7 KiB-3.5 KiB-34.2%20.6 KiB-15.1 KiB-42.4%
@sheetwrite/svelte5.6 KiB-613 B-9.6%15.3 KiB-3.2 KiB-17.2%
v0.3.1v0.4.0
PackageTarballInstalled
@sheetwrite/wasm287.9 KiB+390 B+0.1%792.8 KiB-2.8 KiB-0.3%
@sheetwrite/core302.1 KiB-215.3 KiB-41.6%1.42 MiB-1.29 MiB-47.6%
@sheetwrite/xlsx85.4 KiB-60 B-0.1%429.2 KiB-356 B-0.1%
@sheetwrite/react9.2 KiB0 B+0.0%31.9 KiB0 B+0.0%
@sheetwrite/vue10.1 KiB-2 B-0.0%35.7 KiB0 B+0.0%
@sheetwrite/svelte6.2 KiB0 B+0.0%18.5 KiB0 B+0.0%
v0.3.0v0.3.1
PackageTarballInstalled
@sheetwrite/wasm287.6 KiB0 B+0.0%795.6 KiB0 B+0.0%
@sheetwrite/core517.4 KiB+11 B+0.0%2.71 MiB+24 B+0.0%
@sheetwrite/xlsx85.5 KiB0 B+0.0%429.6 KiB0 B+0.0%
@sheetwrite/react9.2 KiB0 B+0.0%31.9 KiB0 B+0.0%
@sheetwrite/vue10.1 KiB0 B+0.0%35.7 KiB0 B+0.0%
@sheetwrite/svelte6.2 KiB0 B+0.0%18.5 KiB0 B+0.0%
v0.2.0v0.3.0
PackageTarballInstalled
@sheetwrite/wasm287.6 KiB+107.1 KiB+59.4%795.6 KiB+305.6 KiB+62.4%
@sheetwrite/core517.4 KiB+145.0 KiB+38.9%2.71 MiB+821.8 KiB+42.0%
@sheetwrite/xlsx85.5 KiB+10.5 KiB+14.0%429.6 KiB+63.5 KiB+17.3%
@sheetwrite/react9.2 KiB+2.2 KiB+31.2%31.9 KiB+9.9 KiB+45.0%
@sheetwrite/vue10.1 KiB+1.5 KiB+17.7%35.7 KiB+7.2 KiB+25.3%
@sheetwrite/svelte6.2 KiB+1,002 B+18.7%18.5 KiB+3.9 KiB+27.1%
v0.1.0v0.2.0
PackageTarballInstalled
@sheetwrite/wasm180.4 KiB+8.2 KiB+4.7%489.9 KiB+22.3 KiB+4.8%
@sheetwrite/core372.4 KiB+40.0 KiB+12.0%1.91 MiB+209.9 KiB+12.0%
@sheetwrite/xlsx75.0 KiB+53.7 KiB+252.2%366.1 KiB+273.1 KiB+293.7%
@sheetwrite/react7.0 KiB+30 B+0.4%22.0 KiB+134 B+0.6%
@sheetwrite/vue8.6 KiB-24 B-0.3%28.5 KiB-5.5 KiB-16.3%
@sheetwrite/svelte5.2 KiB+66 B+1.2%14.5 KiB+296 B+2.0%
v0.1.0
PackageTarballInstalled
@sheetwrite/wasm172.3 KiB467.6 KiB
@sheetwrite/core332.5 KiB1.71 MiB
@sheetwrite/xlsx21.3 KiB93.0 KiB
@sheetwrite/react7.0 KiB21.9 KiB
@sheetwrite/vue8.6 KiB34.0 KiB
@sheetwrite/svelte5.2 KiB14.2 KiB
IncreaseDecreasescripts/size-history.json

Release retained engine memory

WebAssembly linear memory grows but does not shrink after cells or stores are cleared. When your application is idle after a large import, clear, or workbook disposal, call releaseEngineMemory explicitly. Sheetwrite never schedules a release automatically.

Explicit idle memory release
import { releaseEngineMemory } from "@sheetwrite/core";
const report = await releaseEngineMemory();

The call waits for synchronous transactions and active datasource page loads, then moves all live stores together into a fresh instance of the selected engine. Cell sources, styles, cached formula results (including volatile results), resident datasource pages, unsaved dirty edits, and compact undo/redo history survive. Unloaded clean pages remain unloaded and can be fetched again from the datasource. Worker renderers rebuild their frames without document-change events.

The main-thread pause scales with resident cells and compact history, so do not invoke it on an interaction-critical path. Migration temporarily needs both instances and packed transfer buffers; it can increase peak memory before garbage collection reclaims the retired instance. If preparation fails, every live store stays on the original instance. Concurrent calls share the same release.

report.wasmBytesBefore and report.wasmBytesAfter are committed linear-memory bytes, not live-cell bytes or process memory. report.pauseMs measures migration, excluding the idle wait. A release is not guaranteed to reduce committed bytes: surviving data, compact history, and migration scratch can still require substantial memory. Actual process-memory reclamation depends on garbage collection.

Capture

Captured
2026-10-17 05:18 → 2026-10-17 08:11 UTC
Commit
49e05e298c3f clean worktree
Artifacts
7 validated captures from that one commit; each section states its own environment (browser or runtime) and protocol

bench:release captures render-scale, the performance showcase's paged storage at bench/results/paged-results.json, the interaction evidence at bench/results/interaction-results.json, data, core paths, formula, formula engines, matched engines, and XLSX, then re-records the render regression baseline last. Its smoke rehearsal writes only to bench/results/smoke/.

The interaction capture (bench:interaction, schema interaction-current-v2) measures the current build, not a comparison: packed inverse-index lookup latency in nanoseconds, inverse-index backing bytes, retained bytes after 100 distant edits, and the cold-route long task owned by Sheetwrite. It records the raw samples, the summary derived from them, the runner, and the method, and it holds those four metrics to absolute release ceilings. Version 1 recorded a one-off before/after pair whose baseline build cannot be re-measured at a later release, so no old sample is carried forward into the current capture.

Matched local regression check

Timing comparisons run deliberately on a controlled local machine, not as a required CI job. Capture ten fresh matched rounds, retain every raw sample, and compare the fresh artifact with the committed baseline. Any unapproved slowdown fails the local command.

Zero-regression benchmark
bun run --filter @sheetwrite/bench bench:render:prepare
cd bench
bun run src/render-driver.ts --rounds 10 --output results/render-fresh.json --markdown-output results/render-fresh.md
bun run src/check.ts --baseline results/render-baseline.json --fresh results/render-fresh.json --power-mode balanced --concurrency 1

A result is a regression decision only when that final baseline check passes on the declared power mode and concurrency. bench:verify remains a smoke and safety-ceiling check.

The committed baseline at bench/results/render-baseline.json pins the harness source hashes, the declared sampling, and the runner (OS, CPU, Bun, Node, Chromium, power mode, concurrency). It is therefore machine- and harness-pinned: it must be re-recorded whenever a harness file, the sampling flags, the protocol version, or the runner changes, and the release capture does that as its last step by re-recording ten controlled rounds into bench/results/render-baseline-raw.json and promoting that artifact over the committed baseline. Until the re-recorded baseline is committed, bench:check fails closed on the fingerprint mismatch instead of comparing unrelated measurements.

Runtime resource ownership

The performance showcase at /showcases/performance#resource-ownership renders the public RuntimeResourceSnapshot protocol directly. It keeps exclusive-owner logical bytes, allocated capacity, WASM committed pages, and independent browser runtime observations in separate buckets; committed pages are never summed into live payload. Bulk-edit before/settled deltas use diffRuntimeResourcePhases and omit unchanged owners.

The UI and this reference share RUNTIME_RESOURCE_SCHEMA_VERSION. Detailed peak phases remain in the validated benchmark artifact rather than being presented as measurements from the visitor's browser.

String collection reclaims obsolete pool text and densely remaps surviving IDs. Automatic collection runs only at committed edit transaction barriers, never during painting or page loading, when dead arena/span storage exceeds 256 KiB and the surviving arena/span storage. Growth-based backoff avoids repeatedly scanning unchanged live data. Bulk clears and sheet removal request a new threshold check at the next commit.

compactStringStorage() explicitly collects unreachable text and trims growth slack outside an active mutation/page load. Raw IDs are valid only within stringPoolEpoch(); retain the epoch with projections and invalidate caches on a change, as described in the worker rendering guide. History snapshots own captured text. Persist resolved values, not pool IDs. Reclaimed Rust ownership does not imply that WebAssembly committed linear memory shrinks.

Reproduce and validate the full owner/operation matrix with:

Runtime resource evidence
bun run --filter @sheetwrite/bench bench:resource
Documentation