Spaces:
Running on Zero
Running on Zero
Add a café-jazz lobby bed that plays at startup and when idle
Browse filesA looping background track (frontend/lobby.mp3, LFS-tracked, served at /static/lobby.mp3) starts on the user's first gesture (browsers gate audio autoplay) and plays whenever the player is idle - no tape loaded, or paused. It fades out under a playing tape and back in when the tape stops, driven from the one play/pause chokepoint syncPlayState() -> updateLobby(). Track: Alex Morgan, 'Peaceful Cafe Jazz' (Pixabay, royalty-free). Also adds *.mp3 to Git LFS.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- .gitattributes +1 -0
- frontend/lobby.mp3 +3 -0
- frontend/ui.js +52 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
*.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
*.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
*.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
*.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
frontend/lobby.mp3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7f5abc44caa2c025a3ac37e2c35086e64e24a9a7db209f11ef27a565021b0de2
|
| 3 |
+
size 4245504
|
frontend/ui.js
CHANGED
|
@@ -99,6 +99,57 @@ export function initUI({
|
|
| 99 |
// so tapes are private and a reload starts the shelf empty. Newest first.
|
| 100 |
const sessionTapes = [];
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
function setStage(stage) {
|
| 103 |
controlsRow.classList.toggle("hidden", stage !== "prompt");
|
| 104 |
generating.classList.toggle("hidden", stage !== "generating");
|
|
@@ -433,6 +484,7 @@ export function initUI({
|
|
| 433 |
cassette.classList.toggle("playing", playing);
|
| 434 |
deck.classList.toggle("playing", playing);
|
| 435 |
syncCards();
|
|
|
|
| 436 |
}
|
| 437 |
|
| 438 |
function updateProgress() {
|
|
|
|
| 99 |
// so tapes are private and a reload starts the shelf empty. Newest first.
|
| 100 |
const sessionTapes = [];
|
| 101 |
|
| 102 |
+
// --- lobby music ------------------------------------------------------------
|
| 103 |
+
// A café-jazz bed that plays at startup and whenever the player is idle (no
|
| 104 |
+
// tape loaded, or paused), and ducks out under a playing tape. Browsers block
|
| 105 |
+
// audio autoplay until the user interacts, so it's primed on the first gesture;
|
| 106 |
+
// after that it just follows the player state via syncPlayState() -> updateLobby().
|
| 107 |
+
// Track: Alex Morgan — "Peaceful Cafe Jazz" (Pixabay, royalty-free).
|
| 108 |
+
const LOBBY_VOL = 0.4;
|
| 109 |
+
const lobby = new Audio("/static/lobby.mp3");
|
| 110 |
+
lobby.loop = true;
|
| 111 |
+
lobby.preload = "auto";
|
| 112 |
+
lobby.volume = 0;
|
| 113 |
+
lobby.id = "lobby-audio";
|
| 114 |
+
document.body.appendChild(lobby);
|
| 115 |
+
let lobbyFade = null;
|
| 116 |
+
function fadeLobby(target, durSec = 0.7) {
|
| 117 |
+
if (lobbyFade) clearInterval(lobbyFade);
|
| 118 |
+
const from = lobby.volume;
|
| 119 |
+
const steps = Math.max(1, Math.round(durSec * 30));
|
| 120 |
+
let i = 0;
|
| 121 |
+
lobbyFade = setInterval(() => {
|
| 122 |
+
i += 1;
|
| 123 |
+
lobby.volume = Math.max(0, Math.min(1, from + (target - from) * (i / steps)));
|
| 124 |
+
if (i >= steps) {
|
| 125 |
+
clearInterval(lobbyFade);
|
| 126 |
+
lobbyFade = null;
|
| 127 |
+
if (target === 0) lobby.pause();
|
| 128 |
+
}
|
| 129 |
+
}, 1000 / 30);
|
| 130 |
+
}
|
| 131 |
+
// Play the bed only while nothing else is — fade it out under a live tape.
|
| 132 |
+
function updateLobby() {
|
| 133 |
+
const tapePlaying = !!active.src && !active.paused;
|
| 134 |
+
if (tapePlaying) {
|
| 135 |
+
if (!lobby.paused) fadeLobby(0);
|
| 136 |
+
} else {
|
| 137 |
+
if (lobby.paused) {
|
| 138 |
+
lobby.volume = 0;
|
| 139 |
+
lobby.play().catch(() => {}); // gated on a user gesture (primed below)
|
| 140 |
+
}
|
| 141 |
+
fadeLobby(LOBBY_VOL);
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
// Autoplay needs a user gesture; kick the bed off on the first one.
|
| 145 |
+
function primeLobby() {
|
| 146 |
+
window.removeEventListener("pointerdown", primeLobby);
|
| 147 |
+
window.removeEventListener("keydown", primeLobby);
|
| 148 |
+
updateLobby();
|
| 149 |
+
}
|
| 150 |
+
window.addEventListener("pointerdown", primeLobby);
|
| 151 |
+
window.addEventListener("keydown", primeLobby);
|
| 152 |
+
|
| 153 |
function setStage(stage) {
|
| 154 |
controlsRow.classList.toggle("hidden", stage !== "prompt");
|
| 155 |
generating.classList.toggle("hidden", stage !== "generating");
|
|
|
|
| 484 |
cassette.classList.toggle("playing", playing);
|
| 485 |
deck.classList.toggle("playing", playing);
|
| 486 |
syncCards();
|
| 487 |
+
updateLobby(); // duck the café-jazz bed under a playing tape, restore it when idle
|
| 488 |
}
|
| 489 |
|
| 490 |
function updateProgress() {
|