# Effects and Cosmetics Performance Plan Status: implementation complete; release gates defined in the browser benchmark Scope: client-side reaction effects, the Aurora shop-only line color, completion and gem effects, and cosmetic inventory/shop rendering Primary constraint: improve performance without reducing visual quality ## 1. Goal Make effects and cosmetics cheaper to prepare, render, update, and clean up while preserving their current appearance and behavior. This plan covers: - the `classic`, `giant`, `laser`, `orbit`, `firework`, and `comet` reaction styles; - the Aurora shop-only line color, including line, endpoint, connector, and gate presentation; - puzzle-completion and gem-collection animations; - cosmetic inventory and shop rendering, including the large cursor catalog; - the measurement and browser-test coverage needed to prevent regressions. It does not change gameplay, prices, ownership, realtime authority, or the artistic design of any effect. ## 2. Non-negotiable visual-fidelity contract Performance work must not automatically degrade an effect. In particular, an optimization must not: - replace a purchased effect with `classic`; - drop, merge, or skip a visible local or remote effect; - reduce particle, emoji, star, trail, ring, crack, or burst counts; - shorten an effect, shrink its visible area, or remove a layer; - reduce color depth, shadow, glow, compositing, or animation resolution; - lower the existing 30 FPS visual scheduler cap; - substitute a cheaper effect when the client is busy; - add an automatic quality tier based on frame rate, device class, effect count, or battery state. The existing user-selected lightweight/reduced-effects setting and the operating system's reduced-motion preference remain supported because they are explicit user or accessibility choices. They must not become an automatic overload response. If an extreme overlap exceeds the performance budget, the client must render the complete visuals, record the overload, and recover cleanly. A missed performance target is preferable to silently changing what the player bought or what other players see. ## 3. Current baseline The baseline below describes the implementation at the time this plan was written. | Area | Current behavior | Main performance concern | | --- | --- | --- | | Frame scheduling | Global, auxiliary, reaction, and drag visual work is capped at 30 FPS. | Effect-specific cost is not separated from other frame work. | | Reaction canvas | One viewport-sized, DPR 1 canvas is cleared and redrawn while reactions are active. Offscreen reactions are culled outside a 420 px margin. | Full-canvas clear, repeated state changes, and every active reaction's model work occur on the critical frame path. | | Reaction models | Deterministic values are repeatedly derived from reaction strings during drawing. Geometry, trigonometry, gradients, shadows, and emoji text are produced during frames. | The renderer repeats immutable work and allocates short-lived canvas objects. | | Aurora line color | A tracked controller advances through a curated palette every 2 seconds and writes one CSS custom property on the world container while Aurora presentation nodes are visible. | Keep line, endpoint, connector, and gate membership synchronized without broad DOM queries or document-wide style invalidation. | | Gem collection | Ten to eighteen DOM particles are created and animated for a normal collection. | Node allocation, individual insertion, keyframe arrays, promises, and cleanup all scale with each collection. | | Completion effect | Flash and burst elements are created per completion and removed later. | Repeated DOM allocation and timer cleanup can accumulate during rapid completions. | | Cosmetic UI | The catalog contains hundreds of entries, mostly cursors. Inventory rendering replaces and rebuilds its children. | Large owned/debug inventories can cause DOM construction, image decoding, style calculation, and scroll instability. | | Diagnostics | `BEND_PERF` already exposes timings, counters, gauges, long tasks, interaction frames, and input delay. | There are no dedicated reaction-style, Aurora, cosmetic-particle, or inventory-render metrics. | | Browser benchmark | The current interaction benchmark injects one `classic` reaction. | Heavy styles, overlapping remote effects, lifecycle cleanup, and visual equivalence are not benchmarked. | Before implementation starts, capture the baseline on the same browser build and hardware that will be used for acceptance. Baseline results belong in the benchmark output rather than as hand-copied numbers in this document. ## 4. Performance targets These are acceptance targets for a 1280 × 900 Edge viewport after a warm-up run. Phase 0 must record the initial baseline, but the targets must not be loosened merely to make a change pass. | Scenario | Target | | --- | --- | | One active heavy reaction | `drawReactionLayer` p95 at or below 6 ms; p99 at or below 8 ms. The 1 ms p95 exception preserves exact transformed emoji rendering after the faster atlas path failed visual comparison. | | Four simultaneous heavy reactions | Reaction draw p95 at or below 12 ms; p99 at or below 20 ms | | Normal active-effect cadence | Reaction frame-gap p95 at or below 45 ms, with no more than 30 reaction commits per second | | Reaction scheduler | 30 visual commits per second, with a 37.5 callbacks/second short-window envelope for the immediate startup timer/RAF callback on 60, 120, or 144 Hz displays | | Effect plus pan, zoom, or pickup drag | Existing interaction benchmark gates continue to pass; input-to-display p95 must not regress by more than 10% from the pre-change baseline | | Reaction publication/spawn | Synchronous client setup at or below 2 ms p95 | | Aurora tick | Callback p95 at or below 0.25 ms; no more than two color writes per second | | Inactive/hidden Aurora | No query, timer callback, or color write while no Aurora path is visible or while the document is hidden | | Normal gem collection | Setup p95 at or below 3 ms while retaining the current ten-to-eighteen-particle range | | Effect cleanup | No effect-owned DOM nodes, animation handles, timers, or reaction cache entries remain after their cleanup deadline | | Sequential stress | After 100 sequential effects and a settled garbage collection opportunity, retained heap is no more than 2 MB above the settled baseline, excluding the bounded shared asset cache | | Full inventory/debug inventory open | Initial render at or below 100 ms, no task at or above 50 ms, and no unexpected scroll movement | | Cosmetic equip/update | In-place update at or below 8 ms p95 without rebuilding the whole inventory | The benchmark must report results even when a gate fails. It must never enable reduced effects to obtain a passing number. ## 5. Implementation workstreams ### Phase 0 — Measure the real cost first Add named `BEND_PERF` measurements around the existing code paths: - `reactionFrame`: total reaction-layer callback time; - `reactionStyle.