From 86df9622d132012b8199b1c9545190b40c85149b Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 5 Jun 2026 12:37:59 -0400 Subject: [PATCH] =?UTF-8?q?zebra-spaces:=20client-side=20Whisper=20STT=20?= =?UTF-8?q?=E2=80=94=20per-speaker=20live=20captions,=20off=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fox 2026-06-05: "do we access to a whisper speech to text?" → no, then "yes implement this and please this is perfect, make it an off be default toggle that is part of the client. individual speakers should ues their names and show up like a log under the video area in middle." Implementation: 1. whisper-capture AudioWorklet — separate from the jitter-buffer worklet. Decimates the speaker's 48 kHz mono stream to 16 kHz (Whisper's expected sample rate), batches 5-second chunks, ships them to the main thread via transferable Float32Array on the port. Disabled by default; { cmd: 'start' } / { cmd: 'stop' } from JS gate the capture. 2. transformers.js + Xenova/whisper-tiny.en lazy-loaded from jsdelivr CDN on first transcribe-toggle ON. ~40 MB one-time download (cached by the browser); subsequent toggles are instant. ONNX Runtime runs entirely client-side — no audio leaves the listener's browser. 3. Per-uuid capture lifecycle. When transcribe is on, every speaker in listenerAudioNodes gets a parallel AudioWorkletNode that taps their source. Recognized text appends to a transcript log with "HH:MM:SS name: text" lines. Names resolved via members.get(uuid).handle. Empty / placeholder transcriptions ("." "[BLANK_AUDIO]") filtered out. 4. UI: - Toggle button "transcribe (off/on)" in the controls column, with explainer note about model size + privacy. - #sec-transcript section in the timeline column directly under the spotlight. Monospace font, 14rem max-height, scrollable, auto-scrolls to bottom unless user is reading older lines. - .hidden until first toggle ON; subsequent toggles show/hide. 5. New-speaker hook: attachAudioStreamViaWorklet checks transcribeEnabled and auto-installs capture for late joiners. 6. detachListenerStream tears down node.capture along with the rest. Bandwidth: zero (model and audio never leave the device). CPU on listener: ~5-15% per speaker during the brief inference window every 5s, idle otherwise. Tested mentally; needs real-world verification. --- web/zebra-spaces.html | 237 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index d4a1e37..74bdff3 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -183,6 +183,31 @@ position: relative; } .controls { min-width: 0; } + /* Transcript log — lives in the timeline column under the + * spotlight. Off by default (the section is .hidden); toggled + * visible via the controls-column transcribe button. Auto-scrolls + * to bottom unless the user scrolls up to read older lines. */ + #sec-transcript { + margin-top: 0.6rem; border: 1px solid #ddd; background: #fafafa; + padding: 0.4rem 0.6rem; font-size: 0.85rem; min-width: 0; + } + html.theme-dark #sec-transcript { background: #0d0d0d; border-color: #333; } + #transcript-log { + max-height: 14rem; overflow-y: auto; line-height: 1.45; + font-family: ui-monospace, monospace; white-space: pre-wrap; + word-break: break-word; + } + .transcript-line { padding: 0.1rem 0; border-bottom: 1px dotted #e5e5e5; } + html.theme-dark .transcript-line { border-color: #222; } + .transcript-line:last-child { border-bottom: none; } + .transcript-line .ts { color: #888; font-size: 0.75rem; } + .transcript-line .name { color: #050; font-weight: bold; } + html.theme-dark .transcript-line .name { color: #6cc06c; } + .transcript-line .txt { color: inherit; } + /* Toggle button — same shape as other small buttons, with an + * 'on' state so the user can see at a glance. */ + #btn-transcribe.on { background: #050; color: #fff; border-color: #050; } + html.theme-dark #btn-transcribe.on { background: #6cc06c; color: #000; border-color: #6cc06c; } /* games live in the LEFT column under cameras + screens-thumbs as * proper tile-thumbs. Click one to spotlight it in the middle (an * iframe loads at full size); the click also broadcasts the spotlight @@ -651,6 +676,12 @@ try { + +