jupyterlab_voice_capture_extension
Capture microphone audio in the JupyterLab browser tab and stream it to a server-side FIFO, so terminal applications running inside the container - notably Claude Code voice mode - can record from a microphone the container itself has no access to.
The container has no capture device; the browser does. This extension bridges that gap: the browser captures the mic, ships the audio over an authenticated websocket to a Jupyter server handler, and the handler writes raw PCM to a named pipe. A separate, out-of-scope plumbing layer (PulseAudio module-pipe-source + SoX) turns that pipe into the system default audio source.
How it works
- Capture - a microphone toggle in the status bar calls
getUserMedia; an AudioWorklet resamples to 16 kHz mono and encodes signed 16-bit little-endian PCM off the UI thread - Transport - 20 ms PCM frames (640 bytes) are sent as binary websocket messages to
…/jupyterlab-voice-capture-extension/stream, which lives under the Jupyter base URL and inherits Jupyter token auth - no new port is opened - Sink - the server handler writes each frame, in order, to a FIFO (default
/run/voice/pulseaudio.fifo); the PulseAudio reader creates the pipe (module-pipe-sourcerefuses a pre-existing one), so the handler attaches as writer, waits for the pipe to appear, and tolerates a not-yet-attached reader without blocking the server
Chain: browser mic → AudioWorklet (16 kHz mono s16le) → websocket → server handler → FIFO → (PulseAudio + SoX, out of scope) → terminal app.
Requirements
- JupyterLab >= 4.0.0
- A secure context (https or
localhost) - browsers only expose the microphone over a secure origin
Install
pip install jupyterlab-voice-capture-extension
Dependencies
- Python:
jupyter_serverandtraitlets, installed automatically with the package - System (only for the full voice chain into a terminal app): PulseAudio + SoX. Provision and verify them with the bundled CLI:
jupyterlab_voice_capture install # apt packages + /run/voice dir + client.conf + Jupyter config line (does NOT start the daemon)
jupyterlab_voice_capture start -d # start the PulseAudio daemon + pipe-source (run after install and each restart)
jupyterlab_voice_capture validate # check every component, print what to fix (--json for machine output)
jupyterlab_voice_capture stop # kill the PulseAudio daemon
See docs/jupyterlab-enable-claude-voice.md for the full setup and troubleshooting.
Usage
- Click the microphone icon in the status bar (or run Toggle Voice Capture from the command palette) to start capture
- On the first start the browser asks for microphone permission; the status label moves Disconnected → Connecting → Connected, the icon glows green while streaming, and the browser shows its active-microphone indicator
- Click again to stop - capture tracks are released and the browser indicator clears
- In the Error state (blinking icon) a click disconnects and returns the control to Disconnected; click once more to start capture again
- Only one tab streams at a time: starting capture in a second tab takes over and stops the first
Configuration
The sink FIFO path defaults to /run/voice/pulseaudio.fifo and is overridable via Jupyter server config:
c.VoiceCapture.sink_path = "/run/voice/pulseaudio.fifo"
Settings → Voice Capture has one option, Auto-connect on startup (autoConnect, default off): when enabled, capture starts automatically as JupyterLab loads instead of waiting for a click.
Uninstall
pip uninstall jupyterlab-voice-capture-extension
Troubleshoot
If you see the frontend extension but it is not working, check that the server extension is enabled:
jupyter server extension list
If the server extension is installed and enabled but you do not see the frontend extension, check the frontend extension is installed:
jupyter labextension list
Contributing
If you would like to contribute to this extension, please refer to the Contributing Guide.
Metadata
Release files for jupyterlab-voice-capture-extension 1.0.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jupyterlab_voice_capture_extension-1.0.10.tar.gz | 338.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jupyterlab_voice_capture_extension-1.0.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 407.2 kB
Release files / jupyterlab_voice_capture_extension-1.0.10.tar.gz
| Download URL | jupyterlab_voice_capture_extension-1.0.10.tar.gz |
|---|---|
| Size | 338.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f860cd71ae272977fe2758b6002467209a87a2a18f7ad45cb168ad9b77c4355
|
|
BLAKE2b-256 checksum How to use checksums |
e92ed8d3f9d844c17ecf3c4e3eb1cb4b438e4abecd5fdc74334ba8370b8a2518
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / jupyterlab_voice_capture_extension-1.0.10-py3-none-any.whl
| Download URL | jupyterlab_voice_capture_extension-1.0.10-py3-none-any.whl |
|---|---|
| Size | 69.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5a5273d6fb399154489bb394209f4ff41d67cdc30f05d19d0aae8dc46b58582c
|
|
BLAKE2b-256 checksum How to use checksums |
496985f81890fd3d6f1775f6e84cab09e7e3c599d530d1923345a11949bb87db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|