WebRTC Signaling
DocsWebRTC carries audio, video, or data directly between browsers, but peers still need a signaling route to exchange connection details. This demo uses Agentuity-hosted WebSocket signaling to join a room. The browser RTCPeerConnection carries media and data peer-to-peer. Open the same room in another tab to connect. For server-to-client events, see SSE Stream.
Idle/Open the same room in another tab to connect.
You
Local participant
Voice level
Y
Local preview
Connect to start your camera and microphone preview.
Local preview
Peer
Waiting
Voice level
P
Waiting for peer
Open this room in another tab, then connect from both tabs.
Waiting for peer
Connection events and data-channel messages appear here.
Reference Code
import { Hono } from "hono";
import { createBunWebSocket } from "hono/bun";
import type { ServerWebSocket } from "bun";
const { upgradeWebSocket, websocket } =
createBunWebSocket<ServerWebSocket>();
const rooms = new Map<string, Set<{ send(data: string): void }>>();
const app = new Hono();
app.get("/api/webrtc/signal", upgradeWebSocket((c) => {
const roomId = c.req.query("room") ?? "default";
const peers = rooms.get(roomId) ?? new Set();
rooms.set(roomId, peers);
return {
onOpen(_event, ws) {
if (peers.size >= 2) {
ws.send(JSON.stringify({ type: "room-full" }));
return;
}
peers.add(ws);
ws.send(JSON.stringify({
type: "joined",
initiator: peers.size === 2,
}));
},
onMessage(event, ws) {
// Relay opaque SDP and ICE payloads. The server does not inspect them.
for (const peer of peers) {
if (peer !== ws) {
peer.send(String(event.data));
}
}
},
onClose(_event, ws) {
peers.delete(ws);
if (peers.size === 0) {
rooms.delete(roomId);
}
},
};
}));
// Agentuity hosts this route. Browser WebRTC handles media and data channels.
// Create RTCPeerConnection in the client, send each offer/answer/candidate over
// /api/webrtc/signal, then pass received payloads into setRemoteDescription()
// or addIceCandidate().
export default {
fetch: app.fetch,
websocket,
};Ready
Output will appear here...