logo svelte /markdown v2.0.0

Streaming Benchmarks

This page holds the technical details behind the plain-language claims on the Svelte Streamdown comparison and the LLM Streaming guide. If you only want the summary: on most content Svelte Markdown does less work per frame, on a single open code block, on citation-heavy text, and on HTML blocks Svelte Streamdown does less, and every number below can be reproduced from the repository.

What is measured

Main-thread work per animation frame, in milliseconds. For each frame the harness adds up:

  1. The time spent synchronously when the content changes.
  2. The time spent in deferred work before the next frame renders.
  3. A forced style and layout pass, so the cost of the DOM a renderer produced is charged to that renderer.

A frame that takes longer than 16.7 ms cannot be shown at 60 frames per second; those are counted as frames over budget.

Wall-clock time is deliberately not the metric. A renderer that defers work to the next frame looks instant if you only time the content assignment, and any per-frame measurement collapses to the display’s frame period if you time the whole frame.

How it is measured

SettingValue
BrowserHeadless production Chromium via Playwright
InputThe growing content string, 32 characters added per frame (about 480 tokens per second)
DocumentsAbout 24 KB per scenario, plus closed prefixes of 24, 96, and 384 KB
RunsOne warmup, then five iterations; both renderers run back to back in alternating order; two full suites
Competitorsvelte-streamdown 4.2.0 with animation, controls, and highlighting off
Date and machine2026-09-28 (the numbered list and HTML block rows: 2026-09-29), Intel Core i7-6700K, Ubuntu 26.04

Two safeguards keep the numbers honest:

  • Parity check. Every 25 frames and at the end, the streamed token tree is compared field by field with a fresh one-shot parse of the same text. All 210 runs reported zero mismatches.
  • No double counting. When a frame overruns, the next frame’s window starts where the previous one ended.

Results

Median of five iterations. Ranges span the two suites. The ratio is their total work divided by ours, so a value above 1 means Svelte Markdown did less work.

ScenarioOurs (ms)Streamdown (ms)RatioOurs over budgetTheirs over budget
Mixed prose, 1 update per frame1,797–1,9322,266–2,3331.21–1.260 of 7650
Mixed prose, 4 updates per frame537–5591,103–1,2062.05–2.160 of 1920–1
One open bullet list (~200 items)3,501–3,6259,111–9,3772.51–2.681 of 754224–236
One open table (~150 rows)1,569–1,6203,577–3,7452.28–2.311–2 of 31481–95
One open code fence1,542–1,5701,162–1,1780.750 of 7530
Citation-heavy prose1,456–1,7031,405–1,4450.85–0.970 of 7530
Numbered list with blank lines between items4,334–4,42510,770–10,8962.43–2.511–4 of 753293–306
HTML blocks with markdown inside3,686–4,0072,352–2,4010.60–0.646–9 of 7510
24 KB closed prefix + 2 KB tail113–120156–1621.35–1.370 of 670
96 KB closed prefix + 2 KB tail231–232417–4251.81–1.830 of 670
384 KB closed prefix + 2 KB tail906–9092,436–2,4542.69–2.7014 of 6767 of 67
20 KB closed nested list + 2 KB tail84–85186–1872.18–2.220 of 671

Page weight

Element counts at the end of each stream, identical across all runs:

ContentOursStreamdown
Mixed prose1,3811,704
Plain bullet list768769
Code fence4797
Citation-heavy prose776519

Caveats

  • Citations are not a like-for-like comparison. Svelte Streamdown rendered none of the reference-style links in that document; Svelte Markdown rendered 258. Resolving them is part of our cost.
  • HTML blocks are not a like-for-like comparison. By default Svelte Streamdown shows raw HTML as plain text, and the benchmark keeps that default. It builds none of the <div> and <details> elements that Svelte Markdown renders.
  • Some documents are parsed in full on every update. This trades speed for correct output. It applies to documents with Windows line endings (\r\n), and to documents that currently end inside an unfinished HTML tag, comment, or element. The HTML blocks row above includes this cost.
  • Code fences differ in structure. Svelte Streamdown wraps every line in its own element. Svelte Markdown emits one text node per line with no wrapper elements.
  • Large documents include a full layout every frame. The harness forces a layout of the whole document each frame, which both renderers pay. At 384 KB that pass is most of the frame. Svelte Markdown’s own parsing and rendering work stays nearly flat: about 1.4 ms per frame at 24 KB and 1.9 ms at 384 KB.
  • Milliseconds are machine-specific. The ratios are the claim. Absolute totals moved by tens of percent between captures on the same machine.
  • One workload family. Cumulative string updates at a fixed rate. Your transport, content, and device will differ.

Where the time goes

One traced run per scenario, Svelte Markdown only, in milliseconds per frame:

ScenarioTotalParse and diffOther JavaScriptStyle and layout
Mixed prose2.700.191.320.62
One open bullet list5.352.522.05—
One open code fence2.01—0.730.73
Citation-heavy prose2.490.281.20—

An open list is re-lexed in full on every frame by design, which is why parsing dominates there.

Reproduce it

pnpm build
pnpm preview --host 127.0.0.1 --port 4173 --strictPort

# in another shell
STREAM_COMPARE_URL=http://127.0.0.1:4173/test/stream-compare pnpm perf:stream-compare
pnpm build
pnpm preview --host 127.0.0.1 --port 4173 --strictPort

# in another shell
STREAM_COMPARE_URL=http://127.0.0.1:4173/test/stream-compare pnpm perf:stream-compare

Useful options:

VariablePurpose
STREAM_COMPARE_SCENARIORun a single scenario
STREAM_COMPARE_ITERATIONS / STREAM_COMPARE_WARMUPSChange the run counts
STREAM_COMPARE_URL_A / STREAM_COMPARE_URL_BCompare two builds of Svelte Markdown against each other

node scripts/stream-compare-attribute.mjs records a browser trace and CPU profile for one scenario and prints the per-frame breakdown.

The raw logs, JSON, and per-change A/B results are archived in the repository under .agents/.plans-closed/stream-vs-streamdown/evidence/.

Related