From a full data-transport audit at b0f8780. Four independent main-thread costs on hover/draw frames:
parseColor forces a style recalc on the draw path — resolveCssColor (js/src/20_theme.ts:5-17) does probe-<span> appendChild + getComputedStyle per call for any non-# color. Hot callers: _drawHoverPoint parses its default rgba(15,23,42,.92) every hover frame (js/src/50_chartview.ts:3218-3222); _drawPoints selected/unselected state colors parse twice per trace per frame (~:3055-3060). Fix: memoize expr→rgba in a Map invalidated on refreshTheme.
_hoverAt linear-scans every trace's full CPU columns per pointermove when the GPU pick misses (cursor over empty area — the common case): O(total points) with per-point _axisCoord calls (50_chartview.ts:4519-4561, _nearestCpuIndex ~:4494). ~1-2 ms per move at 200k points. Fixes: skip point-pickable scatter traces (PICK_VS already enlarges hit targets to max(sz,6)*dpr, js/src/40_gl.ts:345); binary-search sorted-x line traces; early-out when the pointer hasn't crossed a pixel.
- Dashed-line arc length recomputed + re-uploaded every draw —
_lineDash (50_chartview.ts:3544-3586) runs an O(n) _dataPx+hypot loop and bufferData(DYNAMIC_DRAW) on every _drawLine, including hover-only redraws; _segmentDash (~:3423-3494) additionally allocates two O(n) string-key arrays + a Map per draw. Fix: cache behind a (view, plot, dpr, bufferId) signature.
- Axis-label DOM fully torn down and rebuilt every non-animated frame —
_drawChrome (50_chartview.ts:4019-4033): labelCadenceMs is 0 unless _viewAnim is active, so labels.textContent = \"\" + recreate runs on steady-hover and pan-drag frames. Fix: cache label DOM behind (view ranges, plot rect, theme epoch).
From a full data-transport audit at b0f8780. Four independent main-thread costs on hover/draw frames:
parseColorforces a style recalc on the draw path —resolveCssColor(js/src/20_theme.ts:5-17) does probe-<span>appendChild +getComputedStyleper call for any non-#color. Hot callers:_drawHoverPointparses its defaultrgba(15,23,42,.92)every hover frame (js/src/50_chartview.ts:3218-3222);_drawPointsselected/unselected state colors parse twice per trace per frame (~:3055-3060). Fix: memoize expr→rgba in a Map invalidated onrefreshTheme._hoverAtlinear-scans every trace's full CPU columns per pointermove when the GPU pick misses (cursor over empty area — the common case): O(total points) with per-point_axisCoordcalls (50_chartview.ts:4519-4561,_nearestCpuIndex~:4494). ~1-2 ms per move at 200k points. Fixes: skip point-pickable scatter traces (PICK_VS already enlarges hit targets tomax(sz,6)*dpr,js/src/40_gl.ts:345); binary-search sorted-x line traces; early-out when the pointer hasn't crossed a pixel._lineDash(50_chartview.ts:3544-3586) runs an O(n)_dataPx+hypotloop andbufferData(DYNAMIC_DRAW)on every_drawLine, including hover-only redraws;_segmentDash(~:3423-3494) additionally allocates two O(n) string-key arrays + a Map per draw. Fix: cache behind a(view, plot, dpr, bufferId)signature._drawChrome(50_chartview.ts:4019-4033):labelCadenceMsis 0 unless_viewAnimis active, solabels.textContent = \"\"+ recreate runs on steady-hover and pan-drag frames. Fix: cache label DOM behind(view ranges, plot rect, theme epoch).