Skip to content

fix(venue/highway): flyover replay on arrangement switch, venue on Virtuoso, and the paused throttle starving the venue#968

Merged
byrongamatos merged 2 commits into
mainfrom
fix/venue-scope-and-arrangement-intro
Jul 14, 2026
Merged

fix(venue/highway): flyover replay on arrangement switch, venue on Virtuoso, and the paused throttle starving the venue#968
byrongamatos merged 2 commits into
mainfrom
fix/venue-scope-and-arrangement-intro

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Three bugs from a live career session.

1. Changing arrangement replayed the venue arrival flyover

changeArrangement() reloads the song through the normal load path, so highway.js re-emits song:loaded — same filename, new arrangement. The venue could not tell that from a fresh arrival, so it flew the camera in from the back of the room again, mid-set, every time the player switched lead → rhythm.

onSongLoaded now compares the filename. Same song → keep the video pipeline running and only re-sync the mood (quiet crossfade, never the intro). Different song → full teardown + flyover, as before.

2. The venue showed up on the Virtuoso highway

The venue was gated purely on isVenueViz() — the selected visualization, a global preference that says nothing about what is on screen. Virtuoso borrows the same highway_3d renderer for its practice charts, so with Venue selected it inherited the backdrop: crowd and stage behind a chromatic exercise.

Selecting Venue is a preference for the player, not a licence to paint the venue over anything else using the renderer. Now gated on viz AND screen, following screen:changed. Nothing else was needed — stop() already unbinds the videos from the renderer.

3. Pausing the song throttled the whole venue to ~10 fps

"when the song is paused the GPU stops being used? Everything around the highway drops fps by a lot"

draw() caps paused frames to one per _PAUSED_FRAME_INTERVAL_MS (100 ms). The rationale is stated plainly in highway-constants.js: a heavy WebGL renderer "does a full render every frame even while paused. That is pure waste."

That was true when a paused chart was a still picture. The venue broke the assumption — its video backdrop keeps playing and its crowd reacts on a clock of their own, and both are drawn into the same canvas as the notes. So a throttle aimed at static notes throttled the entire room: the scene only got a texture upload 10× a second while the transport sat paused.

Renderers can now declare their picture is not static while the chart clock is stopped — an optional needsContinuousFrames(). The throttle is skipped only when it returns exactly true, and the probe fails closed: a renderer that does not implement it, or one that throws, keeps the throttle unchanged. The GPU saving that motivated #654 survives everywhere it was actually valid.

highway_3d implements it and claims continuous frames only while a crowd video is genuinely rolling (bound, unpaused, not ended, readyState >= 2). With no venue pack — the common case — the paused scene really is static, so it keeps the throttle and the GPU still idles.

Tests

Every decision is pinned, and all new assertions fail against the pre-fix source:

  • arrangement switch vs new song — including the first load (must fly over) and a malformed payload (must not silently suppress the flyover forever)
  • the venue's screen scope — player yes; virtuoso / home / folder-library / settings / career no; a throwing document fails closed
  • the throttle capability — that it gates the early return rather than merely being called near it; that it fails closed on absent / non-function / throwing / truthy-but-not-true; and that the 3D renderer keys off the real video elements and can still return false

Two things worth calling out:

  • The existing syncViz test encoded the old contract (activate regardless of screen) — which is bug 2. It now states the new contract and additionally asserts the venue does not activate on virtuoso.
  • I added a guard test (Venue selected AND on the player → venue is active). Without it, every "not active" assertion could pass vacuously if the viz stub were wrong.

The throttle guards are source-level, matching highway_pause_throttle.test.js's existing approach — the draw loop owns the rAF + WebGL lifecycle and is deliberately not reproduced in a vm (see that file's header).

eslint 0 errors; JS 1202/1202; pytest 2597 passed.

Not verified

None of this is driven on-device — they are runtime/visual behaviours and I have no career gig running here. The logic is pinned; the flyover continuity, the Virtuoso screen, and the paused framerate all want an eyeball on the next build.

