Two CPU regressions surfaced in software-rendered / no-GPU containers
(Docker), where transform/opacity and canvas radial-gradient fills
rasterize on the CPU instead of a compositor:
1. Worker-orbs canvas + decorative motion saturate a core and freeze the
UI. A new opt-in "Max Performance" mode is the nuclear low-power switch:
body.max-performance CSS kills blur/shadow/filter AND all
animation/transition (spinners go static), and JS halts every canvas
loop (orbs, particles, cursor-glow, API sparks) via window._maxPerfActive.
Reduce Visual Effects is now decoupled from the orbs — they follow their
own toggle; only Max Performance force-kills them. While Max Performance
is on, the Orbs/Particles/Reduce-Effects checkboxes lock + grey out, and
save reads the runtime flags so prefs aren't clobbered.
2. Password managers (Bitwarden et al.) rebuild their autofill overlay on
every DOM mutation; a captured trace showed Bitwarden using ~6x the CPU
of the whole app (~400 setupOverlayOnField/sec). suppressPasswordManager-
Autofill() tags non-credential inputs with data-bwignore / data-1p-ignore
/ data-lpignore / data-form-type=other so the managers skip them; real
login/PIN fields are left alone.
Wired through: web_server.py (_initial_appearance_context), index.html
(inline flag + body class + checkbox), init.js (applyMaxPerformance +
bootstrap + listener + autofill suppression), settings.js (load/save),
worker-orbs.js / particles.js / api-monitor.js (gates), style.css.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dashboard particle preset built a fresh createRadialGradient + arc-fill for all
50 glows every frame. But each glow's gradient is a fixed size/colour for the
particle's life — only the pulse ALPHA changes per frame, and canvas multiplies
image alpha by ctx.globalAlpha. So bake the gradient once into a per-particle
offscreen canvas (full alpha) and drawImage it each frame with globalAlpha = pulse
(times the incoming globalAlpha, so transition fades stay identical). Rebuilt only
when the accent colour changes.
Pixel-identical output: same colour, same linear falloff, same source-over; sprite
rendered at ceil(glowSize) then downscaled to exact glowSize. Drops 50 gradient
allocations + arc-fills per frame to 50 cached blits. Scoped to the dashboard
preset only (smallest blast radius); other presets untouched.
Navigation & sidebar feedback:
- Show legacy pages optimistically on pointerdown + CSS :active so the
sidebar reacts instantly instead of waiting for the click/router cycle.
- Defer heavy per-page init via requestIdleCallback so a page becomes
scrollable before its init work runs.
Scroll smoothness:
- Cache particle canvas dimensions (no forced reflow per navigation).
- Pause particle + worker-orb canvas redraws during active scroll so the
scroll gets the full frame budget.
- content-visibility:auto on discover shelves and search/wishlist/library
list items to skip off-screen layout.
Dashboard:
- Run the independent initial loads in parallel (Promise.all) instead of
six sequential awaits, collapsing the reflow cascade.
Settings:
- Wire input listeners once instead of rescanning the ~960-node subtree
on every visit.
- Suppress auto-save while the form is programmatically populated on load,
fixing a spurious full save (4 POSTs + backend service re-init) that
fired on every Settings visit.
Reduce Visual Effects = full performance mode:
- Also halts particles, worker orbs and all filters; hides the static
sidebar aura circles that looked broken without their blur/animation.
Global search bar hidden on settings/help/issues/import pages.