Skip to content

perf: lazy-load the CodeMirror editor off the playground critical path - #23

Merged
a-baran-orhan merged 1 commit into
mainfrom
perf/defer-codemirror
Jul 8, 2026
Merged

perf: lazy-load the CodeMirror editor off the playground critical path#23
a-baran-orhan merged 1 commit into
mainfrom
perf/defer-codemirror

Conversation

@a-baran-orhan

@a-baran-orhan a-baran-orhan commented Jul 8, 2026

Copy link
Copy Markdown
Member

What

Lazy-loads the CodeMirror editor in the playground (/play) so it no longer sits on the initial critical path — via dynamic import() after first paint, mirroring how the Three.js renderer already loads.

Why

/play scored lower than the landing page on Lighthouse mobile. main.ts statically imported both CodeMirror and the renderer, so the entry chunk carried ~900 kB. A prior change (already on main) deferred Three.js; this finishes the split by moving CodeMirror into its own lazy chunk.

Measured impact (honest A/B, localhost, identical throttling)

Critical-path entry chunk shrinks dramatically:

Chunk Before After
play entry (critical path) 372 kB (122 kB gzip) 7.9 kB (3.3 kB gzip)
editor (CodeMirror) (in entry) 365 kB — now lazy
renderer (Three.js) 533 kB — lazy 533 kB — lazy

Lighthouse (mobile, localhost, one run each):

Metric Before After
Performance score 63 63
First Contentful Paint 5.0 s 4.2 s
Speed Index 5.0 s 4.2 s
Largest Contentful Paint 7.6 s 7.9 s
Total Blocking Time 50 ms 170 ms (noise; both green)
CLS 0.007 0.007

What this does and doesn't do: code-splitting reduces the critical-path download, so it helps FCP / Speed Index (the shell paints before 365 kB of editor JS arrives) — most on real throttled mobile networks. It does not reduce Total Blocking Time, because the editor is still import()ed immediately at boot and executes right after paint. The overall score was flat on localhost (where downloads are ~instant); the real-network benefit is expected to be a modest FCP/LCP improvement, not a TBT win.

Correctness

editorApi is nullable until its chunk resolves:

  • recompile() captures it once (control-flow narrowing survives the intervening calls) and returns early when null.
  • User-action handlers (library pick, New, Share) optional-chain / guard, so a click during the ~tens-of-ms load window safely no-ops.
  • The two dynamic imports resolve independently; recompile() self-guards so either ordering is safe.

Verification

  • tsc --noEmit passes; production build succeeds.
  • Verified in-browser (Vite dev): editor mounts, figure animates (timeline built), phase ribbon populates, warnings surface, and library pick / New / play-pause work with zero console errors.

Reviewer note

Single-file change (playground/src/main.ts), no behavioral change — only when the editor JS executes. Reasonable to merge as an incremental FCP/Speed-Index improvement with no regression; not a dramatic score jump. If chasing TBT specifically, the lever is reducing/deferring execution (e.g. idle-callback boot), which trades off editor readiness.

The playground entry chunk statically imported CodeMirror, forcing ~365 kB onto
the critical path and inflating Total Blocking Time — the largest single lever in
the Lighthouse performance score. The editor now loads via dynamic import() after
the shell paints, in its own lazy chunk, mirroring how the Three.js renderer
already loads. The play entry chunk drops from 372 kB to 7.85 kB (122 kB to
3.3 kB gzip); both heavyweights now load after first paint.

editorApi is nullable until the chunk resolves: recompile() captures it once and
self-guards, and the user-action handlers (library pick, New, Share) optional-
chain, so a click during the ~tens-of-ms load window safely no-ops.
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
posecode Ready Ready Preview, Comment Jul 8, 2026 9:00pm

@a-baran-orhan
a-baran-orhan merged commit bbefb51 into main Jul 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant