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.