From 5fc18d242332bc4cb2f19a513456c5a365cfc76f Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Thu, 4 Jun 2026 10:33:32 -0400 Subject: [PATCH] zebra-report: deploy self-listener-mode include-self fix --- zebra-report/zebra-spaces.html | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html index 813657e..95ad5f1 100644 --- a/zebra-report/zebra-spaces.html +++ b/zebra-report/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');