Summary by CodeRabbit

  • Bug Fixes

    • Improved venue crowd audio/crossfade behavior to distinguish arrangement switches from genuinely new songs, avoiding unnecessary intro replay and resets.
    • Limited the Venue 3D backdrop to the active song “player” screen so it won’t appear on other screens.
    • Prevented the paused-render throttle from freezing renderers that need continuous updates.
    • Added safer handling for when screen detection isn’t available.
  • Tests

    • Expanded unit coverage for arrangement detection, player-screen-only activation, fail-closed screen detection, and pause-throttle renderer capability behavior.

…he venue off other screens

Two bugs from a live career session.

1. CHANGING ARRANGEMENT REPLAYED THE ARRIVAL FLYOVER.

   changeArrangement() reloads the song through the normal load path, so
   highway.js re-emits `song:loaded` — same filename, new arrangement. The venue
   could not tell that from a fresh arrival, so it reset the machine and flew the
   camera in from the back of the room again, mid-set, every time the player
   switched lead -> rhythm. The player is already on stage.

   onSongLoaded now compares the filename. A repeat of the song already on stage
   keeps the video pipeline running and only re-syncs the mood: the performance
   restarts, so the loop follows the reset machine with a quiet crossfade, never
   the intro. A genuinely different song still gets the full teardown + flyover.

2. THE VENUE SHOWED UP ON THE VIRTUOSO HIGHWAY.

   The venue was gated purely on `isVenueViz()` — the selected visualization,
   which is a GLOBAL preference and says nothing about what is on screen.
   Virtuoso borrows the same highway_3d renderer for its practice charts, so with
   Venue selected it inherited the backdrop: the crowd and the stage behind a
   chromatic exercise.

   Selecting Venue is a preference for the PLAYER; it is not a licence to paint
   the venue over whatever else happens to be using the renderer. The venue is now
   gated on viz AND screen (`shouldBeActive`), and follows `screen:changed` — it
   tears down on leaving the player and rebuilds on return. Nothing else changes:
   stop() already unbinds the videos from the renderer, so deactivating is enough
   to clear the backdrop.

Tests: both decisions exposed as pure predicates and pinned — arrangement switch
vs new song (including the first load, and a malformed payload that must not
suppress the flyover forever), and the venue's screen scope. The existing syncViz
test encoded the OLD contract (activate regardless of screen), so it now states
the new one and additionally asserts the venue does NOT activate on virtuoso.

Includes a guard test: with Venue selected AND on the player, the venue IS
active — without it, every "not active" assertion could pass vacuously.

All 8 new/updated assertions fail against the pre-fix source. eslint clean;
JS 1199/1199; pytest 2597 passed.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Venue crowd loading distinguishes arrangement switches from new songs. Venue 3D rendering is restricted to the player screen, with runtime screen-change handling. Paused highway rendering now remains continuous when active venue crowd videos require animation. Tests cover these behaviors and defensive failure paths.

Changes

Venue behavior scoping

Layer / File(s) Summary
Arrangement-aware crowd loading
static/v3/venue-crowd.js, tests/js/venue_scope.test.js
Crowd state tracks the previous song filename, skips full arrival teardown for arrangement switches, and exposes tested arrangement-switch detection.
Player-screen scene activation
static/v3/venue-scene-3d.js, tests/js/venue_scene_3d.test.js, tests/js/venue_scope.test.js
Venue 3D activation requires the venue visualization and player screen, responds to screen changes, exposes screen-gating helpers, and fails closed when DOM lookup throws.

Continuous venue rendering

Layer / File(s) Summary
Renderer continuous-frame capability
static/highway.js, plugins/highway_3d/screen.js, tests/js/highway_pause_throttle.test.js
Paused-frame throttling is bypassed only when the renderer reports ready, non-ended crowd video playback requiring continuous frames; missing or failing capability checks remain throttled.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HighwayDraw
  participant Renderer
  participant Highway3D
  participant CrowdVideo
  HighwayDraw->>Renderer: needs continuous frames?
  Renderer->>Highway3D: needsContinuousFrames()
  Highway3D->>CrowdVideo: inspect playback state
  CrowdVideo-->>Highway3D: active or inactive video
  Highway3D-->>Renderer: true or false
  Renderer-->>HighwayDraw: bypass or apply paused throttle
