Interactive Grid
A 5x5 grid of boxes, each one an independent interactive object: hovering lifts it toward the camera and tints it from a monochrome base to an accent hue, clicking fires a spring-damped scale pulse, and the whole wall breathes with a slow per-cell idle offset. Every cell's state — hover flag, pulse trigger — lives in one shared RecordState, keyed per cell, so interacting with one box never re-evaluates the other twenty-four.
How it works
- Raycast pointer events —
onPointerOver/onPointerOuttoggle each cell'shoverflag,onClickbumps apulseIdnonce; all three are ordinary scene props dispatched through the pointer-event whitelist. - Per-item state via
RecordState— oneRecordState<Record<string, CellState>>holds all 25 cells;cells.get(key, l)inside a cell's owncolorprop subscribes only to that cell's key, so a hover never touches a sibling's material. SeeRecordState. _keyfor reconcile identity — each grid cell carries_key: "row-col", the same keyed-match semantics as core'sElementList, documented in the grammar keys table.- Reactive
color(function-prop rule 7) —color: (l) => (cells.get(key, l).hover ? ACCENT_COLOR : BASE_COLOR)re-applies and callsroot.invalidate()only when that cell's own hover flag flips. See function-prop rules. onFramespring + idle motion — a per-cellonFrameclosure lerps a hover lift, decays a damped-cosine pulse bump from the lastpulseId, and adds aMath.sinidle bob phase-offset by grid position, all read fromroot.clock/delta. See Animation & Loop.- Duck-typed
position/scale— the array literal onpositionsets the cell's base XY placement once;onFramethen mutatesself.position.zandself.scaledirectly every tick, the same imperative-inside-onFramepattern as the quickstart.