From a044099de55509e64df7ba797519f3ac25ed66cd Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Wed, 3 Jun 2026 13:19:39 -0400 Subject: [PATCH] =?UTF-8?q?zebra-report:=20deploy=20=E2=80=94=20700ms=20ji?= =?UTF-8?q?tter=20buffer=20for=20cross-Wi-Fi=20voice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zebra-report/zebra-spaces.html | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html index 6b4fe68..6a64d39 100644 --- a/zebra-report/zebra-spaces.html +++ b/zebra-report/zebra-spaces.html @@ -2523,8 +2523,12 @@ function handleRemoteSfuTrack(ev){ logLine('', 'sfu: unknown kind '+kind+' from '+pubHex); return; } - /* mic audio — 400ms jitter-buffer target absorbs Wi-Fi peak jitter */ - try { if (ev.receiver) ev.receiver.playoutDelayHint = 0.4; } catch(_){} + /* mic audio — 700ms jitter-buffer target absorbs cross-Wi-Fi peak + * jitter and burst packet loss. 400ms was too tight for users on + * different APs / cellular hand-offs — under-runs were producing + * the 'robot voice' artifact. 700ms costs an inaudible bit of + * end-to-end latency in exchange for clean playback under loss. */ + try { if (ev.receiver) ev.receiver.playoutDelayHint = 0.7; } catch(_){} /* cache by full pubkey (already resolved above) so it survives the * member's session uuid changing across leave/rejoin */ sfuStreamsByPubHex.set(pubHex, ev.streams[0]); @@ -3494,9 +3498,10 @@ async function connectToPeer(uuid, weOffer){ let a = remoteAudio.get(uuid); if (!a){ a = document.createElement('audio'); a.autoplay = true; document.body.appendChild(a); remoteAudio.set(uuid, a); } a.srcObject = ev.streams[0] || new MediaStream([ev.track]); - /* 400ms jitter buffer — Wi-Fi peak inter-arrival is multiples of - * the smoothed jitter, so a generous buffer absorbs the bursts */ - try { ev.receiver.playoutDelayHint = 0.4; } catch(_){} + /* 700ms jitter buffer — matches the SFU path. Cross-Wi-Fi and + * cellular hand-off jitter ate into the old 400ms cushion and + * produced robot-voice artifacts on tighter networks. */ + try { ev.receiver.playoutDelayHint = 0.7; } catch(_){} stopMeter(uuid); startMeter(uuid, a.srcObject); }; pc.onicecandidate = (ev) => { /* using waitForIceGathering pattern, candidates ignored */ }; @@ -4086,8 +4091,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');