Loading

Possibly related PRs

Suggested reviewers: chrisbewithyou

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the three fixes and tests, but it does not follow the required template or include the checklist items. Rewrite it using the repo template: add the What section, note feedpak impact or omission, and complete the Checklist with issue link, changelog, tests, and DCO status.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is specific and matches the main venue/highway fixes, though it is a bit long.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/venue-scope-and-arrangement-intro

Comment @coderabbitai help to get the list of available commands.

Pausing the song dropped the venue, the crowd and the stage to ~10 fps —
"everything around the highway drops fps by a lot".

draw() caps paused frames to one per _PAUSED_FRAME_INTERVAL_MS (100ms), on an
assumption stated plainly in highway-constants.js: a heavy WebGL renderer "does
a full render every frame even while paused. That is pure waste." That was true
when a paused chart was a still picture.

The venue broke the assumption. Its video backdrop keeps playing and its crowd
reacts on a clock of their own, and BOTH are drawn into the same canvas as the
notes — so a throttle aimed at static notes throttled the entire room. The
scene only got a texture upload 10 times a second while the transport sat
paused.

Renderers can now declare that their picture is not static while the chart
clock is stopped: an optional needsContinuousFrames(). The throttle is skipped
only when it returns exactly true, and the probe fails closed — a renderer that
doesn't implement it, or one that throws, keeps the throttle unchanged. So the
GPU saving that motivated #654 survives everywhere it was actually valid.

highway_3d implements it and claims continuous frames ONLY while a crowd video
is genuinely rolling (bound, unpaused, not ended, readyState >= 2). With no
venue pack — the common case — the paused scene really is static, so it keeps
the throttle and the GPU still idles.

Tests extend tests/js/highway_pause_throttle.test.js, which guards this code
path source-level (the draw loop owns the rAF + WebGL lifecycle and is
deliberately not reproduced in a vm — see the file header). The new guards pin
that the capability GATES the early return rather than merely being called near
it, that the probe fails closed on absent/non-function/throwing/truthy-but-not-
true, and that the 3D renderer keys off the real video elements and can still
return false. All 3 fail against the pre-fix source.

eslint 0 errors; JS 1202/1202; pytest 2597 passed.
@byrongamatos byrongamatos changed the title fix(venue): no flyover replay on arrangement switch; venue stays off the Virtuoso highway fix(venue/highway): flyover replay on arrangement switch, venue on Virtuoso, and the paused throttle starving the venue Jul 14, 2026
@byrongamatos
byrongamatos merged commit 4e0e3c5 into main Jul 14, 2026
6 checks passed
byrongamatos added a commit that referenced this pull request Jul 15, 2026
Tester, mid-gig: "the second song in the gig started when the first one ended.
But it showed the flyover intro again."

The flyover is arriving at the venue, and you arrive once. #968 stopped it
replaying on an arrangement SWITCH (same filename), but a gig's song 2 is a
genuinely different file, so it took the full-teardown path and played the
arrival flyover again — the camera flew in from the back of the room before
every track of the set.

The play queue now answers isContinuation(): false for the first song of a set
(or a standalone play — an arrival), true for song 2..N. onSongLoaded carries
the room over to the new song's loop on a continuation, and only a real arrival
plays the intro.

Verified on the built AppImage: isContinuation goes false (song 1) -> true
(song 2) across an advance, and song 2 no longer flies in.

Also confirmed NOT a bug, same session: "didn't show the author for the second
song." The credits card shows on a queue advance whenever the song carries
authors — reproduced with a song that has them as the advanced-to track. The
tester's song 2 simply had no `authors:` metadata (most auto-converted feedpaks
don't). No code change.

Tests: isContinuation across start/advance/clear, and that onSongLoaded gates the
flyover on the continuation check. Both fail on pre-fix source. JS 1214/1214.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant