This demo shows a WebSocket Relay (on this Worker) and its use with the Cloudflare Realtime API
for WebRTC <-> WebSocket bridging. Replace {appId} below with your Cloudflare Realtime App ID.
This Worker relays WebSocket messages: receives on /publish for a channel,
and broadcasts to all /subscribe clients on the same channel.
Use this Relay:
your-random-channel-id.wss://bridge.orange.cloudflare.dev/ws/<your-random-channel-id>/publishwss://bridge.orange.cloudflare.dev/ws/<your-random-channel-id>/subscribeThis relay is unaware of WebRTC or the Cloudflare Realtime API.
The Cloudflare Realtime API (a separate production service) can bridge WebRTC and WebSockets
via its endpoint: https://rtc.live.cloudflare.com/v1/apps/{appId}/adapters/websocket/new
General API Request Body structure to /adapters/websocket/new:
{
"tracks": [{
"location": "'local' or 'remote'",
"sessionId": "your_session_id",
"trackName": "your_track_name",
"endpoint": "target_websocket_url",
// "outputCodec" or "inputCodec": "pcm" (see note below)
}]
}
Note: The codec field is 'outputCodec' for 'location: "remote"' and 'inputCodec' for 'location: "local"'. See scenarios.
location parameter & sessionId behavior (user's perspective):
remote: (Send Realtime track audio TO a "remote" WS)
You are sending audio FROM an existing WebRTC track TO a "remote" WebSocket endpoint.
Uses outputCodec to specify the codec for audio sent TO the WebSocket.
sessionId: REQUIRED. Must be the ID of the Realtime session containing trackName.
local: (Create Realtime track FROM a "local" WS)
You are ingesting audio FROM a "local" WebSocket endpoint TO CREATE a new WebRTC track.
Uses inputCodec to specify the codec of audio received FROM the WebSocket.
sessionId: IGNORED. The API will ALWAYS generate a new, unique sessionId for the
session associated with the newly created track for each API request.
The generated sessionId will be returned in the API response.
(Realtime API: WebRTC Track → "Remote" WebSocket)
/publisher app) publishes mic-track to your Realtime session (e.g., session-abc).mic-track's audio out, POST to https://rtc.live.cloudflare.com/v1/apps/{appId}/adapters/websocket/new with:
{ "tracks": [{ "location": "remote",
"sessionId": "session-abc",
"trackName": "mic-track",
"endpoint": "wss://bridge.orange.cloudflare.dev/ws/<channel>/publish",
"outputCodec": "pcm" }] }
(Note the use of outputCodec for location: "remote".)
/publish URL
and broadcasts it to its /subscribe URL./player app) connect to the relay's /subscribe URL to get the audio.ASCII Diagram for SCENARIO A:
[WebRTC Source] ---WebRTC---> [Cloudflare Realtime API Session (ID: session-abc)]
(e.g., Mic, (Has existing 'mic-track')
/publisher app) |
| API Call: POST to `https://rtc.live.cloudflare.com/v1/apps/{appId}/adapters/websocket/new`
| - location: 'remote'
| - sessionId: 'session-abc' (REQUIRED)
| - trackName: 'mic-track'
| - endpoint: Demo Relay's /publish URL
| - outputCodec: 'pcm'
V
[Demo Relay (This Worker): wss://bridge.orange.cloudflare.dev/ws/<channel>/publish] <---(WebSocket: audio data from Realtime)---
(Acts as "Remote" WebSocket Endpoint for Realtime) |
| (WebSocket: audio data broadcast by Relay)
V
[Demo Relay (This Worker): wss://bridge.orange.cloudflare.dev/ws/<channel>/subscribe]
|
| (WebSocket: audio data)
V
[WebSocket Listeners]
(e.g., /player app)
(Realtime API: "Local" WebSocket → New WebRTC Track with New Session)
/publish URL./subscribe URL. This /subscribe URL will
act as the "local" WebSocket endpoint for Realtime.https://rtc.live.cloudflare.com/v1/apps/{appId}/adapters/websocket/new with:
{ "tracks": [{ "location": "local",
// "sessionId": "any_value_is_ignored",
"trackName": "new-track-from-ws",
"endpoint": "wss://bridge.orange.cloudflare.dev/ws/<channel>/subscribe",
"inputCodec": "pcm" }] }
(Note the use of inputCodec for location: "local". The API will generate a NEW sessionId for this track, returned in the response.)
endpoint, ingests audio, creates new-track-from-ws,
and associates it with a NEWLY GENERATED sessionId./pull app) use the sessionId returned in the API response from step 3
to subscribe to new-track-from-ws in Realtime.ASCII Diagram for SCENARIO B:
[WebSocket Audio Source] --WebSocket--> [Demo Relay (This Worker): wss://bridge.orange.cloudflare.dev/ws/<channel>/publish]
(e.g., backend script) (Receives audio)
|
| (WebSocket: audio data broadcast by Relay)
V
[Demo Relay (This Worker): wss://bridge.orange.cloudflare.dev/ws/<channel>/subscribe]
(Acts as "Local" WebSocket Endpoint for Realtime) |
| (WebSocket: audio data for Realtime to ingest)
|
| API Call: POST to `https://rtc.live.cloudflare.com/v1/apps/{appId}/adapters/websocket/new`
| - location: 'local'
| - sessionId: (IGNORED - API generates NEW one)
| - trackName: 'new-track-from-ws'
| - endpoint: This Relay's /subscribe URL
| - inputCodec: 'pcm'
V
[Cloudflare Realtime API (Generates NEW Session)]
| (API Response includes the new sessionId)
|
| (Creates 'new-track-from-ws'; associates with NEW sessionId)
|
| (WebRTC: audio from 'new-track-from-ws')
V
[WebRTC Listeners (use new sessionId from API response)]
(e.g., /pull app)
/publisher: Publishes mic to Realtime (for Scenario A)./player: Plays audio from a WebSocket URL (e.g., demo relay's /subscribe in Scenario A)./pull: Pulls & plays a WebRTC track from Realtime (e.g., new-track-from-ws in Scenario B,
using the sessionId returned by the API when the track was created).This page is provided strictly for demo purposes with no support nor warranty. If you are a Cloudflare Realtime customer, please talk to your Cloudflare account team or point of contact about supporting your use case with the production Cloudflare Realtime service.