Peer-to-peer synchronized playback for local files and direct online media URLs.
SyncPlayer is a browser-based media synchronization cockpit built for watching or listening together across devices. It combines a React web interface, WebRTC peer connections, shared playback-state messages, and a Rust/WASM sync helper to keep media sessions aligned with low overhead.
SyncPlayer lets two peers connect directly through WebRTC and synchronize playback state without requiring a traditional media server.
It supports:
- Local video and audio files
- Direct online media URLs
- Progressive media files such as MP4, WebM, OGG, MP3, WAV, M4A, AAC, FLAC, and Opus
- HLS
.m3u8streams through native browser support orhls.js - MPEG-DASH
.mpdstreams throughdashjs - Manual peer connection through invite and response links
- Playback-state synchronization over WebRTC DataChannels
- Peer latency checks, transport counters, logs, and room telemetry
The current experience is designed around a dark, eDEX-inspired command-deck UI with a clear main playback surface and dense side telemetry for debugging and coordination.
Open the deployed app here:
https://sync-player-web.vercel.app
You can use the hosted version to test online media URLs or connect two browser sessions using the manual WebRTC invite flow.
The app is organized around four main responsibilities:
The browser UI provides:
- Immersive playback controls
- Room and peer connection controls
- Local file mounting
- Online media URL mounting
- Invite and response link handling
- Peer status indicators
- Logs, settings, and debugging telemetry
The peer layer handles browser-to-browser coordination, including:
- Manual offer and answer pairing
- Shareable invite links
- WebRTC DataChannel setup
- Peer latency checks
- Transport counters
- Playback-state message delivery
The Rust/WASM sync helper provides performance-sensitive logic for:
- File fingerprinting
- Media identity hints
- Time math
- Drift detection
- Drift mode decisions
- Suggested correction rates
- Playback sync calculations
Shared packages define reusable types and utilities, including:
- Message schemas
- Media identity types
- Cross-package helpers
- Sync-related contracts used by both the app and lower-level modules
See docs/architecture/directory-structure.md for the functional map.
The current implementation includes:
- Vite + React command-deck interface
- Local video and audio file mounting
- Direct media URL mounting
- Progressive file playback for common audio and video formats
- HLS
.m3u8playback through native browser support orhls.js - MPEG-DASH
.mpdplayback throughdashjs - Manual WebRTC offer/answer pairing
- Shareable invite links that hide raw WebRTC signaling text
- Online media URLs embedded into invite links
- Automatic online URL loading for viewers when included in the invite
- WebRTC DataChannel playback-state messages
- Peer latency pings
- Transport counters
- Rust/WASM sync helper for drift handling
- Media identity hints
- eDEX-inspired dark cockpit UI
- Dense side telemetry with a clear main media surface
SyncPlayer currently uses a serverless WebRTC signaling flow.
That means there is no dedicated signaling relay yet. Instead, the host and viewer exchange invite and response links manually.
- Open SyncPlayer.
- Select a local media file or paste an online media URL.
- Click
Copy Invite Link. - Send the invite link to the viewer.
- Wait for the viewer to send back a response link.
- Paste the response link into
Response link from viewer. - Click
Finish Connection.
- Open the invite link from the host.
- If the invite contains an online media URL, the player loads it automatically.
- Copy the generated response link.
- Send the response link back to the host.
- Once the host finishes the connection, playback sync can begin.
Because SyncPlayer currently uses serverless manual signaling, a single one-way invite link cannot complete the browser-to-browser connection by itself.
The viewer still needs to return a response link to the host.
Also, local files remain private to each device for now. The invite synchronizes playback state, but it does not transfer the local file. Viewers need access to the same local file until peer-to-peer file transfer is added.
Online media URLs are different: when an invite contains an online URL, the viewer can load that media automatically from the link.
SyncPlayer can mount local audio and video files selected from the browser.
Local files stay on the user’s device. They are not uploaded to a server.
SyncPlayer supports direct links to common progressive media files, including:
.mp4.webm.ogg.mp3.wav.m4a.aac.flac.opus
HLS .m3u8 streams are supported through:
- Native browser playback when available
hls.jsfallback when needed
MPEG-DASH .mpd streams are supported through dashjs.
SyncPlayer is built with:
- Vite for fast web development
- React for the cockpit interface
- TypeScript for safer application code
- WebRTC for peer-to-peer browser connections
- WebRTC DataChannels for sync messages
- Rust for sync-core logic
- WebAssembly for high-performance browser execution
- hls.js for HLS playback fallback
- dashjs for MPEG-DASH playback
Install dependencies:
pnpm installStart the development server:
pnpm devThe root dev command builds the WASM package first, then starts the web app.
Open:
http://localhost:5173/
For LAN or mobile WebRTC testing, use HTTPS:
pnpm dev:httpsThen open the local network URL shown by the dev server, for example:
https://10.0.0.3:5173/
The local certificate is self-signed, so mobile browsers may show a browser warning before loading the app.
Firefox mobile may fail WebRTC on plain HTTP LAN URLs such as:
http://10.0.0.3:5173/
Use HTTPS for more reliable mobile testing.
Create a production build:
pnpm buildDepending on the project setup, you can preview the built app with:
pnpm previewA typical local development loop is:
pnpm install
pnpm devThen open two browser windows or two devices:
http://localhost:5173/
For same-machine testing:
- Open the app in two browser tabs or windows.
- Use one as the host.
- Use the other as the viewer.
- Exchange invite and response links manually.
- Test playback-state sync.
For real device testing:
- Start the HTTPS dev server.
- Open the app from the LAN URL on both devices.
- Accept the self-signed certificate warning.
- Use the invite and response flow.
The repository is organized around app, peer, WASM, and shared contract layers.
For a detailed map, see:
docs/architecture/directory-structure.md
Suggested high-level structure:
.
├── apps/
│ └── web/
├── crates/
│ └── sync-core/
├── packages/
│ └── shared/
├── docs/
│ └── architecture/
└── README.md
Actual structure may evolve as the project grows.
Potential next improvements include:
- Signaling relay for one-click room joins
- Optional room codes
- Peer-to-peer local file transfer
- Multi-peer room support
- Better host/viewer role management
- Persistent room state
- More advanced drift correction
- Subtitle synchronization
- Playlist support
- Media metadata extraction
- Stream capability detection
- Better mobile-first controls
- Connection recovery after tab sleep or network changes
- TURN server configuration for difficult NAT environments
- End-to-end encrypted room metadata
- Import/export session logs for debugging
Check that:
- The host copied the invite link correctly
- The viewer opened the invite link
- The viewer copied the response link
- The host pasted the response link into the correct field
- Both browsers allow WebRTC
- Both devices have network connectivity
Some networks may block direct peer-to-peer WebRTC connections. A TURN server may be needed in the future for maximum reliability.
Local files are not transferred between peers yet.
Both users need to select the same local file manually. SyncPlayer can coordinate playback state, but it does not currently send the media file itself.
Check that:
- The URL points directly to a playable media resource
- The server allows browser playback
- The server provides compatible CORS headers when required
- The media format is supported by the browser or by the HLS/DASH playback layer
Use HTTPS for LAN testing:
pnpm dev:httpsPlain HTTP LAN URLs may cause browser restrictions, especially for WebRTC features.
SyncPlayer is designed around local-first and peer-to-peer behavior.
- Local files stay on the user’s device.
- Local files are not uploaded by the app.
- WebRTC connects browsers directly when possible.
- Online media URLs included in invite links are visible to anyone who receives the invite.
- Manual invite and response links may contain connection metadata needed for WebRTC setup.
Do not share invite or response links with people who should not join the session.
Contributions are welcome.
Good areas to improve include:
- WebRTC connection reliability
- Sync accuracy
- Media compatibility
- UI polish
- Accessibility
- Mobile usability
- Documentation
- Test coverage
- WASM sync-core improvements
Before contributing, run the local development setup and test both the host and viewer flows.
When working on SyncPlayer, keep these principles in mind:
- Keep the media experience responsive.
- Keep local files private unless the user explicitly chooses to share them.
- Prefer clear connection states over hidden magic.
- Make invite and response flows understandable.
- Keep sync messages small and explicit.
- Keep browser compatibility visible in the UI when possible.
- Treat WebRTC failures as expected network conditions, not exceptional edge cases.
Add the project license here.
For example:
MIT
SyncPlayer is in an early first-slice stage.
The foundation is in place for synchronized media playback, manual peer connection, online media URL sharing, and WASM-assisted sync decisions. Future versions can build on this foundation with easier signaling, stronger recovery, file transfer, and richer room features.