# Legend-inspired T3 web performance exploration

Reference: [Legend apps at 4744fb8](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017). Controlled timing baseline: [T3 ab67795](https://github.com/pingdotgg/t3code/commit/ab67795dde448a3608d8689f7ad1975bb977e5a9).

The first pass stopped too early. The resumed exploration found three further improvements beyond lazy minimap formatting. This report replaces the initial one-change conclusion.

These are measurements of T3 web on Linux/Chromium. They are not a native-versus-web race. Legend's mmap-backed storage, C++ parsing and native layout do not carry over directly to a remote browser. Its simulated plain-text chat stream also does less work than T3's Markdown, syntax highlighting and provider lifecycle.

## Retained changes

Each comparison uses five alternating production-build rounds at 1440 × 1000. Values are medians of the five run medians and five run p95s, respectively. Each layer is compared against its immediate predecessor. The percentages must not be added: the first two and last two use different workloads, and each comparison is a separate run set.

| Change | Workload | Median before → after | p95 before → after | Reduction |
| --- | --- | --- | --- | --- |
| Lazy minimap preview formatting | 12 completed fences × 30 lines, growing prose, 120 measured updates | 23.0 → 20.9 ms | 36.9 → 32.4 ms | 9.1% |
| Reuse completed Markdown prefixes | Same fixture, 120 updates | 21.5 → 17.4 ms | 34.0 → 30.3 ms | 19.1% |
| Resume syntax highlighting after completed lines | Start with 200 TypeScript lines, append one line per update, 40 updates | 170.8 → 120.3 ms | 211.5 → 152.6 ms | 29.6% |
| Preserve completed code-line DOM | Same growing-code fixture, 40 updates | 118.9 → 103.6 ms | 154.8 → 127.8 ms | 12.9% |

The last fixture remains slow. A 103.6 ms synchronous update is not a smooth 60 fps stream. Improvements are useful without establishing that the remaining work is solved.

The minimap-only stage benchmark isolates row construction: 200 assistant responses × 20 KB changed from 21.25 ms to below the browser's 0.1 ms resolution; 1000 × 20 KB changed from 112.25 ms to roughly 0.1 ms. That is not a whole-app speedup. Opening a preview still formats its complete selected text.

## What came from each Legend app

| Area | Source idea, T3 experiment and outcome |
| --- | --- |
| [Chat history](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017/apps/chat-history) | Indexed rows and metadata-first access avoid touching every message body. T3 already virtualizes history, but minimap row construction still normalized all response text on updates. Lazy preview formatting was measured and retained. Synthetic long-history navigation and keyboard minimap jumps were browser-verified. |
| [Markdown](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017/apps/markdown) | The native document parser reparses a changing window and preserves block identity. T3 now reuses pristine parsed prefixes through closed top-level fences with blank-line boundaries, then runs the existing whole-document transforms and sanitizer. Reference definitions, footnotes, CR/CRLF and BOM take the full path. Custom plugins and prose without fences bypass the optimization. |
| [Code](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017/apps/code) | Incremental document work and stable visible content motivated resuming Shiki grammar state and retaining completed line nodes. T3's standalone file viewer already uses worker-backed viewport rendering: a 10,000-line file rendered about 100 lines initially and 201 near EOF in the inspected view. Its existing renderer was retained; the demonstrated avoidable work was in streamed chat code. |
| [Diff](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017/apps/diff) | Indexed/range-oriented native diff work maps to T3's existing worker-backed virtualized diff renderer. Browser checks exercised a 200-added/200-removed-line working-tree diff, split/stacked display, scrolling and collapse/expand. No replacement was justified by those checks. They establish bounded rendered content and exercised behavior, not complete diff-latency or memory benchmarks. |
| [Music](https://github.com/LegendApp/legend-apps/blob/4744fb8b57026a645379e826fe8ab34861f43017/apps/music/src/App.tsx) | Initialization, cache hydration, provider setup and window prefetch are scheduled after interactions with cancellation. T3 subscription narrowing and moving toolbar measurement to an observer were tried. Traces showed expensive layout moving to another read rather than disappearing; those changes were rejected. A three-run warm-navigation survey measured first-contentful paint at 84, 140 and 72 ms. This does not measure cold startup or input readiness; delaying required connection hydration was not justified by it. No startup-scheduling change is claimed in this stack. |
| [Kitchen sink](https://github.com/LegendApp/legend-apps/tree/4744fb8b57026a645379e826fe8ab34861f43017/apps/test-kitchen-sink) | Native windows, menus, hotkeys, scanners and text components explain much of the demo's platform integration. They do not establish a web speedup. No native API or state-library migration is included. |
| Tool disclosure | Existing T3 disclosure builds expanded text when opened and bounds expanded groups. No speculative truncation or loss of content was introduced. |

## Experiments rejected or corrected

- Code-body memoization and header extraction did not improve full-app results. A larger two-fence × 1000-line follow-up measured 44.1 ms versus 48.4 ms with body memoization. Both were discarded.
- Skipping `rehype-raw` helped an isolated parse, but broad use changed list whitespace, table normalization and footnote attributes. The conservative version passed parity without a convincing full-app gain. It was discarded.
- Applying the incremental parser wrapper to ordinary prose produced no benefit and noisy regressions. The final gate bypasses prose without fences.
- The initial incremental parser failed CRLF source-position parity. Full parsing for CR/CRLF corrected it. A later review found list-indentation recovery parses evicting the document prefix; the cache now applies only to the processor's first parse, with a regression test.
- The first code-line DOM prototype kept selection during streaming but lost it when completion discarded the incremental renderer state. The corrected renderer retains the completed-line cache through completion. Browser checks in light and dark mode retained the selected word, token DOM identity, pre element, exact scroll offset and reading position through 40 updates and completion.
- CSS invalidation remains expensive. A diagnostic removal of all 188 `:has()` rules reduced one stable-line stress run from roughly 107 ms to 23 ms. Removing just the sidebar rule barely helped; removing only composer or group rules also left much of the cost. This is not a shippable change: those selectors control actual interface states. The stack preserves them. A separate, constrained selector investigation is warranted.

## Measurement and correctness limits

The temporary probe updates the last assistant message through the existing thread atom and measures a synchronous React flush, frame intervals and long tasks. It does not measure provider token rate, network latency, relay behavior or server CPU. Highlighter counters wrap `codeToHtml` only; they exclude direct `codeToHast` calls and must not be used as total highlighting time.

Each run uses the same browser process, preview origin, synthetic fixture and settings. Build directories are switched behind that origin and reloaded; warm-up precedes measurement. Builds, tests, profiling and recordings do not run during timing. Initial narrow-viewport and concurrent-build experiments are excluded from the quoted desktop results. Raw samples retain outliers.

Parser verification includes 17,426 full-tree comparisons across all 652 CommonMark 0.31.2 examples, including source positions, plus rendered-output parity and regression tests. Shiki WASM output matches full highlighting through prefixes in 11 languages and two themes. HTML rendering parity covers colors, escaping, whitespace and blank lines. Integrated tests exercise code controls, disclosure, fallback and completion behavior.

The timing builds are pinned to the original baseline and each measured layer. The stack was subsequently rebased on [57aee3e](https://github.com/pingdotgg/t3code/commit/57aee3e) for integration; these historical timings are not relabeled as measurements of newer main. Per-PR evidence identifies its workload and verification.

Screenshots use actual matching app states. New streaming videos reconstruct Chrome screenshot frames using their original timestamps so dropped-frame intervals remain visible. Earlier fixed-frame-rate recorder videos show visual states but compress those intervals and must not be treated as timing evidence.

All fixture data is synthetic. No private transcript, authentication state, probe, generated build or PR-only recording is committed. Web and Electron's shared renderer benefit; native Electron shell and mobile were not run. Local and hosted web use the same renderer; transport and provider contracts are unchanged. Native/mobile adaptation, full startup profiling and the remaining CSS cost are explicitly not completed performance claims.

Integrated follow-up: after the rebase, 94 focused Markdown/highlighting tests and 159 timeline tests passed, along with web typecheck. Agent-browser checks in light and dark mode retained the selected word and exact scroll position through 40 streamed updates and completion. A review follow-up added an initial-highlighting assertion to the completion test; its 44-test file passed again.

The warm-navigation survey used an already cached authenticated browser and a completed seeded thread, with no replay running. First-contentful paint came from the browser Performance API. The later app-ready observation includes automation latency and must not be treated as exact time-to-interactive. Resource body-size reporting varied on cached responses, so no bundle-transfer improvement is claimed.
