diff --git a/simulator/e2e/tests/welcome-audio.spec.ts b/simulator/e2e/tests/welcome-audio.spec.ts
new file mode 100644
index 0000000..4a157cf
--- /dev/null
+++ b/simulator/e2e/tests/welcome-audio.spec.ts
@@ -0,0 +1,43 @@
+import { test, expect, Page } from "@playwright/test";
+
+// The welcome (opening) screen mirrors the panel controls. It must offer the Audio
+// SOURCE choice (None/Soundtrack/Music) too — so a visitor can pick their sound mode
+// before launching — carried into the session on entry.
+
+async function ready(page: Page) {
+ await page.waitForFunction(() => (window as any).__hefReady === true, null, { timeout: 45_000 });
+}
+
+test("the welcome screen has an Audio source dropdown with 3 options", async ({ page }) => {
+ await page.goto("/");
+ await ready(page);
+ await expect(page.locator("#welcome")).toBeVisible();
+ const opts = page.locator("#welcome-audio-source option");
+ await expect(opts).toHaveCount(3);
+ await expect(opts).toHaveText(["None", "Soundtrack", "Music"]);
+});
+
+test("choosing Music on the welcome screen carries into the session", async ({ page }) => {
+ await page.goto("/");
+ await ready(page);
+ await page.selectOption("#welcome-audio-source", "music");
+ await page.locator("#welcome-launch").click();
+ await page.waitForSelector("#welcome", { state: "detached", timeout: 10_000 });
+ expect(await page.locator("#audio-source").inputValue()).toBe("music");
+ await page.waitForTimeout(300);
+ const url = await page.evaluate(() => (document.querySelector("#aud") as HTMLAudioElement).dataset.url || "");
+ expect(url).toMatch(/audio\/music\/\w+\.mp3/);
+});
+
+test("welcome None greys out the welcome Volume slider", async ({ page }) => {
+ await page.goto("/");
+ await ready(page);
+ await page.selectOption("#welcome-audio-source", "none");
+ await expect(page.locator("#welcome-audio")).toBeDisabled();
+});
+
+test("?audio=music preselects Music on the welcome screen too", async ({ page }) => {
+ await page.goto("/?audio=music");
+ await ready(page);
+ expect(await page.locator("#welcome-audio-source").inputValue()).toBe("music");
+});
diff --git a/simulator/static/app.js b/simulator/static/app.js
index ea4f834..71729e2 100644
--- a/simulator/static/app.js
+++ b/simulator/static/app.js
@@ -1420,6 +1420,7 @@ function applyUrlAudioParams() {
const src = (p.get("audio") || "").toLowerCase();
if (src === "none" || src === "soundtrack" || src === "music") {
const el = $("audio-source"); if (el) el.value = src;
+ const w = $("welcome-audio-source"); if (w) w.value = src; // reflect on the opening screen too
}
const vol = p.get("vol");
if (vol !== null && vol !== "" && Number.isFinite(+vol)) {
@@ -1681,11 +1682,18 @@ function applyWelcomeToPanel() {
$("visual").checked = true; // the welcome screen assumes Video on (no toggle)
const wa = $("welcome-audio");
if (wa) { $("audio").value = wa.value; updateAudioLevelLabel(); }
+ const ws = $("welcome-audio-source"); // carry the chosen source into the panel dropdown
+ if (ws) { $("audio-source").value = ws.value; syncVolumeDisabled(); }
}
function updateWelcomeAudioLabel() {
const el = $("welcome-audio-val");
if (el) el.textContent = String(+($("welcome-audio") || {}).value || 0);
}
+// Volume is meaningless on None — grey the welcome slider too, mirroring the panel.
+function syncWelcomeVolumeDisabled() {
+ const src = $("welcome-audio-source"), slider = $("welcome-audio");
+ if (src && slider) slider.disabled = src.value === "none";
+}
// Remove the welcome overlay (fade out) — the panel reveals via `body:has(#welcome)`.
function enterSimulation() {
@@ -1718,6 +1726,9 @@ function initWelcome() {
updateWelcomeAudioLabel();
const wa = $("welcome-audio");
if (wa) wa.addEventListener("input", updateWelcomeAudioLabel);
+ const ws = $("welcome-audio-source");
+ if (ws) ws.addEventListener("change", syncWelcomeVolumeDisabled);
+ syncWelcomeVolumeDisabled();
const launch = $("welcome-launch");
if (launch) launch.addEventListener("click", onLaunch);
}
@@ -1748,6 +1759,7 @@ async function main() {
$("audio-source").addEventListener("change", () => { syncVolumeDisabled(); applyAudio(); debounced(); });
applyUrlAudioParams(); // shareable ?audio=/?vol= deep-link presets
syncVolumeDisabled();
+ syncWelcomeVolumeDisabled(); // reflect a URL-set source on the welcome slider too
updateWelcomeAudioLabel();
// Video toggle (panel): the FIRST time video turns on without having entered via
// Launch, begin the experience too — couple audio in. (Normally Launch entered
diff --git a/simulator/static/index.html b/simulator/static/index.html
index b0b6179..9b7eec7 100644
--- a/simulator/static/index.html
+++ b/simulator/static/index.html
@@ -26,6 +26,13 @@
list="play-speed-ticks" aria-describedby="welcome-play-speed-val" />
1.00×
+