You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Desktop Lighthouse (13.0.2, devtools throttling) on https://www.worldmonitor.app/ returned Performance 50 (Acc 92 / BP 96 / SEO 100). The other categories are healthy — performance is the bottleneck.
2026-05-31_15-07-01Z.webm
Metric
Current
Target
Verdict
FCP
6.3 s
< 1.8 s
poor
LCP
9.9 s
< 2.5 s
poor
Speed Index
12.0 s
< 3.4 s
poor
TBT
70 ms
< 200 ms
good
CLS
0.127
< 0.1
needs improvement
LCP breakdown:
TTFB: 130 ms ← server is fast
Element render delay: 18,670 ms ← browser is choking
A Playwright recording from a fast home connection (unthrottled) confirms the felt experience: the page's load event fires at 11.6 s and the first 4 panel titles don't appear until 21.2 s. Total transfer is ~12 MB across 125 requests.
Baseline video + perf JSON: lighthouse-videos/before/2026-05-31_15-07-01Z.{webm,json} (gitignored — see PR for the recording script in scripts/record-load.spec.ts).
Root causes (ranked by impact)
2.98 MB Clerk bundle is eagerly imported and 96% unused on first paint. Single biggest win available. Lazy-loading it alone saves ~2.88 MB and an estimated 2–3 s of LCP.
~80 simultaneous API requests fan out from panels-Cf8BmyAe.js on initial render because every panel self-loads on mount in src/app/panel-layout.ts. This saturates the browser's per-host connection pool — every request finishes between 25,000 and 32,615 ms even though TTFB is 130 ms. The max critical path latency is 32.6 s. Server isn't slow; the browser queue is.
522 KiB of render-blocking CSS — including a 70 KiB MapLibre stylesheet that is 98% unused on first paint because the map component isn't mounted yet.
div.header is the entire 0.127 CLS.#authWidgetMount and #unifiedSettingsMount mount async (Clerk + settings UI); the header reflows when they hydrate.
bfcache is fully disabled by Cache-Control: no-store on the HTML response (vercel.json lines 111, 118, 137 — root, /index.html, and the SPA catch-all). The no-store is unnecessary; no-cache would revalidate without killing bfcache.
No preconnect to api.worldmonitor.app — the origin every initial-load request hits. Lighthouse estimates 680 ms of LCP savings from adding it. Similar story for the Sentry ingest origin (310 ms).
Sentry init is on the critical path and eats 1.96 s of CPU before LCP.
DOM has 33,226 elements, including a single <tbody> with 618 children. Style recalc costs 1.2 s of main-thread time and will hurt INP under real interaction.
Two non-composited animations (findings-pulse on box-shadow, .live badge on border-color/color) run on the layout/paint thread instead of the compositor.
Plan
11 parallelizable sub-issues, partitioned by file ownership so they don't merge-conflict.
Summary
Desktop Lighthouse (13.0.2, devtools throttling) on
https://www.worldmonitor.app/returned Performance 50 (Acc 92 / BP 96 / SEO 100). The other categories are healthy — performance is the bottleneck.2026-05-31_15-07-01Z.webm
LCP breakdown:
A Playwright recording from a fast home connection (unthrottled) confirms the felt experience: the page's
loadevent fires at 11.6 s and the first 4 panel titles don't appear until 21.2 s. Total transfer is ~12 MB across 125 requests.Baseline video + perf JSON:
lighthouse-videos/before/2026-05-31_15-07-01Z.{webm,json}(gitignored — see PR for the recording script inscripts/record-load.spec.ts).Root causes (ranked by impact)
panels-Cf8BmyAe.json initial render because every panel self-loads on mount insrc/app/panel-layout.ts. This saturates the browser's per-host connection pool — every request finishes between 25,000 and 32,615 ms even though TTFB is 130 ms. The max critical path latency is 32.6 s. Server isn't slow; the browser queue is.div.headeris the entire 0.127 CLS.#authWidgetMountand#unifiedSettingsMountmount async (Clerk + settings UI); the header reflows when they hydrate.Cache-Control: no-storeon the HTML response (vercel.jsonlines 111, 118, 137 — root,/index.html, and the SPA catch-all). Theno-storeis unnecessary;no-cachewould revalidate without killing bfcache.api.worldmonitor.app— the origin every initial-load request hits. Lighthouse estimates 680 ms of LCP savings from adding it. Similar story for the Sentry ingest origin (310 ms).<tbody>with 618 children. Style recalc costs 1.2 s of main-thread time and will hurt INP under real interaction.findings-pulseonbox-shadow,.livebadge onborder-color/color) run on the layout/paint thread instead of the compositor.Plan
11 parallelizable sub-issues, partitioned by file ownership so they don't merge-conflict.
api.worldmonitor.app)no-storefrom HTML invercel.jsonto restore bfcachemain.css(452 KiB) into per-panel CSSfindings-pulseand.liveanimations as composite-only<tbody>panelConflict graph (parallel work without merge collisions)
Sequencing notes:
main.ts— land A first, then G rebases.panel-layout.ts— A only touches the header section, C only touchesrenderLayout()andcreatePanelblock. Safe in parallel.src/styles/header.css; H ownsmain.css. Safe in parallel.Suggested implementation order (solo)
Each PR:
npm run typecheck,npm run lint, relevant Playwright e2e (e2e/auth-ui.spec.tsfor #3988,e2e/map-harness.spec.tsfor #3991).Expected outcome
Verification
After each PR:
RECORD_PHASE=after npx playwright test --config=scripts/lighthouse-video.config.tsto capture a comparison video inlighthouse-videos/after/.