Build a watch-together experience
Watch parties have two different media problems: delivering one program to many viewers and giving a smaller group a realtime conversation floor. Model them separately so choosing HLS does not accidentally remove the conversation, and opening a conversation does not force every viewer onto a transport.
Build this: compare the runnable starter and its platform evidence. View source: MediaSFU Watch Together.
The journey
- The host supplies a direct HTTPS video and chooses WebRTC or HLS output.
- The backend creates a temporary room and the browser publishes the selected video into MediaSFU over WHIP.
- The host goes live and shares a single-use audience invitation.
- Audience members receive the program; invited floor participants join the realtime room with microphone and camera controls.
- The host ends the party. The backend stops egress and ingest, ends the room, and confirms cleanup.
Invitation and authority
The application owns source validation, output choice, floor presentation, and audience naming. An authenticated backend owns room creation, WHIP/HLS resource authority, and opaque, expiring audience grants. A viewer invitation contains only the selected capability: realtime WebRTC or HLS playback, with an optional floor role. It never contains a reusable room credential.
Room lifecycle
Validate the direct video before creating a room. Connect the host, reserve the program identity, publish over WHIP, and start the selected output only after the room reports ready. End in the reverse order: stop HLS, stop the WHIP publication and session, leave/end the room, then clear the application state.
Primary and mini media
The program player is always the main stage and can enter fullscreen. Realtime floor participants belong in a separate rail or mini-grid. Keep program audio and room audio observable as separate controls. Mount every prepared remote audio renderer for floor members even when a participant's video is absent.
When a screen is shared on the floor, show it with contain and do not mirror
it. Mirror only a local camera self-view.
Media identity
The external program, floor participants, and audience playback are distinct media identities. Reserve the program before publishing so it cannot appear as an ordinary participant. The program is primary; floor cameras remain a secondary rail. Mount every prepared floor-audio renderer independently from the visible cards.
Responsive layout
Let the program fill the measured stage and move the realtime floor into a compact rail below it on narrow screens. Keep output status, playback controls, and the audience action reachable without shrinking the program into a card. The HLS audience surface has its own bounded controls and never inherits room floor audio by accident.
Remote audio
Keep program audio and realtime floor audio as separate controls, and mount every prepared floor-audio renderer even when a participant has no video.
Cloud and self-hosted authority
The browser calls your backend, not MediaSFU account endpoints. Store reusable credentials in the backend environment and return only room-scoped responses, WHIP leases, HLS playback URLs, or opaque audience grants. MediaSFU Cloud is a managed service; MediaSFU Open is an already-running server that your team secures and operates. A server URL does not install MediaSFU Open.
Use an Idempotency-Key for party creation and audience redemption. Keep the
same key for an exact network retry and generate a new key for the next party
or join action.
Recovery and cleanup
Separate “source connected”, “program live”, “audience admitted”, and “party ended” in the UI. If WHIP or HLS setup fails, stop any partial publication and show a recoverable error. On explicit end and page exit, stop the broadcast, stop the WHIP session, leave/end the room, and report success only after the backend confirms that no active resources remain.
Failure states
Show separate recovery messages for an unsupported source, failed room join, WHIP publication error, delayed HLS manifest, autoplay refusal, invite failure, and ended playback. A viewer can retry playback without recreating a healthy host room; a closed room requires a new host session.
Cleanup
Stop the HLS broadcast, stop the WHIP publication and session, leave/end the room, remove timers and listeners, and clear the invite and program state. Make cleanup idempotent on explicit end, page exit, and unmount. Show the ended state only after the backend has confirmed that room and output resources are clear.
Product evolution
The early composition could treat the program as a regular room member and could ask a viewer to play a manifest before it existed. The stable lesson is to reserve external-program identity before publish, separate WHIP, WebRTC, and HLS lifecycles, and wait for a playable manifest before declaring the audience live.
Choose and test your platform
The React implementation is the most complete reference for WHIP input, a separate realtime floor, one-time audience invitations, HLS playback, and cleanup. Vue, Angular, Expo Web, React Native, Flutter, Kotlin Android, and Unity ports are also available. Test file-to-WebRTC conversion, HLS readiness, autoplay recovery, audio, reconnect, and cleanup on every platform you ship.
Related implementation
Open the working example at /showcases/watch-party. The standalone Watch
Together starter is available in the MediaSFU Watch Together repository.
It includes React, Vue, Angular, Expo Web, React Native, Flutter, Kotlin
Android, and Unity implementations. The React path shows a
WHIP program, a distinct realtime host-floor track, invitation redemption, HLS,
and cleanup. Validate native file-to-WebRTC track adapters on every device
family your application supports.