Browser support
Which browsers can run a Synento session, how the SDK reports it, and what has actually been verified.
Browser support
Synento encodes in the browser with WebCodecs and ships frames over a WebSocket. There is no WebRTC fallback — that is a deliberate architectural choice, not a gap: every frame is written to a durable stream, which is what makes each session its own recording — so a browser either has the APIs or it cannot publish. The SDK checks before connecting and tells you which piece is missing.
Requirements
A browser needs all four:
| Capability | Used for |
|---|---|
WebSocket | Transport for media, chat and control |
VideoEncoder / VideoDecoder / VideoFrame (WebCodecs) | Encoding your camera, decoding everyone else's |
navigator.mediaDevices | Camera and microphone capture (requires a secure context) |
AudioWorkletNode | Microphone capture at 16 kHz mono |
AudioEncoder / AudioDecoder (Opus) | Optional. Compresses audio ~8x. Without it the call falls back to uncompressed PCM and works exactly the same, so this does not affect whether a browser is supported — checkSupport() reports it as webCodecsAudio for visibility only. |
navigator.mediaDevices only exists on HTTPS or localhost. On a plain
HTTP origin — including a LAN IP such as http://192.168.1.10:3000 — capture is
unavailable no matter which browser you use.
Matrix
| Browser | Publish + subscribe | Notes |
|---|---|---|
| Chrome / Edge 94+ | Yes | The reference target. H.264, VP9 and VP8 all negotiate. |
| Safari 16.4+ (macOS) | Yes | WebCodecs shipped in 16.4. Verified manually, not in CI. |
| Safari 16.4+ (iOS / iPadOS) | Publish + subscribe, with caveats | See iOS below. |
| Firefox 133+ | Yes | WebCodecs enabled by default from 133. |
| Firefox < 133 | No | VideoEncoder is absent; the SDK reports it and refuses to connect. |
| Any browser over plain HTTP | No | No mediaDevices outside a secure context. |
What has actually been verified
Being precise about this matters more than the table looking green.
- Chromium — automated, every CI run. A two-party call with synthetic camera
and microphone hardware: both sides decode the other's video, chat is
delivered, and both participants' camera and microphone tracks are checked for
recorded data in S2. See
e2e/tests/live-call.spec.ts. - Playwright WebKit — automated, non-media flows only. It runs sign-in, the dashboard, recording and export. A WebKit pass is not a Safari pass. Playwright's WebKit is a build of the engine, not the shipping browser: its WebCodecs support and media handling differ from Safari's, and it has no equivalent of Chromium's fake-device switch. Treating it as Safari coverage would be exactly the false assurance the suite exists to prevent.
- Safari on macOS and iOS — manual. Run the checklist below before a release. The result belongs in this table, not in a test report.
Manual Safari pass
Against a deployed environment over HTTPS (not localhost, so the iOS device can
reach it):
- Open the demo in Safari, join a session, and confirm the local preview shows.
- Join the same session from a second device or browser. Confirm video appears both ways and that you can hear audio in both directions.
- Send a chat message each way.
- Lock the phone / switch apps for ~15 seconds, then return: the SDK should reconnect and resume publishing without a reload.
- Leave, then open the recording in the dashboard: replay should show both participants with audio.
- Export to MP4 and play the file.
iOS
Two behaviours are worth knowing about, and both are platform behaviour rather than Synento bugs:
- Backgrounding stops capture. iOS suspends media capture when the tab is not frontmost. The connection resumes on return, but the recording has a gap.
- Autoplay of remote audio needs a gesture. Audio playback starts from a user interaction (the join tap counts). A page that connects on load without any tap may render video silently until the user touches something.
What your code should do
checkSupport() returns a structured report, and SynentoRoom.connect() refuses
to open a socket when it fails, rather than half-joining a call:
import { SynentoRoom } from '@synento/client'
const support = SynentoRoom.checkSupport()
if (!support.supported) {
// support.reason is written for humans and names the missing capability.
showUnsupportedMessage(support.reason)
return
}The individual booleans (webSocket, webCodecsVideo, mediaDevices,
audioWorklet) are all on the report, so you can tell "update your browser"
apart from "this page needs HTTPS" — a distinction users can act on.