OmniStream is an open-source audio broadcasting server that lets one laptop stream audio to any number of listeners over a local WiFi network. Listeners join through their phone or laptop browser — no app download, no account, no Bluetooth pairing required. Every listener hears the same moment of audio, synchronized to within 5 milliseconds.
You have one laptop playing a movie, a playlist, or a game. Everyone else in the room has their own phone and earbuds but no way to hear what's on the laptop.
Common scenarios where this matters:
- Movie night — one laptop, five people, weak speakers. Everyone wants their own earbuds.
- Road trip — one person controls the music, everyone else wants to hear it without sharing.
- Flights — one downloaded movie, multiple seats. No internet needed, just a hotspot.
- Study group — shared focus music without bothering others in a library.
- No external speaker — use everyone's phone earbuds as distributed speakers.
OmniStream connects the laptop (host) to every phone in the room (listeners) over WiFi. Works offline. Works on a hotspot. No internet required.
The host laptop runs a Python server. Anyone on the same network opens the server URL in their browser and enters a room code. The host drops an audio file on the dashboard (or streams system audio), clicks play, and every listener hears it.
Under the hood:
- Audio is converted to raw PCM by FFmpeg and sliced into 20ms packets
- Each packet is stamped with a server timestamp and sent over WebSocket
- Every listener's browser runs an NTP-like clock sync protocol to calculate its exact offset from the server clock
- Packets are buffered, reordered if needed, and played at the mathematically precise moment so all devices stay in sync
- Result: less than 5ms difference between any two listeners
| Host (runs the server) | Listener (opens browser) | |
|---|---|---|
| macOS | Full support | Safari, Chrome, Firefox |
| Windows | Full support | Any browser |
| Linux | Full support | Any browser |
| iPhone / iPad | — | Safari |
| Android | — | Chrome |
- Python 3.11+
- FFmpeg
Install FFmpeg:
# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
# Windows (Chocolatey)
choco install ffmpeggit clone https://github.com/Aujasyarajput18/Omnistream.git
cd omnistream
./scripts/setup.shWindows:
cd server && pip install -e . && python -m omnistream
./scripts/run.shThe terminal prints the server URL. Open it on your laptop to access the host dashboard. Share the same URL (or the room code) with anyone else on your network.
Stream whatever is playing on your laptop — Spotify, a movie, YouTube — not just files.
macOS — requires BlackHole (free virtual audio driver):
brew install blackhole-2chOpen Audio MIDI Setup, create a Multi-Output Device with your speakers and BlackHole 2ch, and set it as your Sound Output. Then click "Capture System Audio" on the dashboard.
Linux — works automatically via PulseAudio or PipeWire. No setup needed.
Windows — works via DirectShow loopback. If it fails, enable Stereo Mix in Sound Settings → Recording.
| Type | Behavior |
|---|---|
| Public | Anyone with the room code joins immediately |
| Private | Host sees a request and approves or rejects each person |
| Invite-only | Only people with a specific invite link can join |
Single Python process. No Docker. No database. No microservices.
server/omnistream/
├── api/
│ ├── app.py # FastAPI routes and WebSocket handler
│ └── routes_rooms.py # Room management API
├── core/
│ ├── audio_capture.py # FFmpeg subprocess manager
│ ├── distributor.py # Broadcast to WebSocket clients
│ ├── sync_engine.py # NTP-like clock synchronization
│ ├── room_manager.py # In-memory room lifecycle
│ ├── packet.py # 16-byte binary wire protocol
│ └── discovery.py # UDP LAN beacon
└── utils/
└── platform.py # OS-specific audio capture
web-client/
├── index.html
└── js/
├── app.js # Room and connection logic
├── audio-player.js # Web Audio API playback engine
├── sync-client.js # Clock sync client
├── jitter-buffer.js # Packet reordering and buffering
└── audio-worklet-processor.js # Low-latency audio thread
Every audio packet has a fixed 16-byte header:
Bytes 0: Version (2 bits) + Padding (1 bit) + Type (5 bits)
Bytes 1: Channel ID
Bytes 2–3: Sequence number (uint16, big-endian)
Bytes 4–11: Server timestamp in microseconds (uint64, big-endian)
Bytes 12–13: Payload length (uint16)
Bytes 14–15: Flags and reserved
Bytes 16+: Raw PCM audio (s16le, 48kHz, stereo)
50 packets per second. 20ms of audio per packet.
| Layer | Technology |
|---|---|
| Server | Python 3.11+, FastAPI, Uvicorn |
| Audio processing | FFmpeg |
| Transport | WebSocket binary frames |
| Client playback | Web Audio API — AudioWorklet with ScriptProcessor fallback |
| Clock sync | Custom NTP-like protocol, EMA-smoothed |
| Jitter buffer | Adaptive, 60–300ms target |
cd server
pip install -e ".[dev]"
# Tests — 126 passing
pytest tests/ -v
# Lint
ruff check omnistream/
# Type check
mypy omnistream/| Variable | Default | Description |
|---|---|---|
OMNISTREAM_HOST |
0.0.0.0 |
Bind address |
OMNISTREAM_PORT |
8000 |
HTTP port |
OMNISTREAM_SAMPLE_RATE |
48000 |
Audio sample rate (Hz) |
OMNISTREAM_CHANNELS |
2 |
1 = mono, 2 = stereo |
OMNISTREAM_FRAME_DURATION_MS |
20 |
Packet size in milliseconds |
Listeners can't connect — host and listeners must be on the same WiFi. Use the IP address URL if .local hostname doesn't resolve.
No sound on phone — mobile browsers block audio until the user interacts. Tap the "Tap to Enable Audio" banner when it appears.
Choppy audio — caused by WiFi packet loss. Move closer to the router. The jitter buffer handles brief spikes but sustained loss above 5% will cause gaps.
System audio not working (macOS) — install BlackHole (brew install blackhole-2ch) and configure a Multi-Output Device in Audio MIDI Setup.
System audio not working (Windows) — enable Stereo Mix in Sound Settings → More sound settings → Recording tab.
"Cannot bind socket" on macOS — run the server from Terminal.app, not from an IDE terminal. Or run ./scripts/fix-macos-network.sh once.
- Opus encoding (6x compression vs current raw PCM)
- Internet relay via Cloudflare Tunnel
- Synchronized seek and pause controls
- Multi-channel (different groups hear different streams)
- PWA install support for phone home screen
MIT
Pull requests welcome. Run pytest tests/ and ruff check omnistream/ before submitting.