live: cap animation-canvas DPR at 1.5 and redraw at ~60fps#1737
live: cap animation-canvas DPR at 1.5 and redraw at ~60fps#1737ArcanConsulting wants to merge 1 commit into
Conversation
The live-map animation overlay re-clears and re-draws its full backing store every animation frame. Two unbounded multipliers make that expensive: 1. devicePixelRatio is uncapped in updateAnimCanvas(). The canvas is already ~1.4x the screen area (20% pad per side), so at DPR 2-3 it allocates and fills 5-12x the screen's pixels per frame. Cap at 1.5 — lines stay crisp, per-frame fill cost drops up to ~4x on hi-DPI displays. 2. renderAnimations() reschedules via rAF with no rate limit, so on 120/144Hz displays it does 2-2.4x the work for no visible gain. Add a ~60fps guard. Progress is time-based (tickDt, itself capped at 32ms), so skipping frames preserves motion exactly. Paused frames fall through to the existing sleep. No behavior change on a standard 60Hz / 1x-DPI display. Existing animation tests (test-live-dt-cap-1524, test-live-anims) unaffected.
Polish review — focused (tufte + carmack lens on a 20-line perf tweak)3-axis status: mergeable=MERGEABLE · mergeStateStatus=BLOCKED · CI=action_required (first-time contributor; the Verdict: LGTM with one nit + one defensive askThe reasoning in the PR body matches the code. Capping DPR at 1.5 and throttling redraws to ~60fps are both low-risk wins on hi-DPI / high-refresh displays, and the comments inline ( MINOR (non-blocking)
NIT
Test coverageNo tests in this PR. Acceptable for a perf-only tweak that doesn't change rendered output (DPR<1.5 displays see no change; DPR>1.5 see lower resolution which is a deliberate trade). A CDP-based "canvas backing-store width matches Math.min(dpr, 1.5)*w" assertion would be the only useful test, and that's overkill for 20 lines of guard-rail config. Per project AGENTS.md TDD exemption: pure-perf changes that don't alter behavior assertions are exempt. CDP verificationSkipping live-browser verification — the PR is a measurable-by-instrumentation change (frame count, backing-store dimensions), and CI hasn't approved-to-run yet so there's no staging artifact to point a browser at. Once operator approves the workflow run and staging deploys, a 30-second Operator action requiredThis PR is safe to merge once CI is approved-and-green, but auto-merge is blocked by the first-time contributor gate ( — mc-bot watcher v2 · 2026-06-16 20:48Z |
🤖 Hourly PR-watch reviewVerdict: APPROVE-WITH-CONCERNS — CI blocked by #1747, perf change unverified Two perf caps on the live animation canvas: DPR capped at 1.5 + per-frame redraw throttle at ~60fps via Diff review
Concerns
CI statusPlaywright failure is the cross-PR fixture-aging issue tracked in #1747, not this change. Findings: 0 BLOCKER / 0 MAJOR / 3 MINOR — community author, leaving as comment-only per watcher policy.Reviewed by: carmack (perf surface) + tufte (visual fidelity). |
The live-map animation overlay re-clears and re-draws its full backing store
every animation frame. Two unbounded multipliers make that expensive:
devicePixelRatio is uncapped in updateAnimCanvas(). The canvas is already
~1.4x the screen area (20% pad per side), so at DPR 2-3 it allocates and
fills 5-12x the screen's pixels per frame. Cap at 1.5 — lines stay crisp,
per-frame fill cost drops up to ~4x on hi-DPI displays.
renderAnimations() reschedules via rAF with no rate limit, so on 120/144Hz
displays it does 2-2.4x the work for no visible gain. Add a ~60fps guard.
Progress is time-based (tickDt, itself capped at 32ms), so skipping frames
preserves motion exactly. Paused frames fall through to the existing sleep.
No behavior change on a standard 60Hz / 1x-DPI display. Existing animation
tests (test-live-dt-cap-1524, test-live-anims) unaffected.