const CROSSFADE_DURATION_MS = 5000; const CROSSFADE_INTERVAL_MS = 20; const AUDIO_DURATION_MS = 60000; const playBtn = document.getElementById("play-btn"); const catImg = document.getElementsByClassName("cat")[0]; const volumeSlider = document.getElementById("volume-slider"); const currentVolumeLabel = document.getElementById("current-volume-label"); const clickAudio = document.getElementById("click-audio"); const clickReleaseAudio = document.getElementById("click-release-audio"); const meowAudio = document.getElementById("meow-audio"); const listenerCountLabel = document.getElementById("listener-count"); let isPlaying = false; let isFading = false; let currentAudio; let maxVolume = 100; let currentVolume = 0; let ws = connectToWebSocket(); function playAudio() { // add a random query parameter at the end to prevent browser caching currentAudio = new Audio(`./current.mp3?t=${Date.now()}`); currentAudio.onplay = () => { isPlaying = true; playBtn.innerText = "pause"; if (ws) { ws.send("playing"); } }; currentAudio.onpause = () => { isPlaying = false; currentVolume = 0; playBtn.innerText = "play"; if (ws) { ws.send("paused"); } }; currentAudio.onended = () => { currentVolume = 0; playAudio(); }; currentAudio.volume = 0; currentAudio.play(); fadeIn(); setTimeout(() => { fadeOut(); }, AUDIO_DURATION_MS - CROSSFADE_DURATION_MS); } function pauseAudio() { currentAudio.pause(); currentAudio.volume = 0; currentVolume = 0; } function fadeIn() { isFading = true; // volume ranges from 0 to 100, this determines by how much the volume number // should be incremented at every step of the fade in const volumeStep = maxVolume / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS); const handle = setInterval(() => { currentVolume += volumeStep; if (currentVolume >= maxVolume) { clearInterval(handle); currentVolume = maxVolume; isFading = false; } else { currentAudio.volume = currentVolume / 100; } }, CROSSFADE_INTERVAL_MS); } function fadeOut() { isFading = true; // volume ranges from 0 to 100, this determines by how much the volume number // should be decremented at every step of the fade out const volumeStep = maxVolume / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS); const handle = setInterval(() => { currentVolume -= volumeStep; if (currentVolume <= 0) { clearInterval(handle); currentVolume = 0; isFading = false; } else { currentAudio.volume = currentVolume / 100; } }, CROSSFADE_INTERVAL_MS); } function animateCat() { let current = 0; setInterval(() => { if (current === 3) { current = 0; } else { current += 1; } catImg.src = `./images/cat-${current}.png`; }, 500); } /** * Allow audio to be played/paused using the space bar */ function enableSpaceBarControl() { document.addEventListener("keydown", (event) => { if (event.code === "Space") { playBtn.classList.add("button-active"); playBtn.dispatchEvent(new MouseEvent("mousedown")); clickAudio.play(); } }); document.addEventListener("keyup", (event) => { if (event.code === "Space") { playBtn.classList.remove("button-active"); clickReleaseAudio.play(); } }); document.addEventListener("keypress", (event) => { if (event.code === "Space") { playBtn.click(); } }); } function connectToWebSocket() { const ws = new WebSocket(`ws://${location.host}/ws`); ws.onmessage = (event) => { console.log(event.data); if (typeof event.data !== "string") { return; } const listenerCountStr = event.data; const listenerCount = Number.parseInt(listenerCountStr); if (Number.isNaN(listenerCount)) { return; } if (listenerCount <= 1) { listenerCountLabel.innerText = `${listenerCount} person tuned in`; } else { listenerCountLabel.innerText = `${listenerCount} ppl tuned in`; } }; return ws; } playBtn.onmousedown = () => { clickAudio.play(); document.addEventListener( "mouseup", () => { clickReleaseAudio.play(); }, { once: true }, ); }; // Cat audio sound function catImg.onmousedown = () => { meowAudio.play(); }; playBtn.onclick = () => { if (isPlaying) { pauseAudio(); } else { playAudio(); } }; volumeSlider.oninput = () => { maxVolume = volumeSlider.value; currentVolumeLabel.textContent = `${maxVolume}%`; if (!isFading && currentAudio) { currentAudio.volume = maxVolume / 100; currentVolume = maxVolume; } clickAudio.volume = volumeSlider.value / 100; clickReleaseAudio.volume = volumeSlider.value / 100; meowAudio.volume = volumeSlider.value / 100; }; volumeSlider.value = 100; window.addEventListener("offline", () => { ws = null; }); window.addEventListener("online", () => { ws = connectToWebSocket(); }); animateCat(); enableSpaceBarControl();