mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
d73e900e355d4acd07e39914af04f6f78a46c6f6
44
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e779c72396 |
feat(venue): reactive crowd video layer behind the 3D highway (career mode 1/3) (#905)
* feat(venue): reactive crowd video layer behind the 3D highway (career mode PR1) Two crossfading video backdrop planes in the highway_3d venue background style, driven by a new venue-crowd.js state machine that maps v3:live-performance-state to crowd states (bored/neutral/engaged/ecstatic) with 3s stability + 8s dwell hysteresis, plus one-shot reaction stingers on streak milestones and end-of-song accuracy. Inert without a venue pack manifest (career plugin, PR2) or the feedBack-venue-crowd-dev flag — the static bg plate behaves exactly as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): retry renderer binding + preserve mid-stinger transitions Codex preflight P2s: (1) videos created before highway_3d registered its globals never reached the backdrop planes — binding is now idempotent and retried from start/perf-event/re-activation paths; (2) a crowd-state switch committing while a stinger played was dropped because the machine had already advanced — it is now deferred and played when the stinger ends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): per-video load tokens + unbind renderer on stop Codex preflight round 2: (1) the global load token let a stinger cancel a committed loop load on the other layer — tokens are now per-element, and a stinger preempting an in-flight loop on its own layer requeues that loop for when the stinger ends; (2) setManifest(null)/deactivate left the last crowd frame bound and visible over the static plate — stop() now unbinds both layers from the renderer and zeroes the mix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): flush deferred loop on stinger failure, source accuracy from perf events Codex preflight round 3: (1) a failed/timed-out stinger left a deferred loop switch queued forever; the failure path now flushes it. (2) stats:recorded only carries {filename, arrangement}, so the end-of-song reaction now uses the accuracyPct from the song's last v3:live-performance-state event (a real percentage) instead of a field that never existed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): requeue mid-crossfade loops preempted by stingers; hard-stop on manifest swap Codex preflight round 4: (1) idleLayer() still points at the fading-in layer during a crossfade, so a stinger firing mid-fade overwrote the new loop with nothing requeued — the fading loop is now tracked and requeued like an in-flight load; (2) swapping venue packs while active now goes through stop() so _stopGen invalidates the old manifest's in-flight loads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): generation-gate stinger handlers; recrop on video size change Codex preflight round 5: (1) an ended/timeout handler orphaned by stop() could fire into a later stinger's lifecycle on the reused element — handlers now detach unconditionally and carry a generation token; (2) the renderer only re-applied cover-crop on camera aspect changes, so a src swap with a different intrinsic size kept stale repeat/offset — it now recrops when videoWidth/Height change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): bail loop-fade completion when a stinger preempted the layer Codex preflight round 6: the loop crossfade's completion callback could still run between a stinger's start and its canplaythrough, promoting the stinger's layer to active and pausing the real loop — it now bails when the fading loop was preempted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): keep rear video layer opaque during crossfades Two half-transparent layers let the static bg plate bleed through (~25% at mid-fade) — visible as a flash of the old still image on every state transition. The crossfade is now always the front layer fading over an opaque rear layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): reset active layer with mix on stop Codex preflight: stop() zeroed the mix but left _activeLayer at 1, so a restart flashed layer 0's stale frame until the new loop loaded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): reset crowd mood to neutral on song load Codex preflight: a song ending in ecstatic/bored left the next song's crowd stuck in that mood until the hysteresis window passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): cancel in-flight fade when a stinger preempts it Codex preflight: the orphaned ramp kept pushing the mix toward the layer whose src the stinger had just replaced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): don't let null accuracy resets wipe the end-of-song value Codex preflight: Number(null) is 0, so idle HUD resets overwrote _lastAccuracyPct before stats:recorded consumed it, suppressing the end-of-song stinger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): abort stale stinger state on song load Codex preflight: a stinger straddling a song change could fade back into the previous song's layer or flush its pending loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): always detach load listeners, gate only the callback Codex preflight: superseded loads left canplaythrough/error listeners attached to the persistent video elements — unbounded growth over a session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(venue-crowd): flyover intro with crowd-ambience ducking On song:loaded, an optional pack intro plays once: a camera flyover video (idle layer, one-shot) with bar-crowd ambience audio that ducks out on song:play, near the flyover's landing, or at handoff — whichever first. Machine commits and stingers defer during the intro; stop()/song-change abort it. Packs without an intro behave as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(venue-crowd): fall back to the loop when the intro fails to load Codex preflight: a failed/timed-out intro left the song with no crowd loop at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e134f5c802 |
fix(highway_3d): size Butterchurn output canvas buffer to fill the highway (#820)
* fix(highway_3d): size Butterchurn output canvas buffer to fill the highway The 3D-highway Butterchurn background set only the output canvas CSS size and called setRendererSize(), but never sized the canvas DRAWING BUFFER (canvas.width/height). Butterchurn does not size the output canvas itself (renderToScreen viewports to the reported size into the default framebuffer), so the buffer stayed at the browser default 300x150 while the viewport was the full highway. Only the bottom-left ~300x150 of the pattern was drawn, then CSS-stretched across the whole highway -- zoomed, soft, and aspect-wrong, worse the larger the panel. Add _bcApplySize(cssW, cssH): set the drawing buffer to the device-pixel render size (round(css * min(DPR, 1.5))), confine every layer (canvas, backdrop, scrim, tint) to the highway rect, and report the same device px to setRendererSize so buffer == on-screen viewport. Seed the buffer at create and switch createVisualizer to pixelRatio:1, textureRatio:1 (DPR is now folded into the reported size, so buffer == viewport == internal texsize, no double-counting). render() and resize() both route through it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com> * chore(highway_3d): bump to 3.31.5 (3.31.4 taken by #823 on main) Rebased onto main; #823 already shipped 3.31.4 (per-panel camera), so this Butterchurn buffer-sizing fix advances to 3.31.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Byron Gamatos <xasiklas@gmail.com> |
||
|
|
14d116d827 |
fix(highways): validate panel index before indexing the camera map
_resolveFreeCam() (keys/drum) and _freeCamFor() (highway_3d) guarded the panel map lookup with only `i != null`, so a non-integer / negative / string index from panelIndexFor() could resolve an unintended or inherited property (e.g. map['toString']) instead of cleanly falling back to the global camera. Gate the index on `Number.isInteger(i) && i >= 0` before `map[i]`, matching the hardening already applied in _bgPanelKey(). Extend the resolver tests with float/string (prototype-key) cases. Behavior change only for malformed indices. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
bcee2e8610 |
fix(highway_3d): _bgPanelKey rejects non-integer panel index; JSDoc bridge fns
- _bgPanelKey() treated any non-null panelIndexFor() return as a valid panel id, so a NaN/non-finite index minted a bogus "panelNaN" localStorage key instead of falling back to "main". Gate on Number.isInteger(idx) && idx >= 0. (The camera path is already NaN-safe — panelsMap[NaN] misses and falls through.) - Add a NaN/negative-index case to the resolver tests (drum 22, keys 57, pass). - Convert the camera-bridge helpers' comments to JSDoc (_bgPanelKey, _freeCamFor, _resolveFreeCam, _ssApi across the three plugins) to lift docstring coverage on the changed surface. Comment/robustness only; no behavior change beyond the NaN guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
a6a5186180 |
fix(highway_3d): make _bgPanelKey throw-safe on panelIndexFor
Follow-up to the _bgPanelKey alias fix: _freeCamFor already treats panelIndexFor as potentially throwy and catches to keep framing stable, but _bgPanelKey called it bare. A throwing splitscreen build would take down background-settings resolution (and the render path) even though the camera path falls back safely. Wrap the call in try/catch, falling back to 'main'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
0d4d8229c7 |
fix(highways): address review — bg-key alias, drum cam guard, resolver tests
Three review findings on the per-panel camera work: - highway_3d: _bgPanelKey() resolved splitscreen via window.feedBackSplitscreen only, while _freeCamFor() uses the feedBackSplitscreen||slopsmithSplitscreen alias it claims to "mirror". If the rename lands, per-panel background settings would silently stop being per-panel while the camera stayed per-panel. Resolve the alias the same way in _bgPanelKey. - drum_highway_3d: applyCamera()'s "before first positionCamera()" guard tested `_camBaseH == null`, but _camBaseH/_camBaseD were initialized to 0, so the guard never fired (and could apply a base-0 pose for a frame). Initialize to null. - keys + drum: the PR claimed the Camera Director resolver was unit-checked, but nothing exercised it. Extract the resolver into pure, exported helpers (_resolveFreeCam + _ssApi), delegate the per-instance _freeCamFor to them, and add tests/camera_bridge.test.js covering per-panel select, global fallback, null-when-absent, throw-safety, and the slopsmith-alias resolution. Drum 15→21, keys 50→56, all pass; behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
ff8a638d28 |
docs(highway_3d): name the concrete camera-bridge globals in comments
Address a review note on the free-camera block: the comments described the bridge as "per-panel-aware" without naming the actual globals. Spell out that _freeCam comes from _freeCamFor(highwayCanvas) — window.__h3dCamCtlPanels[ panelIndexFor(canvas)] when split, else the global window.__h3dCamCtl, else null — and update the nearby comment that mentioned only __h3dCamCtl. Comment- only; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
5aa336961c |
feat(highways): per-splitscreen-panel Camera Director cameras
Make the three 3D highways read the Camera Director bridge per panel so each splitscreen panel renders its own camera (independent orbit/height/zoom/tilt/pan), instead of all panels sharing the focused camera. - Add a shared `_freeCamFor(canvas)` resolver to each highway: prefer this panel's entry in `window.__h3dCamCtlPanels[panelIndexFor(canvas)]`, fall back to the global `window.__h3dCamCtl`, else null (100% stock). Defensive on the splitscreen global name (feedBackSplitscreen || slopsmithSplitscreen), NaN-safe, allocation-free. - highway_3d (guitar): source `_freeCam` from the resolver (was global-only). - keys_highway_3d: adopt the bridge for the first time — layer dolly/height/orbit + pan/pitch offsets onto the pan/zoom follow rig at the camera write. - drum_highway_3d: adopt the bridge — new per-frame `applyCamera()` folds the static base pose + kick-pulse dip + free-cam offsets. - In a follower (popped-out) window there is one panel, so the resolver yields whatever camera the plugin set in that window; no highway change needed for pop-out. Camera Director absent → resolver returns null → renderers behave exactly as before. Bump each plugin patch version. Existing plugin tests pass (drum 15, keys 30); the keys "default look unchanged" test confirms the stock path is byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com> |
||
|
|
b914612f9d |
fix(highway_3d): recover from WebGL context loss instead of crashing on alt-tab (#790)
Switching the active window / alt-tabbing away (most often on Windows) can trigger a GPU context reset. The 3D highway's WebGL renderer had no webglcontextlost handler, so a lost context was left to escalate into a render-process crash -- matching the intermittent "randomly crashes when I change windows" desktop reports. The renderer now binds webglcontextlost/webglcontextrestored on its own WebGL canvas (ren.domElement): the loss is preventDefault()'d so the browser keeps the context restorable, draw() bails while the context is down so no GL work runs on a dead context, and on restore the viewport is re-applied and rendering resumes (Three re-uploads scene resources on the next frame). Listeners are removed in teardown. Root cause is a strong hypothesis -- the crash is intermittent and unreproducible -- but the fix is low-risk and additive and closes a real gap: there was no context-loss handling anywhere in the renderer. plugins/highway_3d 3.31.2 -> 3.31.3. Tests: tests/js/highway_3d_context_loss.test.js (source-contract, like the other highway_* tests). The sibling keys_highway_3d / drum_highway_3d renderers share the same gap -- follow-up in their repos. Claude-Session: https://claude.ai/code/session_01UR2Cr7GEu3yMY7SrfxH6c1 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3e2703d8e1 |
feat(keys_highway_3d): audio-reactive ambience + score FX overlay (K4) (#709)
- BG_STYLES port (off/particles/lights/geometric; lights use the pitch-class palette) mounted behind the scene; _bgGetAnalyser/ _bgReadBands (stems-first, one-shot #audio fallback, permanent-failure latch); Ambience intensity + Audio-reactive settings; remounts on style/intensity change - Score-FX overlay canvas (drum_highway_3d pattern): +1 pops at the scored key, ring pulse every 10-combo, milestone bursts at 25/50/100, red wash on 3+ streak break (wrong notes AND swept misses); cleared when idle, removed in teardown - Tests: style id validation + FX defaults (30 total) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
95cb51b2ad |
perf(highway_3d): forceSinglePass on transparent DoubleSide quads
The retrace after the label-swap fix showed getParameters unchanged (~2.5s / ~4% throttled main thread) — the real driver is Three r158+'s transparent-DoubleSide two-pass path: renderBufferDirect renders such objects back side then front side, setting material.needsUpdate BOTH times, i.e. a full getParameters/program-cache lookup twice per object per frame, plus double draw calls. (Found by reading the two-pass branch in the vendored three.module.min.js right next to the getParameters call site.) All 18 transparent DoubleSide materials in this renderer are flat unlit quads — technique markers, sustain rails, chord frames, lane planes, halo bars — where the two-pass self-occlusion ordering buys nothing. Declare forceSinglePass: true on all of them. Also corrects the _setLabelMap comment's churn attribution (that fix removes the label-swap contribution; this one removes the dominant source). Plugin 3.31.1 -> 3.31.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
59aa70ce5a |
perf: remove throttled-trace residuals — program churn, per-frame rect, HUD clock
A 4x-CPU-throttled retrace (the honest weak-hardware proxy) surfaced three residual per-frame costs; stack attribution pinned each: - getParameters/getProgramCacheKey (~4% of main thread): every pooled label sprite map swap set material.needsUpdate, bumping material.version and forcing full program re-resolution next render. Swapping between two non-null cached textures never changes the compiled program (USE_MAP define unchanged) — new _setLabelMap() helper only flags needsUpdate on a null<->texture transition, used at all 7 swap sites. - getBoundingClientRect (~1.2%): the 3D highway's per-frame canvas-size self-check forced a layout read every frame. The CSS-box drift read now runs every 10th frame (or when the wrap isn't pinned); the backing-store comparison stays per-frame with cheap property reads and forces an immediate box read + applySize when it fires. - set textContent: the core 60 Hz HUD clock rewrote hud-time (and getElementById'd it) every tick for a display that changes 1/s — now write-on-change with a cached element ref. (The remaining textContent writer in the trace is notedetect's badges.js — external repo, to be filed there.) tests/js: resize-reframe shape test updated for the hoisted _bsChanged gate, incl. an assertion that the throttle can never delay the backing-store path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1e9741043b |
review: address PR #694 findings
- isVisible() forces a fresh DOM sample (was serving the rAF loop's
throttled cache, contradicting its 'live DOM check' docstring).
- v3 chrome: reconcile the edge-driven overControls hover flag against
matches(':hover') on the throttled ~6 Hz tick — covers a missed
mouseleave (flag stuck true, transport never hides) and a re-created
#player-controls node with lost listeners.
- highway_3d pre-warm now also covers teachFg/teachSd label textures
and the technique sprite factories (mute X, hammer/pull triangles,
bend chevrons, slide arrows) per active-palette string colour, plus
a maintenance note tying new label styles to the warm list.
- Document that the visibility throttle's manual invalidations are
latency-only (periodic resample self-heals within ~10 frames), and
why highway_3d keeps its local lowerBoundT (downlevel hosts).
External-repo audit (finding 1): staffview, tabview, piano, drums,
keys_highway_3d, drum_highway_3d grepped — no cross-frame bundle
retention or bundle-identity checks found.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
77547af110 |
perf: allocation/scan hardening for weaker hardware
Static-analysis follow-ups to the trace-backed fixes; each is cheap insurance on machines where the profiled headroom doesn't exist. - highway.js: _makeBundle now mutates one persistent per-instance object instead of allocating a fresh ~35-field bundle every rAF frame (xN under splitscreen). Object identity is stable and meaningless; array fields still swap reference on chart changes, which field-identity caches rely on. Contract documented in both CLAUDE.mds. - highway.js: new bsearchTime (lower-bound on .time) windows the default 2D renderer's beat-line scan (was O(all beats) per frame); bundle.lowerBoundT / bundle.lowerBoundTime expose the searches to custom viz so they stop reimplementing visible-window culling. - highway_3d: localStorage 'h3d_full_sus' polled at ~1 Hz instead of every frame (synchronous storage read on the hot path). - highway_3d: drawLyrics caches the measureText row layout keyed on (lyrics ref, line index, shown count, font size, width) — per-frame work is now just drawing over cached widths. - tests/js: bundle source-shape assertions widened to accept the assignment form ([:=]) alongside the old object-literal form. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5239665e2b |
perf(highway_3d): pre-warm shaders and label textures at init
Trace showed frame spikes from Three.js first-use costs mid-song: shader program compilation (getParameters/getProgramCacheKey) and lazy texture uploads (texSubImage2D) whenever a chord name, section banner, or fret label first appeared. - ren.compile(scene, cam) after initScene (pools already warmed by feedBack#226, board built, background mounted) so programs compile during the load spinner. - Pre-rasterise + GPU-upload (ren.initTexture) the deterministic txtMat entries: fret numbers 0-24 in the noteFret/fretRow/ghostFret combos the per-frame paths request. - Chart-dependent labels (chord template names, section names) prewarm once on the first draw() after each init, when bundle arrays are guaranteed populated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
095d718b85 |
Address review: Reset on All restores defaults verbatim
The Reset handler forced base.enabled = true after copying _ASPECT_DEFAULTS (where enabled is false) — a leftover from when enabled controlled panel visibility. Visibility is now independent (Shift+A / ×), so drop the override and let Reset restore the defaults exactly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
1434eb6342 |
Address review: only register panes while the tuner is open
camUpdate registered every pane each frame regardless of whether the tuner had ever been opened, so window.__h3dAspectPanes could grow unbounded (prune runs only while the panel is open) and it ran even for users who never opt in. Gate _aspectRegisterPane behind __h3dAspectPanelOpen (same gate as the readout). The pane key is still resolved every frame so saved overrides keep applying; only the picker bookkeeping is deferred until the panel is open. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
24d24ef2cf |
Address review: resolve cache, Date.now fallback, prune-on-open, rename
- Memoize _resolveTuneFor per pane, invalidated by a revision bumped on every tune mutation (all writes funnel through _aspectPersist). Panes with an override no longer rebuild the merged object every frame; panes without one still return the base directly. - _aspectNowMs falls back to Date.now() when the Performance API is absent, so pane/readout pruning still works in older/borrowed contexts. - _setAspectPanelVisible prunes stale panes before the first dropdown build, so panes from a prior song/split don't flash until the first RAF tick. - Rename _abShortcutRegistered/_registerAspectAbShortcut to _tunerShortcutRegistered/_registerTunerShortcut — the shortcut opens/closes the tuner now, it isn't an A/B toggle. - Fix a stale 'pane1' example in a comment (keys are 'arr:<name>'/'pane:<uid>'). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
58047e6ad6 |
Address review: force target to All when the pane picker is hidden
When only one pane is live the Target row is hidden, but _aspectEditTarget could remain a specific pane key — silently routing edits into a hidden (and persistent arr:*) override in single-player. Reset the edit target to "" in _aspectBuildTargets whenever the row is hidden (or the selected pane is gone), so single-pane edits always go to the shared base. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
817db6382b |
Address review: explicit button types + Target select label
- Set type="button" on the × close control and the Reset/Copy buttons so they can never act as submit if the panel is ever nested in a <form>. - Add aria-label="Target pane" to the Target <select> so screen readers can identify the control. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
9f914770c6 |
Address review: sparse overrides, hfov clear, readout prune
Three fixes from PR review of the per-pane tuner: - Sync no longer writes back. _syncAspectPanel dispatches synthetic input events to refresh slider labels; guard those with _aspectSyncing so the slider handler skips the write. Previously opening/switching a target populated a full override for every field (defeating sparse inherit) and spammed localStorage. - Unchecking "Override held hFOV" on a pane target now clears the override key (via _aspectClearVal) so the pane re-inherits the base value, instead of pinning hfovDeg:null in the override. On the base target it still sets the explicit auto (null). - _aspectPrunePanes now prunes the matching __h3dAspectReadout slot and drops a dangling __last, so the readout cache can't grow unbounded as songs and arrangements churn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
5ef163e9f9 |
Key wide-pane overrides by arrangement, not the split panel index
The Target picker disappeared in split because it keyed panes off the
external splitscreen panel index (panelIndexFor), which isn't always
available — both panes then collapsed to a single 'main' key and the
one-pane row-hide kicked in.
Key panes by arrangement name instead ('arr:Bass'): distinct between split
panes AND stable across songs, with no dependency on the split plugin. A
per-instance id ('pane:N') is the fallback when a pane has no arrangement.
Only arr:* overrides persist to localStorage (instance-id fallback keys are
session-only, so they can't leak a new key each reload). This also gives
nicer semantics — a pane's framing follows its arrangement into the next
song.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: topkoa <topkoa@gmail.com>
|
||
|
|
64b95d6f34 |
Persist per-pane framing across songs via durable slot keys
Per-pane overrides were keyed by an ephemeral per-instance id, so leaving a
song and opening another rebuilt the renderer with a new id and the pane's
framing was lost.
Key overrides by the durable split slot again ('main' | 'panel<idx>', via
_bgPanelKey) so the same slot means the same pane across songs, and persist
__panels to localStorage. Keep the anti-flicker fixes that were the actual
cause of the earlier dropdown churn (prune stale panes, rebuild only on a
pane-set change, never rebuild while the select is focused). The slot key is
latched to the last real slot so a transient null from panelIndexFor during
a song/layout transition can't flip it to 'main' and drop the override for a
frame; it resets in destroy() for instance reuse in another slot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: topkoa <topkoa@gmail.com>
|
||
|
|
6c42d83c31 |
Fix flickering / wrong panes in the wide-pane Target picker
The Target dropdown keyed panes off feedBackSplitscreen.panelIndexFor, which can return the focused index for any canvas — so both split panes' keys ping-ponged, rebuilding the <select> every frame (flicker) and listing wrong/duplicate entries. The registry also never dropped panes from a prior song or a closed split. - Key each pane by a stable per-renderer-instance id (_paneUid, assigned once in init) instead of the split panel index. - Prune panes not reported within ~1.5s (song change / split teardown). - Mark the dropdown dirty only when the pane SET changes, not on every per-frame re-report, and skip rebuilding while the <select> is focused. - Hide the Target row entirely when there's a single pane. - Label panes by arrangement name, falling back to "Pane N". Per-pane overrides are now session-only (keyed by ephemeral instance ids), so they're no longer persisted to localStorage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
9f0d48cb1f |
3D highway wide-pane tuner: dismiss + per-pane targeting
Two usability gaps in the wide-pane framing tuner: - No way to dismiss the panel. Add a × close button to the header and make the Shift+A shortcut open/close the panel (reveal/dismiss). The A/B enabled toggle now lives as a checkbox in the panel, so closing the panel no longer changes the framing state. - Edits hit every split pane at once. Add a Target selector (All panes, or a specific pane labelled by its arrangement, e.g. "Panel 1 — Rhythm"). Per- pane edits write a sparse override map (__panels[key]); each renderer resolves the shared base with its own pane's overrides laid on top via _resolveTuneFor(paneKey), so one pane can be framed independently. Reset on a pane clears its override (re-inherits the base); Copy exports the resolved values for the selected target. The live readout is keyed per pane. Panes are discovered from the existing per-panel key (_bgPanelKey / feedBackSplitscreen.panelIndexFor) and self-register each frame for the picker. Overrides persist to localStorage alongside the base. Tests extended in tests/js/highway_3d_wide_fov.test.js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> |
||
|
|
3e3a98a0d0 |
Aspect-aware framing for ultra-wide 3D highway panes (#652)
* Add aspect-aware framing for ultra-wide highway panes On a top/bottom 2-player split each 3D highway pane is full-width / half-height (~32:9). The camera's vertical FOV was locked at a single value, so at that aspect the horizontal cone ballooned past 130deg and squeezed the fixed-width neck into a thin central sliver with large dead margins on either side. Add a "horizontal-FOV-hold" path: past a configurable start aspect the effective vertical FOV is lowered so the horizontal cone stays roughly constant, letting the neck fill a wide pane. At/under the start aspect it is an exact no-op, so normal ~16:9 single-player and most 2x2 panes are unchanged. Optional pose nudges (height / dolly / pitch / look-depth) further flatten the view toward a low, immersive angle. Everything is driven by a runtime bridge (window.__h3dAspectTune) with a live tuner panel (Shift+A in the player) exposing every knob plus a live aspect/FOV readout, localStorage persistence, and a Copy button. Toggling the feature off restores the exact prior framing, so it doubles as an A/B control. Shipped on by default for wide panes for testing feedback. Source-pinned by tests/js/highway_3d_wide_fov.test.js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: topkoa <topkoa@gmail.com> * fix(highway_3d): ship wide-pane framing default-OFF with a coherent config Review fixes for the aspect-aware framing. The first cut shipped _ASPECT_DEFAULTS = { enabled:true, baseVfov:30, blend:0, minVfovDeg:36 }, which contradicted the PR's own "default off → byte-for-byte prior behaviour" claim: - enabled:true made the tune active for everyone, and baseVfov:30 forced every pane's vertical fov from 70° to 30° (normal single-player/2x2 panes included — a drastic global zoom, not the advertised no-op). - blend:0 collapsed the Hor+ math back to base, so the actual horizontal-FOV- hold did nothing even on wide panes — the only net effect was the zoom. - minVfovDeg:36 > baseVfov:30 was an inverted floor (clamped wide panes UP to 36° rather than flooring a real reduction). New defaults: { enabled:false, baseVfov:BASE_VFOV(70), blend:1, minVfovDeg:HORPLUS_MIN_VFOV(28) }. Now: - OFF by default → camUpdate passes a null tune → effectiveVfov returns BASE_VFOV → exact no-op on every pane (verified: 70° at 16:9 and 32:9). - When a tester enables it (Shift+A), baseVfov==BASE_VFOV keeps normal/≤start panes at 70° (still a no-op there) and blend:1 makes the hold actually engage on genuinely wide panes (47.7° at 32:9, flooring toward 28° as aspect grows). - minVfovDeg < baseVfov is a real floor. Also bumps the localStorage key (h3d_aspect_tune → h3d_aspect_tune2) so a machine that persisted the old broken default gets the corrected one, and adds source-pin tests guarding default-off + the coherent base/blend/floor so this can't silently regress to default-on again. The pose-nudge values are left as the author's in-progress wide-pane look (dormant until enabled). 110/110 tests pass; node --check clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Signed-off-by: topkoa <topkoa@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: byrongamatos <xasiklas@gmail.com> |
||
|
|
3120ae3e71 |
fix(highway_3d): dolly back so the fret-number row can't clip off the bottom (#633)
The heat-coloured fret-number row is drawn as a band BELOW the board (sY(lowest) - S_GAP*1.4), but camUpdate's self-correcting framing only anchors the board CENTRE to the lower third of the screen and reserves no headroom for that row. So a tight zoom on a centred active span (worst mid-neck; fine at either end of the neck) pushes the numbers past the bottom edge -- which is why testers saw it "only when centered" and "not every song." Tilt can't fix it (it would only trade a bottom clip for a top clip); the vertical-extent problem at tight zoom needs camera distance. Add a fret-row fit guard: project the row band with the final camera and, when it falls below FRET_ROW_FIT_NDC_MIN, raise a capped, hysteretic _fretRowFitBoost applied to the curDist lerp target (the span-driven tgtDist still owns zooming IN). The boost rises promptly (proportional to the deficit), relaxes lazily past a deadband, and is capped at FRET_ROW_FIT_BOOST_MAX (+60%) so the zoom can't pop or hunt. It cooperates with the tilt loop (pull-back shrinks the scene, tilt keeps the centre anchored) and yields entirely to the Camera Director free-cam. Surgical: passages where the row is already visible never trigger it, so framing is unchanged everywhere it already worked. plugin.json 3.30.0 -> 3.30.2 (screen.js cache-buster; 3.30.1 is taken by the FPS-counter PR). Tests: tests/js/highway_3d_camera_framing.test.js (guard constants, the boosted curDist lerp, the projected-row hysteresis, free-cam yield). Fixes #632 Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b29bab1884 |
fix(highway_3d): keep the FPS counter from hiding behind the v3 "Up Next" pill (#630)
The on-highway FPS readout (Settings -> Graphics -> 3D Highway -> Show FPS counter) is pinned to the top-right of the highway overlay -- the same corner the v3 player chrome stacks its persistent Up Next pill and live-performance HUD into, on a higher layer that paints over the canvas. So the readout sat behind that chrome and couldn't be read, exactly when a tester turned it on to judge performance (and because the pill is default-on it covered the counter regardless of the separate "Up Next won't turn off" report). Keep it top-right (where testers look) but drop it just below whichever of that chrome is showing: measure the lowest visible top-right v3 HUD element (#v3-upnext / #v3-live-performance-hud / #hud-time) and floor the FPS box's Y beneath it. Element refs are resolved once and cached (no per-frame querySelector, per the plugin perf rules) and only read while the counter is actually drawn; gated on window.feedBack.uiVersion === 'v3' so classic v2 is byte-for-byte unaffected. Bump plugin version 3.30.0 -> 3.30.1 (the screen.js cache-buster). Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
290783b80b |
feat(highway_3d): hit-feedback juice + Hit-sparks toggle (#618)
* feat(highway_3d): hit-feedback juice — cinematic lighting, strike line, sparks, intensity dial Charrette wave 1 (additive, default-tasteful, all behind settings): - #8 Hit-feedback settings: hitFx (0..1), cinematic, verdictMarks, timingFx, streakFx in BG_DEFAULTS + h3dBgSet* setters + settings.html (intensity slider + cinematic toggle). hitFx=0 → colour verdict only. - #2 Cinematic lighting: ambient 0.85→0.35 + stronger key light when cinematic on, so emissive gems have a dark surround to pop against. Live-toggleable. - #1 Strike line: a glowing bar at the hit line (Z=0) that flashes green on a verified hit / red on a miss, eased from the per-frame verdict alpha. - #3 Hit sparks: a pooled additive Points burst at the gem on a verified hit (deduped one burst per note), scaled by hitFx; disposed on teardown. Staged for wave 2 (after dogfooding): bloom+ACES (#4), colorblind verdict glyphs (#6), early/late timing tint (#5), streak heat + clean-bar (#7), gem scale-punch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * feat(highway_3d): wave 2 — gem scale-punch, streak heat, colorblind verdict marks - #3 (completion) gem scale-punch: the hit gem briefly grows (1 + 0.22·hitFx·alpha), biggest at the strike and easing with the verdict — the per-gem impulse. - #7 streak heat: a renderer-side consecutive-hit counter eases a 0..1 "heat" (plateau at 16) that grows the spark burst + warms the strike-line idle glow; a miss eases it back down. Behind the Streak-feedback toggle. - #6 colorblind verdict marks: a redundant ✓ (hit) / ✗ (miss) glyph on the verdict via the existing 2D label overlay, so the green/red pair isn't the only signal — notably also covers the provider path (where the timing labels don't show). - settings.html: Streak-feedback + Accessible-marks toggles. Deferred: #4 bloom+ACES (needs the Three.js postprocessing addons vendored into core static/vendor/three/ — not present; warrants its own infra change), and #5's timing tint (the early/late ±ms labels already render on the event path; surfacing them on the provider path needs a notedetect verdict field — a cross-plugin item). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * feat(highway_3d): #4 bloom + ACES — vendored Three.js postprocessing, perf-gated The single biggest fidelity lever from the charrette. Core had only three.module.min.js (no postprocessing addons), so this vendors the r170 EffectComposer/RenderPass/UnrealBloomPass/OutputPass + their shader deps into static/vendor/three/addons/, with every `from 'three'` rewritten to the SAME vendored three (../../three.module.min.js) so the addons share the plugin's three instance (a CDN copy would be a second, non-interoperable module). highway_3d wiring: - Lazy-loads the addons only when the new `bloom` setting is on (dynamic import), builds EffectComposer(RenderPass → UnrealBloomPass(strength .65/radius .5/ threshold .82 — high so only emissive gems + the hit flash bloom) → OutputPass). - Render loop uses composer.render() with ACES tone-mapping when bloom is active, else the unchanged direct ren.render() with NoToneMapping (bloom-off = today's look). - Perf-gated: OFF in splitscreen; graceful fallback to direct render if the modules or composer fail; composer.setSize on canvas resize; disposed on teardown. - settings.html: "Glow bloom" toggle (default on). Verified the import chain resolves + renders via a same-origin module-load test (EffectComposer built + a bloom frame rendered, three r170). Charrette status: 7/8 (only #5's early/late timing tint remains — a notedetect verdict-field change, outside the highway). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * feat(highway_3d): #5 early/late timing — colour the hit feedback by timing Surfaces the detector's timing on every hit (the charrette's last item), fully highway-side: notedetect already dispatches the judgment (timingState/timingError) on notedetect:hit/miss, so we carry timingState onto the event mark and tint the hit's spark burst + the ✓ verdict glyph by it — on-time green, early cyan, late amber. Gracefully falls back to green when no timing is known (pure-provider path), so it never invents data. Behind the new "Timing feedback" toggle (default on). Charrette: 8/8 complete. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * feat(highway_3d): add a "Hit sparks" on/off toggle (note-hit particles) The on-hit spark burst (the particle effect that fires the instant note_detect confirms a hit) could previously only be removed by dragging Hit-feedback intensity to 0 — which also kills the strike-line flash and the scale-punch. Add a dedicated "Hit sparks" toggle (default on) under 3D Highway settings, in the hit-feedback group beside the intensity slider, that gates ONLY the spark particles; the strike flash and colour verdict are unaffected. Wired the same way as the sibling juice toggles: a `sparks` boolean in BG_DEFAULTS, in _BG_BOOL_KEYS, a window.h3dBgSetSparks setter, the per-instance _sparks state + settings re-read, and a guard on the _sparkBurst spawn. Reuses existing Tailwind utility classes, so assets/plugin.css is unchanged; plugin.json version bumped to 3.28.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(highway_3d): act on tester charrette — strike line, fog readability, AA Addresses the alpha-tester 3D-highway feedback thread via the design panel's recommendations: - Strike line (panel rec 1a): now a HIT-ONLY faint "now" line — flashes green on a confirmed hit, no red miss branch (misses already show at the gem: red wash + ✗). Moved off the bottom edge to the vertical CENTRE of the string field, which was the "incorrectly placed" complaint (it read as the board's lower border and fused with open-string gems on a miss). Added a "Strike line" on/off toggle (`strikeLine`, default on). - Horizon readability (#2): the note gems + their outlines are now fog-exempt (`material.fog = false` on mStr/mGlow/mStrHitOutline/mHitBright/mWhiteOutline/ mMissOutline), so upcoming notes punch through the distance fog and stay legible as they render in — the board, lane, sustains and scenery keep their atmospheric fog, so depth is preserved. - Cinematic lighting softened: cinematic ambient 0.35 -> 0.45 so the dark stage doesn't crush note/fret legibility. - Anti-aliasing under bloom (perf rec): give the bloom EffectComposer a multisampled (WebGL2 MSAA x4) HalfFloat render target. The default target had no `samples`, so bloom-on bypassed MSAA — the "too HD / jagged on Windows, fine on Mac" report (Mac only won via Retina supersampling). This is the highest-value, smallest fix for the jaggies. plugin.json -> 3.29.0. The renderScale quality-oscillation is core (static/highway.js) and will be a separate feedBack PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(highway_3d): remove the strike line; sparks-only hit feedback, subtler Second tester-charrette pass. The strike line (even hit-only/centred from the last pass) was still too distracting/confusing on a hit, so it's removed entirely — strings + fret markers already orient the player, and the hit is fully carried at the gem (bright outline + scale-punch + spark burst) with the timing-coloured ✓/✗ verdict as the knowledge-of-results channel. - Deleted the strike-line mesh, its per-frame update, the `strikeLine` setting (BG_DEFAULTS / _BG_BOOL_KEYS / setter / settings-load), the settings.html toggle, and the now-dead `_strikeLine`/`_ndHitFlash`/`_ndMissFlash` state + their verdict-block feeds. - Made the spark burst subtler now that it's the sole celebration: point size 1.7→1.0·K, opacity 0.95→0.8, burst count (7+13·hitFx)→(4+7·hitFx), radial speed (7+r·20)→(5+r·12)·K, life (0.40+r·0.28)→(0.30+r·0.16)s. - Toggles for Hit sparks and the ✓/✗ verdict marks already exist in settings (kept). Minimal hit-feedback set now: gem bright + subtle spark (celebration) + timing-coloured ✓/✗ (the KR) + ambient streak heat. plugin.json -> 3.30.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(highway_3d): hydrate hit-feedback settings controls from saved state The 7 new juice controls (Hit sparks, Cinematic, Streak, Verdict marks, Bloom, Timing, Hit-feedback intensity) were hard-coded to their default markup and never read back from localStorage when the settings panel reopened — so a saved non-default (e.g. Hit sparks off) showed as the default (checked) even though the renderer correctly honored it. The sibling controls in the same panel were already hydrated; this restores that pattern for the new ones. Reads h3d_bg_* directly; defaults mirror BG_DEFAULTS (all bools on, hitFx 0.70) and the _bgCoerceBool 'true'/'1' vs 'false'/'0' coercion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: byrongamatos <xasiklas@gmail.com> |
||
|
|
13bbfc0b3d |
refactor(highway_3d): move Butterchurn controls into settings.html (#600)
Addresses the altitude finding from the Butterchurn review: the visualizer's on/off + slider options shipped as a parallel UI (a ~140-line floating in-canvas control panel) separate from the plugin's standard settings panel. Move the standard controls (Background on, opacity, dim-behind-lane + strength, chart accents + strength, color tint + strength, guitar gain, song gain) into settings.html, using the plugin's normal settings UI. They persist into the same 'viz3d_settings' blob the controller already reads; a new module-scope window.h3dBcApplySettings() hook lets settings.html push changes to a mounted highway live (it invalidates the controller's settings cache and re-applies). The in-canvas panel is now ONLY the live preset browser (pick / favorite / ban / cycle / hold / meters) — things that are inherently live tools and don't belong in a static settings form. cyclePool/hold and the favorites/bans lists stay there; reads were made cache-safe (read fresh via _bcLoadSettings) so a settings.html write can't be clobbered by a stale captured reference. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6fa5aabba8 |
fix(highway_3d): re-home Butterchurn panel to a surviving highway (splitscreen) (#599)
The Butterchurn control panel is a singleton, created only when a controller is created and parented to that controller's wrap. In splitscreen the panel followed the last-created controller; when that controller was torn down, destroy() only removed the panel DOM if it was the LAST controller, so with another highway still alive the panel stayed orphaned on the destroyed wrap and the surviving highway was left with no visualizer controls. Track each controller's wrap (ctrl.wrap) and, on destroy with another controller still alive, re-home the panel+pane onto the surviving primary's wrap via _bcEnsurePanel (which moves them when connected, or rebuilds them on the survivor if the old wrap was already detached). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3bb55ab854 |
feat(highway_3d): Butterchurn visualizer background style (#598)
* feat(highway_3d): add Butterchurn visualizer background style Adds an opt-in "Butterchurn (visualizer)" option to the 3D Highway plugin's Background-style dropdown. When selected, the highway renders over a WebGL MilkDrop (Butterchurn) canvas that reacts to your playing (guitar input on desktop, the song <audio> spectrum in the browser) and the chart (beat/note/ chord accents + instrument-color tint). The default stays 'particles', so existing users see no change until they pick it. Integrates the standalone "3D Highway + Butterchurn" mod into the bundled renderer as the 'butterchurn' bg-style (not a fork): - a self-contained _bc* controller that lazy-loads the vendored butterchurn libs only when the style is selected; mount/unmount is driven idempotently by the existing bg-style lifecycle (_bcSyncMode in _bgMountStyle) plus an explicit teardown in destroy() - the renderer uses alpha:true with the transparent clear gated on the mode, so every other bg style stays byte-identical (opaque clear) - the fog-scenery <audio> tap is disabled while active to avoid a double createMediaElementSource on #audio - the mod's slopsmith* globals are adapted to the current feedBack* names and the vendored asset URLs repointed to /api/plugins/highway_3d/assets/ Vendors butterchurn.min.js + butterchurnPresets.min.js (MIT) + viz-worklet.js under assets/vendor/; see plugins/highway_3d/NOTICE for attribution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * fix(highway_3d): anchor Butterchurn panel to the highway, centered Addresses three issues found testing the visualizer control panel: - Attach the panel + preset pane to the 3D highway's `wrap` (position:absolute, pointer-events:auto) instead of position:fixed on document.body, so they sit on the highway's right edge and only exist while the highway is on-screen (no longer linger on the main menu / float at the app edge). - Re-home the singleton panel to the active highway wrap on mount, so it follows whichever highway is showing (e.g. moves off Virtuoso's embedded highway onto a normal song's highway) instead of sticking to the first one created. - Center it vertically (top:50% + translateY(-50%), folded into the slide transform) so a top overlay element no longer covers it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * fix(highway_3d): harden Butterchurn audio + lifecycle (review #597) Browser audio reactivity now REUSES the highway's existing shared analyser (the fog scenery's #audio / stems side-chain tap) instead of opening a second createMediaElementSource on #audio. The old _bcBrowserSource path: - threw InvalidStateError when the fog tap already owned #audio (default config), leaving the visualizer non-reactive in the browser, and could permanently disable fog reactivity if it tapped first (one-shot/element); - rerouted the song through a fresh, possibly-suspended AudioContext, which could MUTE playback when butterchurn was selected mid-song; - ignored the stems analyser, so it saw only silence on sloppak songs. _bcCreateController now takes an audioProvider (wired to _bgGetAnalyser) and connectAudio()s the shared AnalyserNode (a passthrough, so the fog's own reads are undisturbed). Also: - destroy() now closes the AudioContext when we own it (desktop / browser fallback), fixing a per-mount leak that hit the browser ~6-context cap after a few style toggles. The shared (fog-owned) context is never closed. - _bgApplyVenueSceneFog keeps the clear transparent while butterchurn is active, so the venue scene no longer occludes the visualizer. - _bcLoadLib no longer caches a rejected promise, so a transient vendor-load failure can be retried instead of disabling the feature for the session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(highway_3d): Butterchurn lifecycle + audio re-bind (Codex preflight) Local Codex preflight on the Butterchurn feature flagged four issues; all fixed: - WebGL context leak on teardown: destroy() (and the async-init failure path) now call _bcReleaseCanvasGL() to force WEBGL_lose_context before dropping the canvas, so repeated mount/toggle cycles can't exhaust the browser's WebGL context cap. - Stale shared analyser across songs: the browser path captured the analyser once at mount, so a sloppak stems swap (new analyser, often new context) left the visualizer reacting to a dead node. update() now compares the live _bgGetAnalyser() against what the controller actually bound (boundAnalyser(), guarded by ready()) and either reconnects (same context) or rebuilds the controller (context changed) via the proven destroy()+_bcSyncMode paths. - Half-mounted controller on createVisualizer failure: the async .catch now cleans up (closes an owned AudioContext, removes layers, marks dead) and _bcSyncMode retries when bcCtrl.dead(), instead of leaking and never recovering. - _bcFfIdx off-by-one dropped accents landing exactly on a seek/loop target time; it now uses strict < so the update walkers fire the boundary event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: ChrisBeWithYou <christian.a.cowan@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
97dae88860 |
feat(highway_3d): colour theming — string presets + Background/Highway scene themes (#596)
* feat(highway_3d): add one-click string-color presets
Adds 12 named string-color presets (Warm→Cool, Vivid, Colorblind-friendly,
Neon, Accessible, Warm Ember, Tape Deck, CRT Green/Amber, Pitch Ramp, Sunrise)
selectable from the 3D Highway settings panel.
Extends the existing core HWC (highway-color) subsystem in static/app.js with
HWC_PRESETS + applyHighwayStringPreset(), exposed on the existing facade as
window.feedBack.highwayColors.{presets, applyPreset}. The plugin settings page
renders the preset buttons from that core list and refreshes the per-string
pickers on apply. Purely additive — stock behavior is unchanged.
Scope: core static/app.js (the shared HWC facade both highways consume) plus the
highway_3d plugin's settings.html / screen.js / CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq
* fix(highway_3d): address review of colour-theming PR
- Rebuild assets/plugin.css so the new `flex-wrap` (preset row) and
`text-[10px]` (theme-dropdown helper) Tailwind classes are actually
compiled, and bump plugin.json 3.26.0 -> 3.27.0 so the <link>'s ?v=
cache-buster fetches the fresh CSS (per the plugin's build rule).
- Replace the mirror-at-every-read hwTheme migration with a one-time
backfill (persist hwTheme := bgTheme on first load, no emit). The two
scene-color axes are now genuinely independent: changing the Background
dropdown no longer silently retints the Highway surface/lane, and the
rendered highway can't disagree with the Highway dropdown value.
- Collapse the duplicated theme id-set in settings.html (two identical
<option> lists + VALID_BG_THEMES) into a single SCENE_THEMES source the
dropdowns and validator are generated from; sync points 4 -> 2.
- Update CLAUDE.md to document the backfill + reduced sync contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
|
||
|
|
af2949677a |
rename: slopsmith → feedBack, byron → got-feedBack (#537)
* Update GitHub repo references from feedback* to feedBack* * rename: slopsmith -> feedBack, byron -> got-feedBack Renames across the entire codebase: - slopsmith/Slopsmith/SLOPSMITH/SlopSmith -> feedBack/FeedBack/FEEDBACK/FeedBack - byron/Byron/Byrongamatos -> got-feedBack/got-feedBack/got-feedBack - /home/byron/ -> /opt/got-feedBack/ - byron@ougsoft.com -> hi@got-feedBack.org - github.com/byrongamatos/ -> github.com/got-feedback/ - com.byron. -> com.got-feedback. - SLOPSMITH_ env vars -> FEEDBACK_ with backward-compat fallback - Protocol/storage strings migrated with read-old/write-new pattern - window.slopsmith JS API -> window.feedBack (canonical) + backward-compat alias Refs: #rename-slopsmith * rename: complete regen against current main + fix backward-compat alias Regenerated the slopsmith->feedBack / byron->got-feedBack rename on top of current main (3 commits had landed since the branch: #572/#554/#574), resolving the four content conflicts in favour of main's newer content (autoplay/auto-exit, accuracy-badge, Virtuoso re-home, feedpak badge). Completion fixes on top of the mechanical rename: - Re-apply rename to post-branch content the original rename never saw: window.slopsmith(.Tour) consumers in lessons.js / notifications.js / onboarding-tour.js, and the matching JS + python tests (autoplay_exit, progression_*, test_feedpak_extension FEEDBACK_* env vars). The test env vars now match server.py (which reads FEEDBACK_SYNC_STARTUP / FEEDBACK_SKIP_STARTUP_TASKS), so the sync-startup test exercises the real path again. - Restore the window.slopsmith backward-compat alias dropped during conflict resolution, and move the bus aliases to AFTER the _feedBackExisting merge block so they reference the fully-assembled object (also fixes the loop_api.test.js API-surface regex, which the original PR latently broke). - Drop the stray empty data/web_library.db (runtime DB lives in CONFIG_DIR) and gitignore it. - Fix stale tone-source test: feed[dB]ack -> fee[dB]ack to match shipped source labels. Verified locally (org CI billing-blocked): JS 819/819 pass; pytest 1669 passed / 1683 collected with 0 import errors; zero residual slopsmith/byron except the two intentional window.slopsmith aliases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * rename: implement advertised backward-compat + prune dead community plugins Address gaps where PR #537's "Backward compatibility" section was advertised but not implemented, and clean up the community plugin list. Env vars (FEEDBACK_* canonical, legacy SLOPSMITH_* honoured): - New lib/env_compat.py (getenv_compat / env_flag_compat) + tests. server.py (_env_flag + all FEEDBACK_* reads), diagnostics_hardware, gp2midi and tailwind_rebuild now resolve the legacy alias, so existing SLOPSMITH_UI / SLOPSMITH_PLUGINS_DIR / etc. deployments keep working. - Fix the rename collapsing plugins/__init__.py and minigames/routes.py from `FEEDBACK_PLUGINS_DIR or SLOPSMITH_PLUGINS_DIR` into a redundant `FEEDBACK_ or FEEDBACK_` (the fallback was silently lost). Storage (app.js update-channel): - Read feedBack-update-channel, fall back to legacy slopsmith-update-channel, and clear the legacy key on write — so a user's update-channel preference survives the rename instead of resetting to "stable". Community plugin list (README): the rename rewrote third-party repo URLs we don't own. Probed every one; their owners never renamed, so: - Restore the 13 live community plugins to their real slopsmith-* names. - Prune 6 that are 404 to the public (topkoa splitscreen/stems, OmikronApex tuner, Jafz2001 nam-rig-builder, DeathlySin song-preview, Erikcb91 shuffle). - Fix a pre-existing Guitar Theory clone-command typo (nam-tone -> guitar-theory). Verified: env_compat 7/7, JS 819/819, pytest 1690 collected / 0 import errors, rename-sensitive + startup suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: byrongamatos <xasiklas@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
187d0bb978 |
fix(highway): stop stale viz frame bleeding through after switching visualizations (#565)
Switching between the 3D drum highway (renders onto #highway) and the 3D guitar highway (renders into its own .h3d-wrap overlay) left the previous drum frame showing through the gap the overlay did not cover. Core: _setRenderer now replaces #highway on a genuine viz change (keyed on viz id via _rendererVizKey, not object identity, so benign same-viz re-installs don't churn the canvas) as well as on a context-type change. highway_3d: applySize pins the .h3d-wrap overlay to #highway's exact box, derived from the same getBoundingClientRect measurements that size the renderer (sub-pixel correct under zoom). Re-pins once the canvas lays out (init race) and resets to the static anchor in the not-laid-out fallback. Reviewed locally via codex (5 rounds, converged clean). CI checks are the known org Actions billing block, not real failures. |
||
|
|
fe8d30ce3e |
fix(highway): apply 3D fret-spacing live instead of reloading (#561) (#562)
window.h3dSetFretSpacing was the only 3D-highway setting that applied via location.reload(). The SPA boots with #home as the active screen and has no restore-last-screen mechanism, so the reload ejected the user from Settings onto the home screen. Apply it live like every other 3D-highway setting: rebind the module-scope _h3dFretUniform flag (so panels mounted later this session pick up the new mode), recompute the two fretX-derived scalars baked at init (_fretLabelScaleRefW, FRET_WIDTH_MID), and broadcast a 'fretSpacing' change over the existing _bgEmitChange pub-sub so every mounted panel rebuilds its board via buildBoard(). Per-frame note geometry already reads fretX live. Settings copy updated (no longer reloads) and tests/js pin the no-reload / live-rebuild behavior. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e518910baa |
feat(highway): render caged + guideTones teaching labels (§6.6) (#545)
Mirror the voicing/fn.rn teaching-mark render for the two new chord-template
fields, in both the 2D and 3D highways:
- Extend the shared pure chordHarmonyLabels() helper (identical in static/highway.js
and plugins/highway_3d/screen.js) to also surface caged ("CAGED: E") and
guideTones ("gt 4,10"), pre-formatted and node-testable. Invalid caged enum and
out-of-range / non-int guide tones are filtered out.
- Draw both, stacked above the existing rn/voicing labels, in distinct colors.
- Gated behind the SAME teaching-marks toggle (_showTeachingMarks 2D /
teachingMarksVisible 3D) — no clutter on the default highway.
Render only — no scoring / NoteVerifier coupling.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
3fc077cbc1 |
feat(highway): render chord harmony fn.rn + voicing on 2D + 3D (§6.3.1, §6.6) (#541)
* feat(core): carry chord harmony fn + template voicing on the wire (§6.3.1, §6.6)
Add two OPTIONAL per-chord harmony annotations (feedpak 1.7.0), mirroring the
teaching-marks (fg/ch/sd) wire work:
- Chord.fn (instance): {rn, q, deg} harmonic-function object, key-dependent.
Validated by _validate_fn on BOTH decode and emit so a partial / out-of-range
fn (which would fail the schema's required-keys rule) never rides the wire.
Default-omitted, mirroring bend bnv.
- ChordTemplate.voicing (template): key-independent voicing-type string
("open", "triad", "shell", "drop2", "barre", ...). Emitted only when
non-empty; non-string wire values fall back to "".
Display/teaching only — never fed to a grader (honesty rule). fn auto-derivation
is DEFERRED (carry-only): a complete rn/q needs chord-quality analysis, and a
deg-only fn would be schema-invalid, so server.py carries author-provided fn
unchanged. GP import unchanged (no reliable per-chord function/voicing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(highway): render chord harmony fn.rn + voicing on 2D + 3D (§6.3.1, §6.6)
Draw the chord's harmonic-function Roman numeral (instance fn.rn) and its
template voicing string, stacked above the chord name on both highways. A shared
pure helper chordHarmonyLabels(fn, voicing) formats the two labels (empty when
absent/malformed) and is node-tested against both files.
Both labels are gated behind the EXISTING teaching-marks opt-in
(_showTeachingMarks / teachingMarksVisible bundle flag) — they're chord-level
teaching overlays, same class as sd/ch, so they stay off the default highway.
2D guards the empty-note-chord case; 3D reuses the gold chord-label sprite style.
Render only — no scoring / NoteVerifier path is touched (honesty rule).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f182bd0ab7 |
fix(highway): make fret-hand finger (fg) hints hideable (default on) (#539)
Post-merge review of #538 noted the fg finger numeral rendered unconditionally on both highways and couldn't be turned off — only sd/ch sat behind the (default-off) teaching-marks toggle. A user who finds per-note numerals busy had no way to declutter. Add a SEPARATE finger-hints gate that keeps fg shown by default but makes it hideable, independent of the sd/ch opt-in (so the two defaults — fg on, sd/ch off — coexist; a single boolean can't express that): - 2D static/highway.js: _showFingerHints (localStorage 'showFingerHints' !== 'false', i.e. default on), a fingerHintsVisible bundle flag, and get/toggle/setFingerHintsVisible API; gates the fg label. - 3D plugins/highway_3d/screen.js: mirrors via bundle.fingerHintsVisible !== false (default on); gates the fg sprite. sd/ch unchanged. Default-on preserved (absent localStorage / absent bundle flag => shown); only an explicit false hides fg. Codex-reviewed: clean. Render test 7/7. Part of got-feedback/feedback#334. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0f1006972b |
feat(highway): render teaching marks fg/ch/sd on 2D + 3D (§6.2.2) (#538)
Render the three per-note teaching marks on both highways, mirroring the bend-curve render (#532). Display only — no scoring / NoteVerifier coupling. - 2D static/highway.js: fg renders by default as a small finger numeral hugging the gem (T = thumb, 1..4); sd (degree label) and ch (strum bracket connecting notes that share a ch key, arrow direction from pkd) are opt-in behind a new `showTeachingMarks` toggle (exposed via toggle/get/set + the bundle's `teachingMarksVisible` flag). Pure helpers teachingFingerLabel / teachingDegreeLabel / strumGroupBuckets drive the glyphs. ch bracket is note-stream-only (chord notes already read as one gesture). - 3D plugins/highway_3d/screen.js: fg (default) + sd (opt-in, mirrors the 2D toggle via bundle.teachingMarksVisible) render next to the per-note fret label via a new pooled sprite (pTeachMarkLbl); _scrChordNote resets fg/sd so chord notes don't inherit stale marks. ch strum brackets are deferred in 3D (no cross-note batch pass in the per-note render); 2D covers ch. Tests: tests/js/highway_teaching_marks.test.js extracts the pure helpers from both files (extract-and-eval) and asserts label mapping + strum-group bucketing. Part of got-feedback/feedback#334 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a858617d71 |
fix(bend): GP8 short-bend curve loss + 2D curve timing + 3D bnv gating (#535)
Post-merge Codex review of the bend-curve PRs (#531/#532) surfaced edge cases: - GP8 (#531 P2): bnv timing used rn.sustain, which is zeroed for notes <= 0.2s, so short GP8 bends kept the scalar bn but lost bt/bnv. Use the beat duration `dur` (matching the GP5 path) so the curve survives. - 2D highway (#532 P2): bnvNormalizedPoints mapped x over the curve's own t-range [first,last] instead of the note span, so curves not starting at 0 / ending at sus were time-distorted. Now maps over [0, sus] (clamped), with a curve-span fallback when sus<=0 (existing no-sus callers unaffected). - 3D highway (#532 P3): the sustain ribbon + bend chevron were gated on bn>0, so a note carrying an authoritative bnv with bn==0 drew no ribbon/marker. Both now also fire on bnv presence; chevron steps derived from max(bn, bnv peak). Codex-reviewed: clean (no findings). +1 JS test (sus-relative mapping + fallback). JS 8/8, 250 core GP/song tests pass. NB: GP8's short-bend path still lacks a dedicated synthetic-GPIF fixture (same gap as the GP8 offset-prop-names P3) — _gpx_bend_shape units cover the function; the fix is the one-line caller change. Part of got-feedback/feedback#334. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
351b273ab5 |
feat(highway): render per-note bend curve (bnv) on 2D + 3D (#532)
PR-B of the bend-shape feature (feedpak §6.2.1). Both highways drew a bend
from the scalar `bn` only; now they trace the authoritative `bnv` curve
([{t, v}]) when present and fall back to the `bn` arc/envelope otherwise.
2D (static/highway.js drawNote): when a note carries `bnv`, draw the real
shape as a contour above the gem (round-trip rises then falls, pre-bend
starts high, release descends — `bt` is implicit in the point shape), with
an arrowhead only when the gesture ends rising. `bnvNormalizedPoints` maps
{t,v} to a 0..1 x span. The scalar-arrow path is preserved unchanged as the
fallback; the peak label is unchanged.
3D (plugins/highway_3d/screen.js): `bnvSampleAt` linearly interpolates the
curve (clamped to its endpoints) and `bendSemisAtTime` samples it when
present, else keeps the synthetic rise→hold→release envelope from `bn`. The
chevron count still comes from the peak. Fixed a stale-scratch hazard: the
reused `_scrChordNote` now resets `bnv`/`bt` (omit-when-default) after
Object.assign, mirroring the existing `fhm` reset, so a chord note without a
curve can't inherit the previous note's contour.
Render-only — no wire/schema change. Pure helpers covered by
tests/js/highway_bend_curve.test.js (interp, clamping, round-trip,
degenerate/empty); node --check passes on both files; full tests/js green.
Part of got-feedback/feedback#334
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4148b0e72e |
Purge external-format terminology from code, tests and docs
Reword comments/docstrings/strings and rename identifiers that referenced the external game and its file formats: - format-id "psarc" -> "archive"; local vars psarc_path -> song_path, psarc_base -> tone_base - lyrics provenance value "sng" -> "notechart" (legacy "sng" still accepted) - highway_3d fret-ghost scope value "rocksmith" -> "chords" (invalid/legacy values fall back to the default, preserving behaviour) - neutralise references in prose, test names/data, .gitattributes and docs No functional change beyond the renamed identifiers; all Python compiles. |
||
|
|
6c110398b4 | Clean release snapshot |