d
This commit is contained in:
parent
c3a6f5ff37
commit
4c4e767ec6
73 changed files with 3502 additions and 741 deletions
|
|
@ -53,12 +53,14 @@ Long connected lines become thicker and award more points. Higher-level puzzles
|
|||
|
||||
## Shops and items
|
||||
|
||||
A solved line may reveal a shop. Each shop offers a deterministic selection of cursor designs and one utility item.
|
||||
クリアした盤面からショップが現れることがあります。各ショップには、上段にカーソル6点、下段にカーソル以外のアイテム6点が盤面シードに基づいて陳列されます。
|
||||
|
||||
- **Score Lens:** Shows a projected reward for an unsolved puzzle. Its base price is 200,000 gems.
|
||||
- **ラインカラー:** 新しく引く線と利用可能なゲートの色を変更します。各プレイヤーには初期色が1色自動で付与されます。ショップ限定のオーロラは、選定された色へ2秒ごとに切り替わります。
|
||||
- **リアクション:** 未クリア盤面の外側で送るリアクション演出を変更します。
|
||||
- **スコアレンズ:** 未クリア盤面の予想報酬を表示します。基本価格は200,000ジェムです。
|
||||
- **Emoji or flag cursor:** Changes the pointer appearance.
|
||||
|
||||
Difficulty-adjustment field items are not part of the current game. Item prices and purchases are validated by the shared-world server when online.
|
||||
Colors other than the granted starter color, including Aurora, and all enhanced emoji effects must be obtained from shops. Difficulty-adjustment field items are not part of the current game. Item prices and purchases are validated by the shared-world server when online.
|
||||
|
||||
## Time Attack
|
||||
|
||||
|
|
@ -71,14 +73,14 @@ When the game is opened through `server.js`, every player explores the same gene
|
|||
- Cleared boards and newly generated boards are shared.
|
||||
- The first accepted solver's name is shown on the cleared board.
|
||||
- Recent clears appear above the minimap with coordinates and level.
|
||||
- Unfinished lines and camera state remain local. Player score, purchases, name, and equipped cursor belong to the individual player rather than the shared world.
|
||||
- Unfinished lines and camera state remain local. Player score, purchases, name, starter color, and equipped cosmetics belong to the individual player rather than the shared world.
|
||||
|
||||
Progress is saved automatically. Most synchronization runs in the background, but a completed solution is verified by the server before shared expansion is finalized. If expansion keeps trying for at least eight seconds and still leaves an unresolved new-board frontier, the solver receives a one-time level-6-equivalent bonus. Use the gear button to change the player name and presentation settings; use the shared-status button to restore an existing synchronization code.
|
||||
|
||||
|
||||
## Shared-world play
|
||||
|
||||
- Other nearby players appear as named cursors. Their positions are shown as small dots on the minimap.
|
||||
- Other nearby players appear as named cursors. Their positions are shown as small dots on the minimap. Emoji reactions and their purchased visual styles are also shared with nearby players.
|
||||
- Grabbing or operating an unfinished board requests exclusive control of that board.
|
||||
- A board occupied by another player shows their name and cannot be played until released.
|
||||
- Control expires after five minutes without board operation. A disconnected player follows the same timeout.
|
||||
|
|
@ -88,4 +90,4 @@ Progress is saved automatically. Most synchronization runs in the background, bu
|
|||
|
||||
## Settings
|
||||
|
||||
Open the gear button to change the player name, enable lightweight rendering, or turn sound effects on and off. Lightweight rendering disables selected background, glow, and particle effects without changing puzzle rules.
|
||||
Open the gear button to change the player name, enable lightweight rendering, or turn sound effects on and off. Lightweight rendering simplifies selected ambient background and interface decoration without changing puzzle rules. It does not replace purchased emoji reaction styles or automatically drop overlapping reactions.
|
||||
|
|
|
|||
368
docs/effects-cosmetics-performance-plan.md
Normal file
368
docs/effects-cosmetics-performance-plan.md
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
# 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.<style>`: drawing time for each reaction style;
|
||||
- `reactionPrepare`: one-time immutable model preparation;
|
||||
- `reactionComposite`: final canvas compositing work;
|
||||
- `auroraTick`: Aurora activation check and color update;
|
||||
- `gemEffectSetup` and `completionEffectSetup`;
|
||||
- `inventoryRender` and `inventoryPatch`.
|
||||
|
||||
Add counters and gauges for:
|
||||
|
||||
- active and visible reaction counts;
|
||||
- reaction frames, deadline-timer callbacks, draw-RAF callbacks, and skipped scheduler opportunities;
|
||||
- emoji draws, gradient creations, path builds, and canvas pixels cleared;
|
||||
- prepared-model, glyph-atlas, and static-layer cache hits, misses, size, and evictions;
|
||||
- active cosmetic DOM particles and pooled nodes;
|
||||
- Aurora active-path count and color writes;
|
||||
- inventory nodes created, reused, removed, and images decoded.
|
||||
|
||||
Each reaction must keep its style name in the metric, but metrics must not contain player identifiers, emoji text, or other unbounded labels.
|
||||
|
||||
Deliverable: a repeatable baseline report for every effect and overlap scenario in Section 7.
|
||||
|
||||
### Phase 1 — Prepare immutable reaction data once
|
||||
|
||||
Move deterministic model construction out of the draw loop and into reaction normalization/application:
|
||||
|
||||
1. Compute and store the reaction seed once.
|
||||
2. Precompute stable angles, radii, offsets, sizes, rotations, color choices, crack branches, star positions, lifetime-independent trail coefficients, and bloom membership.
|
||||
3. Store normalized duration values and style-specific constants on a compact prepared model.
|
||||
4. Keep time-dependent interpolation, viewport-dependent geometry/bounds, camera transformation, alpha, and compositing in the frame callback.
|
||||
5. Delete the prepared model when its reaction expires or is explicitly removed.
|
||||
|
||||
Use typed arrays where they reduce object churn without making the model harder to validate. The prepared values must be generated from the same deterministic inputs so that the result is visually identical at every sampled lifetime.
|
||||
|
||||
Expected result: string hashing, most trigonometry, and immutable geometry allocations disappear from active frames.
|
||||
|
||||
### Phase 2 — Cache expensive drawing assets
|
||||
|
||||
Build bounded caches that change how pixels are produced, not which pixels are intended:
|
||||
|
||||
- Create an emoji glyph atlas keyed by emoji, rendered size, shadow/glow recipe, device scale, and browser font identity. Draw cached glyphs with `drawImage` instead of repeating `fillText`.
|
||||
- Cache `Path2D` objects for static rings, cracks, star shapes, and burst geometry where coordinates do not change.
|
||||
- Pre-render visually static effect layers to transparent offscreen surfaces. Keep dynamic translation, scale, rotation, alpha, and color stages on the reaction canvas.
|
||||
- Reuse gradient recipes or pre-rendered gradient textures when their stops and local bounds are unchanged.
|
||||
- Prewarm the currently equipped effect and its common glyphs during an idle callback after the initial world render.
|
||||
|
||||
All caches must:
|
||||
|
||||
- have a documented byte or entry bound;
|
||||
- use least-recently-used eviction;
|
||||
- expose hit, miss, size, and eviction metrics;
|
||||
- use a synchronous exact-render fallback on a miss;
|
||||
- evict only reusable data, never an active effect;
|
||||
- clear browser-dependent assets when font/device-scale inputs change.
|
||||
|
||||
Do not use a low-resolution cache and scale it up. Offscreen surfaces must retain the current effective resolution and compositing behavior.
|
||||
|
||||
Expected result: repeated emoji shaping, gradient allocation, and static geometry painting are replaced by bounded image and path reuse.
|
||||
|
||||
### Phase 3 — Reduce canvas state and pixel work
|
||||
|
||||
After Phases 1 and 2 are measured, optimize the reaction layer:
|
||||
|
||||
1. Replace the current every-display-refresh RAF polling with a timer-to-RAF scheduler: wait until the next 30 FPS deadline is near, then use one animation frame for display-synchronized drawing.
|
||||
2. Derive deadlines from an absolute timeline so timer drift cannot lower the sustained cadence.
|
||||
3. Group compatible draws so font, shadow, blend mode, alpha, and transform state change less often.
|
||||
4. Replace repeated `save()`/`restore()` pairs with explicit state restoration where benchmarks prove it safe.
|
||||
5. Reuse paths and temporary arrays instead of allocating them per frame.
|
||||
6. Track the previous and current visual bounds of every reaction.
|
||||
7. Test clearing the union of dirty bounds instead of the full canvas.
|
||||
8. If dirty rectangles cause trails, clipping, blend changes, or edge artifacts in any golden frame, retain the full clear and rely on the other workstreams.
|
||||
9. Composite each prepared offscreen layer once per reaction per frame.
|
||||
|
||||
Worker preparation with `OffscreenCanvas` may be added only for immutable asset preparation. The main thread must retain a visually identical fallback for browsers that do not support the worker path. Do not put input delivery or camera state behind asynchronous worker messages.
|
||||
|
||||
Expected result: less state churn and fewer cleared/redrawn pixels, with a safe full-canvas fallback.
|
||||
|
||||
### Phase 4 — Make Aurora lifecycle-aware and locally scoped
|
||||
|
||||
Aurora is implemented as a shop-only line-color contract rather than a separate equip slot:
|
||||
|
||||
- Track mounted Aurora paths, connectors, endpoint nodes, and gate nodes as boards render or are removed; do not query the DOM on a color tick.
|
||||
- Start one scheduler when the first visible Aurora presentation node appears.
|
||||
- Stop it when the active count reaches zero.
|
||||
- Suspend it while `document.hidden` is true and resume from a time-correct deadline.
|
||||
- Advance through a curated palette exactly once every 2 seconds.
|
||||
- Write `--aurora-rgb` on the shared world container, not on `document.body`.
|
||||
- Avoid duplicate writes when the selected value is unchanged.
|
||||
- Apply Aurora to newly drawn paths and to every usable, unsealed gate while it is equipped. Existing non-Aurora paths retain their recorded appearance.
|
||||
- Repaint mounted boards once when the equipped line color changes so gate membership and saved path styling update immediately.
|
||||
- Migrate the legacy `lineEffectStyle: "aurora"` saved value to the Aurora line-color item ID without discarding the equipped appearance.
|
||||
|
||||
Expected result: zero Aurora work when inactive, one curated color selection per two seconds when active, and consistent line/gate presentation.
|
||||
|
||||
### Phase 5 — Pool completion and gem DOM effects
|
||||
|
||||
Preserve the current particle counts, paths, durations, colors, easing, and layering while removing repeated setup work:
|
||||
|
||||
- Maintain a pool large enough for the current maximum normal gem burst and any documented concurrent bursts.
|
||||
- Reset and reuse particle nodes instead of creating and discarding each node.
|
||||
- Append newly required nodes with one `DocumentFragment`.
|
||||
- Reuse immutable keyframe and animation-option templates; fill only the values that differ for a particle.
|
||||
- Use one owner/controller to track animations and cleanup rather than one unobserved promise chain per particle.
|
||||
- Pool the completion flash and burst nodes and cancel stale timers before reuse.
|
||||
- On world reset, navigation, or teardown, cancel animations and return every owned node to the pool.
|
||||
|
||||
The pool must be bounded. If concurrency exceeds its size, create the additional nodes required to preserve visuals, then release the overflow nodes after the burst.
|
||||
|
||||
Expected result: the same animation with lower node, object, promise, and timer churn.
|
||||
|
||||
### Phase 6 — Patch cosmetic UI instead of rebuilding it
|
||||
|
||||
Keep the current catalog, ordering, card design, category behavior, and item visibility while reducing UI work:
|
||||
|
||||
1. Give every cosmetic card a stable key based on catalog ID.
|
||||
2. Reuse existing category and card nodes across inventory renders.
|
||||
3. Patch only changed state such as ownership, equipped status, price, and selected styling.
|
||||
4. Preserve the scrolling element and its exact `scrollTop` during every patch.
|
||||
5. Apply `content-visibility: auto` and an accurate intrinsic-size estimate to offscreen categories/cards.
|
||||
6. Lazy-decode flag and thumbnail images near the viewport; cache successfully decoded assets.
|
||||
7. Batch class and text changes before the browser's style/layout phase.
|
||||
8. Keep keyboard order, focus, screen-reader names, and category collapse behavior unchanged.
|
||||
|
||||
If the full debug/owned inventory still misses its budget, add accessible windowing as a later step. Windowing must preserve the scrollbar range, focus restoration, category navigation, and exact item visuals; it must not remove discoverable items or unexpectedly move the list.
|
||||
|
||||
Expected result: opening a large catalog and equipping an item no longer creates a full-tree rebuild or automatic scroll jump.
|
||||
|
||||
### Phase 7 — Harden lifecycle and overlap behavior
|
||||
|
||||
Ensure optimization state cannot leak or change multiplayer behavior:
|
||||
|
||||
- cancel reaction animation frames when no reactions are active;
|
||||
- remove expired reaction models and cached active surfaces deterministically;
|
||||
- suspend background visual schedulers while the page is hidden, then resume from authoritative time rather than replaying queued frames;
|
||||
- clear effect-owned state during world reset and client teardown;
|
||||
- render all valid overlapping reactions from different players;
|
||||
- retain the existing server and client rule for each player's concurrent special reaction;
|
||||
- record an overload gauge when valid overlap exceeds the tested matrix, without dropping or simplifying effects.
|
||||
|
||||
No client cache may become a source of gameplay or ownership truth.
|
||||
|
||||
## 6. Priority and delivery order
|
||||
|
||||
| Priority | Change | Reason | Dependency |
|
||||
| --- | --- | --- | --- |
|
||||
| P0 | Effect-specific instrumentation and benchmark matrix | Makes all later gains and regressions visible | None |
|
||||
| P0 | One-time reaction model preparation | Removes repeated CPU/allocation work with low visual risk | Metrics |
|
||||
| P0 | Emoji/static-layer/path caches | Targets the most expensive repeated canvas work | Prepared models |
|
||||
| P0 | Deadline-based 30 FPS scheduler | Avoids polling at 60–144 Hz without changing visible cadence | Scheduler metrics |
|
||||
| P1 | Aurora lifecycle and scoped variable | Small, isolated change with clear inactive-state benefit | Metrics |
|
||||
| P1 | Gem/completion node pooling | Removes predictable DOM churn | Metrics |
|
||||
| P1 | Keyed cosmetic inventory patching | Addresses large catalogs and scroll movement | UI metrics |
|
||||
| P1 | Canvas state batching | Reduces frame cost after model and asset work are separated | Prepared models and caches |
|
||||
| P2 | Dirty-rectangle clearing | Can reduce pixel work but has higher artifact risk | Golden-frame coverage |
|
||||
| P2 | Worker/offscreen preparation | Useful only if main-thread preparation still misses the budget | Stable prepared-model format |
|
||||
| P2 | Accessible inventory windowing | Use only if keyed patching and content visibility are insufficient | UI benchmark and accessibility tests |
|
||||
|
||||
Each row should ship independently where practical. Capture a before/after trace and memory result for every row rather than combining all optimizations into one unreviewable change.
|
||||
|
||||
## 7. Verification matrix
|
||||
|
||||
### Effect scenarios
|
||||
|
||||
Run all of these with reduced effects disabled:
|
||||
|
||||
- each reaction style alone: `classic`, `giant`, `laser`, `orbit`, `firework`, and `comet`;
|
||||
- four simultaneous heavy reactions from different players;
|
||||
- eight simultaneous mixed reactions from different players as an overload/recovery test;
|
||||
- Aurora alone and Aurora while reactions are active;
|
||||
- a completion burst;
|
||||
- minimum and maximum normal gem bursts;
|
||||
- rapid sequential gem and completion effects;
|
||||
- full owned inventory and debug/all-item inventory;
|
||||
- cosmetic equip changes while the inventory is scrolled.
|
||||
|
||||
### Interaction combinations
|
||||
|
||||
For each relevant effect scenario, measure:
|
||||
|
||||
- idle camera;
|
||||
- continuous pan;
|
||||
- continuous zoom;
|
||||
- pickup drag;
|
||||
- pickup edge-pan;
|
||||
- shop/inventory scrolling.
|
||||
|
||||
This ensures effect work does not reintroduce the previously observed camera and pickup-display stalls.
|
||||
|
||||
### Environment matrix
|
||||
|
||||
At minimum:
|
||||
|
||||
- Edge at 1280 × 900 and device scale 1;
|
||||
- Edge mobile-size viewport at 390 × 844;
|
||||
- normal CPU and browser 4× CPU throttling;
|
||||
- visible document, hidden for the middle of an effect, and resume;
|
||||
- cold cache and warm cache;
|
||||
- normal catalog and debug/all-item catalog.
|
||||
|
||||
Use one controlled Edge instance at a time and close its temporary profile after the matrix. The test runner must not leave background browser processes or temporary profiles behind.
|
||||
|
||||
### Visual-equivalence checks
|
||||
|
||||
Use a test hook to inject exact normalized lifetime values of 0.10, 0.25, 0.50, 0.75, and 0.95, then capture deterministic reference frames before changing a renderer. Repeat the exact injected-lifetime captures after every visual-path optimization.
|
||||
|
||||
Compare:
|
||||
|
||||
- particle/glyph count and identity;
|
||||
- bounds, position, rotation, and scale;
|
||||
- ring, trail, crack, star, flash, and burst presence;
|
||||
- color stops, shadow/glow extent, blend order, and alpha;
|
||||
- start time, total duration, and fade timing;
|
||||
- layering relative to the world and other reactions.
|
||||
|
||||
Pixel differences are acceptable only for demonstrated browser anti-aliasing noise. Use a small per-channel tolerance and require at least 99.5% of pixels within that tolerance. Any structural difference fails even if the aggregate pixel threshold passes.
|
||||
|
||||
### Functional and cleanup checks
|
||||
|
||||
Verify that:
|
||||
|
||||
- purchased/equipped styles still resolve to the same renderer;
|
||||
- local and remote players see the same style and duration;
|
||||
- overlapping valid reactions are all rendered;
|
||||
- ownership, store pricing, and equip persistence are unchanged;
|
||||
- hidden/resumed effects use authoritative elapsed time;
|
||||
- no unexpected inventory scroll or focus movement occurs;
|
||||
- all timers, animation frames, animations, pooled overflow nodes, and active models are cleaned up;
|
||||
- the full test suite and the existing real-browser performance benchmark pass.
|
||||
|
||||
## 8. Planned code and test changes
|
||||
|
||||
| File or area | Planned responsibility |
|
||||
| --- | --- |
|
||||
| `app.js` | Prepared reaction models, bounded caches, effect metrics, reaction lifecycle, Aurora controller, pooled DOM effects, and keyed inventory patching |
|
||||
| `style.css` | Narrow Aurora variable scope, content visibility/intrinsic sizing, and any pool reset styles that preserve current appearance |
|
||||
| `test/browser-performance-benchmark.js` | Per-style, overlap, interaction-combination, lifecycle, cadence, and memory probes |
|
||||
| `test/effects-performance-smoke-test.js` | Source/runtime invariants for cache bounds, cleanup, scheduler caps, overlap behavior, and the no-auto-degradation contract |
|
||||
| Visual reference fixtures | Deterministic effect checkpoints and comparison metadata for supported Edge rendering |
|
||||
| `docs/internal-system.md` | Final architecture and lifecycle after implementation |
|
||||
| `docs/test-policy.md` | New effect-performance and visual-equivalence release gates |
|
||||
|
||||
`realtime-server.js` should not require a behavior change for this work. Server-side changes are only justified if additional diagnostics or deterministic test fixtures are needed; reaction validation and authority must remain intact.
|
||||
|
||||
## 9. Risks and safeguards
|
||||
|
||||
| Risk | Safeguard |
|
||||
| --- | --- |
|
||||
| Cached emoji differ from direct browser text rendering | Render the atlas with the same browser, font string, shadow recipe, scale, and compositing mode; compare golden frames before enabling it |
|
||||
| A cache saves CPU but retains too much memory | Enforce a measured bound, expose byte/entry gauges, test eviction, and clear browser-dependent entries on environment changes |
|
||||
| Dirty rectangles leave trails or clip glow | Include previous and current expanded bounds; immediately retain full clear if any golden or overlap case shows artifacts |
|
||||
| Offscreen/worker output changes blending | Composite with the same alpha and blend order; keep the direct main-thread path as the correctness reference |
|
||||
| Pool reuse leaks stale classes/styles | Centralize a complete reset routine and assert the reset state in tests |
|
||||
| UI reuse introduces stale ownership/equip state | Patch from one normalized view model and test every ownership/equip transition |
|
||||
| Visibility suspension changes lifetime | Derive life from authoritative timestamps on resume; never replay missed animation frames |
|
||||
| An optimization accidentally becomes adaptive quality | Test source and runtime invariants that prohibit style substitution, count reduction, duration reduction, and automatic reduced-effects activation |
|
||||
|
||||
## 10. Definition of done
|
||||
|
||||
The work is complete only when:
|
||||
|
||||
- all current effects and cosmetics are visually unchanged under the checks in Section 7;
|
||||
- no automatic quality degradation path exists;
|
||||
- the targets in Section 4 pass in the supported Edge matrix;
|
||||
- effect work does not regress pan, zoom, cursor, or pickup-drag responsiveness;
|
||||
- caches, pools, schedulers, and prepared models are bounded and cleaned up;
|
||||
- the inventory retains its exact scroll position and focus during updates;
|
||||
- ownership, pricing, persistence, and realtime behavior are unchanged;
|
||||
- benchmark reports include per-style timings, overlap results, cache statistics, and memory cleanup;
|
||||
- documentation and release tests reflect the implemented architecture.
|
||||
|
||||
Implementation note: exact-output validation rejected transformed emoji atlases, cached laser layers, and dirty-rectangle clearing, so those paths retain direct rendering and full-canvas clearing. The accepted implementation uses immutable reaction models, absolute-deadline scheduling, batched direct text state, a bounded transform-neutral glyph cache, a bounded fixed-geometry `Path2D` cache for Orbit and Firework, scoped Aurora updates, bounded DOM pools with shared Gem animation templates, and signature-based localized inventory patches. Worker preparation and inventory windowing remain conditional only and are not enabled because the synchronous path and keyed catalog remain the authoritative visual/accessibility implementation. The browser gate loads deterministic checkpoints from `test/fixtures/effect-visual-checkpoints.json`, covers setup cost, active/inactive lifecycle, effect-plus-interaction cases, focus/scroll retention, and 100-cycle cleanup, and writes a machine-readable report. Normal-speed timing gates remain authoritative; the 4x CPU matrix is an overload/completeness diagnostic because this plan explicitly prefers full visuals to automatic degradation.
|
||||
|
||||
## 11. Explicit non-goals
|
||||
|
||||
This plan does not:
|
||||
|
||||
- redesign, retire, or simplify an effect;
|
||||
- reduce the scheduler below 30 FPS;
|
||||
- introduce automatic adaptive quality;
|
||||
- change the number or duration of visible elements;
|
||||
- change store selection, price, ownership, or persistence rules;
|
||||
- change reaction rate limits or multiplayer authority;
|
||||
- use lower-quality visuals as the definition of a performance fix.
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
### Cursor, camera, and pickup cadence
|
||||
|
||||
- Cursor rendering now keeps only the newest pointer sample and commits it on a display frame.
|
||||
- Cursor commits use a 16.67 ms minimum interval, limiting presentation to 60 FPS even on high-refresh displays.
|
||||
- Camera commits use the same 60 Hz ceiling with a separate missed-frame fallback.
|
||||
- Pickup dragging remains on its bounded 60 Hz scheduler and keeps logical catch-up work separate from visual pointer tracking.
|
||||
- Cursor commits use the shared 33.33 ms visual interval, limiting presentation to 30 FPS even on high-refresh displays.
|
||||
- Camera commits use the same 30 FPS ceiling with a separate missed-frame fallback.
|
||||
- Pickup dragging remains on the bounded 30 FPS visual scheduler and keeps logical catch-up work separate from visual pointer tracking.
|
||||
|
||||
### Continuous overview panning
|
||||
|
||||
|
|
@ -39,12 +39,7 @@
|
|||
|
||||
- Focused regression coverage executes the in-gesture cache refresh, durable-clear sanitization, and production-only gate schedule.
|
||||
- The complete fast test suite passes.
|
||||
- A real Edge normal-speed scenario measured:
|
||||
- display cadence: 16.70 ms median;
|
||||
- cursor cadence: 16.67 ms median;
|
||||
- cursor input age: 16.70 ms p95;
|
||||
- camera work: 0.20 ms p95;
|
||||
- no uncapped pickup presentation.
|
||||
- The real Edge benchmark gates cursor, camera, and pickup presentation at the shared 30 FPS ceiling, checks input age and camera work separately, and rejects uncapped presentation.
|
||||
- Edge and benchmark server processes are checked after every browser run; the final count is zero.
|
||||
|
||||
## Remaining stress-test observation
|
||||
|
|
|
|||
|
|
@ -87,6 +87,18 @@ The field uses several levels of detail:
|
|||
|
||||
Camera updates, board drag frames, the minimap, distant overview, presence cursors, reactions, and static noise are scheduled and instrumented independently. Difficulty-field overlays do not exist in the current runtime.
|
||||
|
||||
### Effects and cosmetics
|
||||
|
||||
Reaction rendering keeps the complete `classic`, `giant`, `laser`, `orbit`, `firework`, and `comet` artwork at the existing 30 FPS cap. Accepted local reactions are sent to nearby realtime subscribers; reactions created while the socket is reconnecting are queued until their visual lifetime expires. Each accepted reaction receives one deterministic prepared model containing its immutable geometry and trigonometry. Frames reuse that model, draw every valid visible reaction in original layer order, and derive lifetime from authoritative timestamps. Overload is measured but never changes style, duration, particle count, glow, or compositing.
|
||||
|
||||
The reaction scheduler uses absolute deadlines, one pending timer, and one pending animation-frame callback. It stops while the page is hidden and resumes from current time. The renderer retains full-canvas clearing because cropped clears and cached transformed glyphs did not satisfy the visual-equivalence gate. A bounded emoji cache is used only for transform-neutral draws; rotated or scaled glyphs use direct browser text rendering. A separate bounded LRU `Path2D` cache reuses fixed Orbit and Firework geometry, with direct path construction as the exact fallback.
|
||||
|
||||
Aurora is a shop-only line-color contract. Its curated palette advances once every two seconds and colors newly drawn Aurora paths, connectors, endpoints, and usable gates. Updates are scoped to the world element and run only while tracked Aurora presentation nodes are visible. Completion flashes, completion bursts, and gem particles use bounded reusable node pools with overflow cleanup; gem particles share immutable keyframe and option templates while per-particle values live on the reused nodes. The cosmetic inventory keeps keyed category/item nodes, skips unchanged view signatures, patches only the old and new equipped cards, preserves focus and scroll position, lazy-decodes flag images, and applies render containment to offscreen cards.
|
||||
|
||||
`BEND_PERF` reports reaction preparation and publication, style, compositing, frame cadence, scheduler callbacks, visible/active counts, glyph and path-cache entries/hits/misses/evictions, Aurora ticks and writes, pooled-node setup/activity, and inventory patch/render timings.
|
||||
|
||||
The item UI contains no debug toggle. Opening `/debug-items` enables the debug purchase economy for that URL only: normal shop stock and purchase records are retained, while affordability checks settle at zero cost.
|
||||
|
||||
## Persistence
|
||||
|
||||
IndexedDB is the durable local source of truth. A compact local mirror supports recovery when IndexedDB startup or writes fail.
|
||||
|
|
@ -109,7 +121,7 @@ The world-generation identifier is separate from the application version. Ordina
|
|||
|
||||
`test/run-all.js` runs source, gameplay, economy, performance, storage, concurrency, generation, interaction, reset, special-cell, and server regressions.
|
||||
|
||||
`test/browser-performance-benchmark.js` verifies real Edge startup and measures pointer work, camera work, minimap conversion, level-of-detail processing, persistence, overview rendering, DOM size, and main-thread responsiveness.
|
||||
`test/browser-performance-benchmark.js` verifies real Edge startup and measures pointer work, camera work, minimap conversion, level-of-detail processing, persistence, overview rendering, DOM size, main-thread responsiveness, every reaction style, four/eight-effect overlap, deterministic checkpoint visual equivalence, reaction/Gem/completion setup cost, Aurora active/inactive/hidden behavior, localized cosmetic inventory updates with focus and scroll retention, effect-plus-interaction scenarios, and 100-cycle cleanup. It writes the complete report to `test-results/browser-performance-benchmark.json` unless `BEND_FIELD_BENCHMARK_OUTPUT` overrides the path.
|
||||
|
||||
|
||||
## Documentation policy
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ The release pipeline has four explicit tiers:
|
|||
behavior. CI sets the small bounded browser profile and runs one job at a
|
||||
time.
|
||||
|
||||
## Effects and cosmetics release gates
|
||||
|
||||
The browser performance runner loads deterministic checkpoints from `test/fixtures/effect-visual-checkpoints.json` and exercises all six reaction styles at normalized lifetimes of 0.10, 0.25, 0.50, 0.75, and 0.95. Direct main-thread rendering with glyph and path caches bypassed is the correctness reference. Cached output may differ only within the anti-aliasing tolerance, with at least 99.5% of pixels inside that tolerance. Structural reductions, style substitution, dropped overlap, shorter lifetimes, or automatic quality tiers are release failures.
|
||||
|
||||
Normal-speed Edge gates enforce the 30 FPS cadence, per-style work limits, the four-effect overlap budget, full rendering of eight simultaneous valid effects, bounded scheduler callback rates, reaction publication p95 at or below 2 ms, Gem and completion setup p95 at or below 3 ms, localized inventory patch p95 at or below 8 ms, exact inventory focus/scroll/node retention, Aurora active-write and inactive/hidden zero-work behavior, effect-plus-pan/zoom/pickup/edge-pan operation, and zero owned state after a 100-cycle cleanup stress. The settled heap delta must remain at or below 2 MB after the bounded caches are cleared. The 4x CPU profile is an overload diagnostic: it enforces bounded callbacks, complete rendering, and cleanup, but it does not permit the test or runtime to reduce visuals merely to meet normal-speed timing.
|
||||
|
||||
`effects-performance-smoke-test.js` is a documented repository-policy guard. Its source assertions protect the no-auto-degradation contract, bounded glyph/path caches, shared animation templates, localized inventory patches, and cleanup/lifecycle mechanisms that do not yet have a smaller public module seam. The browser runner always writes its report to `test-results/browser-performance-benchmark.json` unless `BEND_FIELD_BENCHMARK_OUTPUT` supplies another path, including when a gate fails after measurement.
|
||||
|
||||
## Source-shape guard inventory
|
||||
|
||||
The historical `source-smoke` and versioned `v47xx` files contain temporary
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue