SVG DOM Update Benchmark

This page measures how fast the browser can show each new frame of an SVG component. It runs the component directly, records sequences of real frames, and replays each sequence through eight ways to put SVG output into the page.

<qip-play svg-inline> currently parses each frame with DOMParser and swaps the whole tree with replaceChildren. morphdom and React instead patch the existing nodes, so text selection, hover state and CSS transitions on unchanged elements survive a render. This page shows what that costs or saves.

Pick a component:

ComponentElements per frameScenarios
OpenAI vs Anthropic ARR chartabout 80Scale tween, hover sweep, arrow keys
The same chart, keyed by partabout 100Scale tween, hover sweep, arrow keys
Orders data grid: 1,000 rows, 17 rendered, keyed by orderabout 190Scroll, sort, type to filter, hover rows
Date range picker: two months, keyed by date and monthabout 290Month paging, stay preview, arrow keys

The keyed chart draws the same chart, but wraps each part in a <g> with a data-key and data-rev: the title, each button, the plot frame, each axis, each series, the selection, the tooltip and the detail panel. The selected point's larger dot and ring move into their own group, so a hover that moves the selection leaves both series unchanged. The axes fade by the group's opacity, so a scale tween leaves their tick marks unchanged. It uses about 25 more elements than the plain chart, and only this page uses it.

The grid and the date picker are shaped like components that React apps commonly build by hand. Their rows, days and month panels carry a data-key attribute. morphdom and React use it as the node key, as a React developer would with key.

Each keyed element also carries data-rev, an FNV-1a digest of its content that the component writes as it renders. The element's own attributes are left out of the digest, so a grid row that only scrolls to a new position or changes its hover fill keeps its digest. Two strategies use it:

Chunks at structural elements uses the same glue without help from the renderer. It splits at data-key and id elements, SVG groups, HTML landmarks and block elements, custom elements and significant ARIA roles, and compares each chunk's markup with the last frame's instead of a digest. Unkeyed chunks match by position.

Try the components #

Both components run live below through <qip-play svg-inline>, the current replaceChildren path. Click one to focus it, then use the keyboard.

The orders grid sorts when you click a column header. Type to filter, Backspace to remove a character and Escape to clear the filter. The Up and Down arrows, Page Up, Page Down, Home and End move the cursor.

In the date range picker, click a check-in day, then hover to preview the stay and click a check-out day. Arrow keys move between days, Page Up and Page Down change month, Enter or Space chooses the focused day, and Escape clears the selection.

Recorded results #

Mean milliseconds per frame from one run in headless Chrome, 20 rounds per scenario. Shorter bars are faster. The chart is drawn by name-group-value-to-svg-bars.wasm.

The React columns are timed from prepared trees, so React never parses markup. The other strategies parse the markup inside the timed region.

Run the benchmark #

Running the benchmark adds a chart of your own results below the tables.

Loading the component…

Reading the results #

Each round starts from the frame before the scenario, untimed, so every strategy patches the same first change.

The components are gui/data-grid-svg.wasm and gui/date-range-picker-svg.wasm, plus the chart's gui/openai-anthropic-arr-svg.wasm and its keyed copy gui/openai-anthropic-arr-keyed-svg.wasm. The vendored modules are built by make -C benchmarks/svg-dom-update vendor.