On this pageOPENCLOSE
Data channels
Chat, files, and game state over SCTP with the right reliability, ordering, and backpressure choices.
Goal
- 01Choose ordered or unordered delivery and reliable or partially reliable modes for each data channel.
- 02Send chat, game state, and files without filling the SCTP buffer or freezing the page.
- 03Know when a data channel is the wrong transport for the job.
The concept, plainly
An `RTCDataChannel` runs over , which is multiplexed over DTLS. It is not a WebSocket and it is not a replacement for HTTP. It is a peer-to-peer transport for application data already associated with a connected peer connection. Every channel has a label, an ordered flag, and a reliability configuration.
With `ordered: true`, messages are delivered in send order. With `ordered: false`, they can arrive independently even on a reliable channel; a later small message may overtake an earlier large one. `maxRetransmits: 0` or `maxPacketLifeTime` makes a channel partially reliable, useful for stale-sensitive state such as cursor positions or sensor updates. The browser's default channel is ordered and reliable.
SCTP provides message boundaries, so a chunk sent is not arbitrarily split and recombined at the API boundary. There is still a maximum message size negotiated per association. A 50 MB message is a bad idea even if the browser accepts it: it consumes memory, blocks the data path, and is not what these channels are designed for. Chunk files and pace them.
Backpressure is mandatory. `bufferedAmount` reports bytes queued but not yet handed to SCTP. If you call send in a tight loop, memory grows and latency spikes. Set `bufferedAmountLowThreshold`, wait for the low event, and pause your producer. Local file reading can also outpace the channel; use a chunk size such as 8 or 16 KB and await the low-water mark.
Head-of-line blocking is per association, not necessarily per channel. SCTP can have ordered and unordered streams, and channel options trade reliability and ordering. If a large file transfer must not delay live chat, use a separate channel with independent ordering semantics, or a separate peer connection if the implementation blocks at the association level. Measure before promising isolation.
Not everything belongs here. Use a data channel for ephemeral peer-to-peer messages and transfers while both peers are present. Use HTTP or object storage for durable files, resumable uploads, and anything that must survive a reload. The channel's security is tied to DTLS; it is encrypted in transit but it is not a durable store. At scale over an SFU, data channels work but are typically not the same as server-side reliable messaging.
See it move
Interactive diagram
One SCTP association, several guarantees
Create a channel with intent
The offerer creates labeled channels with explicit ordered and reliability options. The answerer receives them through ondatachannel.
Each channel declares its own reliability and ordering. All of them share one DTLS transport.
Behind the scenes
SCTP runs over DTLS over ICE
The data channel does not bypass WebRTC's transport stack. ICE finds a candidate pair, DTLS authenticates and encrypts, and SCTP multiplexes channels over that secure association. Data channels and media often share a bundle, but they use different protocols above DTLS: SCTP for data, SRTP for media.
Channel options are negotiated as streams
The label is application metadata. The reliability and ordering options describe SCTP stream behavior. If both sides create a channel with the same negotiated id, the browser reuses or coordinates it. Creating several channels gives you independent message semantics, but the underlying association and network path are still shared.
bufferedAmount is not the network speed
It is the amount waiting in the local SCTP send queue. A low value does not guarantee the peer received anything, and a high value tells you the producer is ahead of the path. Use getStats and delivery acknowledgements if you need end-to-end progress. For files, a receiver-side completion message is the only proof that assembly succeeded.
Code walkthrough
Chat is ordered and reliable. Cursor state tolerates loss and reordering. Give each channel a single purpose.
const chat = pc.createDataChannel("chat", {ordered: true,});const cursor = pc.createDataChannel("cursor", {ordered: false,maxRetransmits: 0,});chat.onopen = () => console.info("chat open");chat.onmessage = (event) => {if (typeof event.data !== "string") return;const message = JSON.parse(event.data) as { text?: unknown };if (typeof message.text === "string") renderChat(message.text.slice(0, 2000));};pc.ondatachannel = (event) => {const channel = event.channel;if (channel.label === "chat") attachChatReceiver(channel);if (channel.label === "cursor") attachCursorReceiver(channel);};
Do and don't
Do
Name channels by purpose
A reader should know the contract from the label. Do not multiplex unrelated app features into one opaque channel without a protocol.
Chunk and pace files
Use bounded chunks, backpressure, progress, cancellation, and a total-size cap on the receiver.
Choose reliability deliberately
Chat and commands need reliable ordered delivery. Live coordinates and previews often need freshness more than completeness.
Confirm delivery at the app layer
A send call only queues bytes. For files or critical commands, have the receiver acknowledge assembly or execution.
Don't
Don't send megabytes in one message
Large messages consume memory, block the queue, and can exceed implementation limits. Chunk them.
Don't poll bufferedAmount in a tight loop
Use bufferedamountlow plus an occasional fallback timer. Busy-waiting burns the main thread and fights the network.
Don't use a data channel as a database
The peer must be present and the channel must survive transport failure. Use object storage for durable files.
Don't assume unordered means lossy
Ordered and reliable are independent flags. An unordered reliable channel reorders but does not drop.
Practice
Hands-on lab
Send chat and a file over SCTP
The 1:1 lab mounts a real ordered data channel. Send messages, upload a file, and watch chunk progress. Kill signaling afterward and keep sending.
- 1Connect two tabs and send a chat message in each direction.
- 2Send a small file and watch the progress update as chunks arrive.
- 3Kill the signaling socket and send another message. It should still work.
- 4Clear the messages and inspect the peer-event log for data-channel open and file events.
Solo? Use the loopback mode. The sender and receiver both live in this tab, but messages still cross a real SCTP association and real DTLS.
Quick quiz
Knowledge check
0 of 4 answered
- 01
What does ordered: false change?
- 02
Why is bufferedAmount important?
- 03
Which workload is a data channel least suited for?
- 04
SCTP in WebRTC runs over which transport?
Key takeaways
- SCTP data channels offer per-channel ordering and reliability over a DTLS-secured peer connection.01
- Ordered and reliable are independent choices, and the defaults are both on.02
- Backpressure, chunking, validation, and size caps are required for file transfer.03
- Use data channels for live peer data and durable systems for anything that must survive.04
Common errors and fixes
Terms in this lesson
Done with this lesson?
Mark it complete, then keep the thread.