Summary
On the very first Play press after a sloppak with stems finishes loading, core.play() can be aborted by the stems plugin's takeover sequence, leaving the transport UI showing "Paused" even though the song may (or may not) actually be playing underneath.
Sequence
playSong() → togglePlay() (static/js/transport.js:310) calls audio.play() (native HTML5, since the stems plugin hasn't finished detecting the sloppak yet).
- Concurrently,
stems' onSongReady() (plugins/stems/src/main.js:772) detects the multi-stem sloppak and, once its audio shims are installed, calls nativeCorePause(core) (plugins/stems/src/main.js:815) to silence the native single-track playback before taking over with its own Web Audio-driven multi-stem transport.
- The in-flight native
play() promise rejects with AbortError: The play() request was interrupted by a call to pause().
togglePlay()'s catch block (static/js/transport.js:348-364) only has a carve-out for this exact shape of race when window._juceRerouteInProgress is set (the JUCE HTML5→native reroute case) — there is no equivalent carve-out for the stems plugin's HTML5→Web-Audio takeover, so the catch treats it as a real failure: S.isPlaying = false; setPlayButtonState(false);.
- Meanwhile,
stems has already captured play-intent correctly (S.pendingPlay) and resumes playback on its own transport once the graph finishes building (plugins/stems/src/main.js:928) — so audio may actually be playing, but the transport button/S.isPlaying state is now out of sync with it, OR (depending on shim install timing) the button and displayed time genuinely never resume.
Impact
- First Play click after a fresh page load intermittently shows "Paused" / doesn't produce audio, even though a second click doesn't reliably fix it.
- Reported in practice with a 6-stem sloppak (feedpakr-converted GP8 import), Docker/Chromium,
stems + stem_mixer plugins both installed.
Suggested fix
Extend the existing window._juceRerouteInProgress guard in togglePlay()'s catch block (static/js/transport.js:360) to also recognize an HTML5→stems-takeover reroute — e.g. a window._stemsRerouteInProgress flag (or equivalent) set by stems' onSongReady() around its nativeCorePause() call, mirroring how the JUCE case is handled, so togglePlay() doesn't stomp S.isPlaying/the button state when the pause was a deliberate takeover rather than a genuine failure.
Repro environment
docker compose up (this repo's own Dockerfile/docker-compose.yml)
- Sloppak song with 6 stems (guitar/bass/drums/vocals/piano/other),
feedpakr-imported from a GP8 file
stems + stem_mixer plugins both active
Summary
On the very first Play press after a sloppak with stems finishes loading,
core.play()can be aborted by thestemsplugin's takeover sequence, leaving the transport UI showing "Paused" even though the song may (or may not) actually be playing underneath.Sequence
playSong()→togglePlay()(static/js/transport.js:310) callsaudio.play()(native HTML5, since thestemsplugin hasn't finished detecting the sloppak yet).stems'onSongReady()(plugins/stems/src/main.js:772) detects the multi-stem sloppak and, once its audio shims are installed, callsnativeCorePause(core)(plugins/stems/src/main.js:815) to silence the native single-track playback before taking over with its own Web Audio-driven multi-stem transport.play()promise rejects withAbortError: The play() request was interrupted by a call to pause().togglePlay()'s catch block (static/js/transport.js:348-364) only has a carve-out for this exact shape of race whenwindow._juceRerouteInProgressis set (the JUCE HTML5→native reroute case) — there is no equivalent carve-out for thestemsplugin's HTML5→Web-Audio takeover, so the catch treats it as a real failure:S.isPlaying = false; setPlayButtonState(false);.stemshas already captured play-intent correctly (S.pendingPlay) and resumes playback on its own transport once the graph finishes building (plugins/stems/src/main.js:928) — so audio may actually be playing, but the transport button/S.isPlayingstate is now out of sync with it, OR (depending on shim install timing) the button and displayed time genuinely never resume.Impact
stems+stem_mixerplugins both installed.Suggested fix
Extend the existing
window._juceRerouteInProgressguard intogglePlay()'s catch block (static/js/transport.js:360) to also recognize an HTML5→stems-takeover reroute — e.g. awindow._stemsRerouteInProgressflag (or equivalent) set bystems'onSongReady()around itsnativeCorePause()call, mirroring how the JUCE case is handled, sotogglePlay()doesn't stompS.isPlaying/the button state when the pause was a deliberate takeover rather than a genuine failure.Repro environment
docker compose up(this repo's ownDockerfile/docker-compose.yml)feedpakr-imported from a GP8 filestems+stem_mixerplugins both active