From 6cdf11ffdb171e8f692986d2509298108db5afb2 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Thu, 4 Jun 2026 10:33:16 -0400 Subject: [PATCH] =?UTF-8?q?zebra-spaces:=20self-listener=20mode=20includes?= =?UTF-8?q?=20own=20stream=20=E2=80=94=20solo=20host=20can=20monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous version skipped uuid === myUUID in the enrolment loop, so a host alone with listeners (the common solo-DJ case) flipped the toggle and got silence because there was nobody else to stream from. Include self in the loop — listeners hear every speaker including us, so "what listeners hear" must pull our own /stream too. Self has no remoteAudio entry to mute (self-echo skip in handleRemoteSfuTrack), so that part is a no-op for the self row. --- web/zebra-spaces.html | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 813657e..95ad5f1 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -4245,19 +4245,28 @@ async function enableSelfListenerMode(){ try { sendMicState(); } catch(_){} } let added = 0; + /* INCLUDE self in the enrolment — listeners hear every speaker + * (including us), so the canonical "what listeners hear" experience + * pulls our own /stream too. Mic is already muted, so the loopback + * doesn't create feedback; the user gets full broadcast monitoring, + * AND a solo-host alone with listeners still has something to play. */ for (const [uuid, mm] of members){ - if (uuid === myUUID || !canSpeak(mm.role) || !mm.pubkey) continue; + if (!canSpeak(mm.role) || !mm.pubkey) continue; let pubHex; try { pubHex = hex(unb64(mm.pubkey)); } catch(_){ continue; } if (streamMode.has(pubHex)) continue; streamMode.add(pubHex); startStream(uuid, pubHex); + /* mute the matched WebRTC remote so we don't hear both paths. + * Self has no remoteAudio entry (we never subscribe to ourselves + * via SFU — see the self-echo skip in handleRemoteSfuTrack), so + * this is a no-op for the self row. */ const w = remoteAudio.get(uuid); if (w) try { w.muted = true; } catch(_){} added++; } renderRoom(); - logLine('', 'self-listener ON — '+added+' peers on buffered HTTP path, mic muted'); + logLine('', 'self-listener ON — '+added+' peers on buffered HTTP path (incl. self), mic muted'); } function disableSelfListenerMode(){ if (!selfListenerMode) return; @@ -5042,8 +5051,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');