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
Follow-up to #4429/#4431. PR #4431 deferred the mobile SVG-map dynamic-overlay render off first paint via scheduleAfterFirstPaint. Validated on prod (2026-06-26, mobile Lighthouse ×3):Map-*.js boot scripting dropped 1277 ms → ~880 ms (−30%) — but mobile Perf stayed ~65 and TBT ~1477 ms (both within noise).
Root cause of the non-movement: scheduleAfterFirstPaintreorders the overlay render off first paint (helps FCP/perceived load) but the deferred work still executes as a single long task within Lighthouse's TBT window (FCP→TTI). TBT = Σ(task − 50 ms) over tasks > 50 ms — moving one ~390 ms long task later doesn't reduce it. To cut TBT you must break the long render task into sub-50 ms chunks, not just reorder it.
Scope
src/components/Map.tsrender() — the mobile/fallback D3/SVG renderer. The base block (countries/grid/graticule) and the dynamic block (cables/pipelines/conflicts/AIS + renderClusterLayer + renderOverlays) each run synchronously as part of one render task.
Approach (directional)
Break render()'s dynamic-overlay pass into per-layer steps that yield to the main thread between layers (e.g., await a microtask/scheduler.postTask/setTimeout(0) between renderCables → renderPipelines → renderConflicts → renderAisDensity → renderClusterLayer → renderOverlays), so no single task exceeds ~50 ms.
Consider the same for the base render if renderCountries (topojson path generation) is itself a >50 ms task.
Measure-first: instrument the per-layer task durations to confirm which layers exceed 50 ms before chunking.
Acceptance
No single map-render task > ~50 ms in a mobile DevTools performance trace.
Prod mobile Lighthouse TBT improves vs the ~1477 ms baseline (median of ≥3).
All layers still render; zoom/pan/toggle/theme interactions remain correct (no torn/partial overlays).
Note
Deferral (#4431) addressed perceived-load; this addresses the score/TBT by reducing long-task duration. See the validation in #4429 comments + the 2026-06-26 measurement.
Why
Follow-up to #4429/#4431. PR #4431 deferred the mobile SVG-map dynamic-overlay render off first paint via
scheduleAfterFirstPaint. Validated on prod (2026-06-26, mobile Lighthouse ×3):Map-*.jsboot scripting dropped 1277 ms → ~880 ms (−30%) — but mobile Perf stayed ~65 and TBT ~1477 ms (both within noise).Root cause of the non-movement:
scheduleAfterFirstPaintreorders the overlay render off first paint (helps FCP/perceived load) but the deferred work still executes as a single long task within Lighthouse's TBT window (FCP→TTI). TBT = Σ(task − 50 ms) over tasks > 50 ms — moving one ~390 ms long task later doesn't reduce it. To cut TBT you must break the long render task into sub-50 ms chunks, not just reorder it.Scope
src/components/Map.tsrender()— the mobile/fallback D3/SVG renderer. The base block (countries/grid/graticule) and the dynamic block (cables/pipelines/conflicts/AIS +renderClusterLayer+renderOverlays) each run synchronously as part of one render task.Approach (directional)
render()'s dynamic-overlay pass into per-layer steps that yield to the main thread between layers (e.g.,awaita microtask/scheduler.postTask/setTimeout(0)betweenrenderCables→renderPipelines→renderConflicts→renderAisDensity→renderClusterLayer→renderOverlays), so no single task exceeds ~50 ms.renderCountries(topojson path generation) is itself a >50 ms task.renderScheduled/MIN_RENDER_INTERVAL_MSmachinery + thedestroyedguard added in perf(map): defer mobile SVG-map dynamic overlays off the first-paint path (#4429) #4431).Acceptance
Note
Deferral (#4431) addressed perceived-load; this addresses the score/TBT by reducing long-task duration. See the validation in #4429 comments + the 2026-06-26 measurement.