Use the WebRTC data channel label bb-chat-v1 to connect to per-stream chat.
- Data channel label:
bb-chat-v1 - Outbound client message type:
chat.send textlength: 1-2000 charsdisplayNamelength: 1-80 chars
Client -> server payload:
{
"type": "chat.send",
"clientMsgId": "uuid-or-any-unique-id",
"text": "hello",
"displayName": "alice"
}Server -> client message types:
chat.connectedchat.history(contains{ type: "message", message: ... }[])chat.message(single live message)chat.ack(echoesclientMsgId)chat.error(may includeclientMsgId)
Message shape:
{
"id": "message-id",
"ts": 1730000000,
"text": "hello",
"displayName": "alice"
}const CHAT_LABEL = "bb-chat-v1";
type Outbound =
| { type: "chat.connected" }
| { type: "chat.history"; events: Array<{ type: "message"; message: ChatMessage }> }
| { type: "chat.message"; eventId: number; message: ChatMessage }
| { type: "chat.ack"; clientMsgId: string }
| { type: "chat.error"; error: string; clientMsgId?: string };
type ChatMessage = {
id: string;
ts: number;
text: string;
displayName: string;
};
const chatChannel = peerConnection.createDataChannel(CHAT_LABEL);
const pending = new Map<string, (error?: Error) => void>();
chatChannel.addEventListener("message", (event) => {
const payload = JSON.parse(event.data) as Outbound;
if (payload.type === "chat.history") {
payload.events.forEach((e) => console.log("history", e.message));
}
if (payload.type === "chat.message") {
console.log("live", payload.message);
}
if (payload.type === "chat.ack") {
pending.get(payload.clientMsgId)?.();
pending.delete(payload.clientMsgId);
}
if (payload.type === "chat.error" && payload.clientMsgId) {
pending.get(payload.clientMsgId)?.(new Error(payload.error));
pending.delete(payload.clientMsgId);
}
});
function sendChat(text: string, displayName: string) {
if (chatChannel.readyState !== "open") {
throw new Error("chat channel is not open");
}
const clientMsgId = crypto.randomUUID();
const payload = {
type: "chat.send",
clientMsgId,
text,
displayName,
};
return new Promise<void>((resolve, reject) => {
pending.set(clientMsgId, (error?: Error) => {
if (error) reject(error);
else resolve();
});
chatChannel.send(JSON.stringify(payload));
});
}- Create
RTCPeerConnection. - Create chat data channel with label
bb-chat-v1before SDP offer. - Handle inbound
chat.connected,chat.history,chat.message,chat.ack, andchat.errorpayloads. - Send messages as
{ "type": "chat.send", "clientMsgId", "text", "displayName" }. - Treat
chat.ackas send success andchat.erroras send failure.