Peer-to-peer data without the middleman

Moving data between two browsers usually means routing it through a server you have to set up, pay for, and possibly scale across regions. That adds latency and puts the data in someone else's hands. WebRTC's RTCDataChannel API offers a different path: direct, peer-to-peer transfer.

WebSocket, AJAX, and Server-Sent Events are all built around client-server communication. RTCDataChannel instead works with RTCPeerConnection to establish connectivity straight between peers, cutting out the intermediary and reducing hops. It runs over the Stream Control Transmission Protocol (SCTP), which gives you configurable delivery semantics: you can choose whether messages arrive in order and whether they are retransmitted.

SCTP-based RTCDataChannel is supported on desktop and Android in Chrome, Opera, and Firefox.

What you still need servers for

Peer-to-peer doesn't mean server-free. To bootstrap a connection, peers must exchange metadata through a signaling process. WebRTC then uses the ICE framework to find the best network path, STUN servers to discover each peer's public IP and port, and TURN servers to relay data if a direct connection can't be made through NATs and firewalls.

Delivery modes and data types

The API mirrors WebSocket closely and supports DOMString, Blob, ArrayBuffer, and ArrayBufferView. That flexibility matters for use cases like file transfer and gaming.

You can configure the channel for different reliability profiles:

  • Reliable and ordered: Guarantees delivery and ordering, but adds overhead that can slow things down.
  • Unreliable and unordered: No delivery or ordering guarantees—effectively UDP semantics, but much faster because there's no overhead.
  • Partial reliable: Guarantees delivery under a condition such as a retransmit timeout or a maximum retransmission count. Ordering is also configurable.

When there's no packet loss, the first two modes perform about the same. In reliable and ordered mode, however, a single lost packet can block subsequent ones until it's retransmitted, at which point it may already be stale. You can also open multiple channels in one app, each with its own reliability settings.

TCPUDPSCTP
ReliabilityReliableUnreliableConfigurable
DeliveryOrderedUnorderedConfigurable
TransmissionByte-orientedMessage-orientedMessage-oriented
Flow controlYesNoYes
Congestion controlYesNoYes

Setting up a channel

Working demos are available at simpl.info, and the WebRTC samples project includes text transmission and file transfer examples. These demos typically create a peer connection to the same page, open a data channel, and send a message through it.

The core code is compact:

const peerConnection = new RTCPeerConnection();

// Establish your peer connection using your signaling channel here
const dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = (error) => {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = (event) => {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = () => {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = () => {
  console.log("The Data Channel is Closed");
};

The dataChannel object is created from an already-established peer connection, either before or after signaling. It takes a label to distinguish the channel and an optional settings object:

const dataChannelOptions = {
  ordered: false, // do not guarantee order
  maxPacketLifeTime: 3000, // in milliseconds
};

Useful options include:

  • ordered: whether the channel guarantees message order
  • maxPacketLifeTime: maximum time to retransmit a failed message
  • maxRetransmits: maximum retransmission attempts for a failed message. You can specify only one of maxPacketLifeTime or maxRetransmits, not both
  • protocol: specifies a subprotocol for app-level metadata
  • negotiated: if true, disables automatic setup on the remote peer, letting you create the channel manually with an ID
  • id: your own channel ID, usable only with negotiated set to true

For UDP-like behavior, set maxRetransmits to 0 and ordered to false. Reliable and ordered is the default with SCTP. Most applications only need the first three options; full unreliable and unordered mode is only useful when you want the app layer to own everything, which in practice is rare—partial reliability is usually the right call.

As with WebSocket, the channel fires events for connection open, close, errors, and incoming messages.

Security and encryption

All WebRTC traffic is encrypted. RTCDataChannel uses Datagram Transport Layer Security (DTLS), a derivative of SSL, so data gets the same protection as any standard secure connection. DTLS is standardized and built into every browser that supports WebRTC.

Building a file-sharing app

With RTCDataChannel, browser-based file sharing is realistic. Data stays encrypted and never touches the app provider's servers, and because peers can connect directly, multiple clients can collaborate for faster distribution.

Successful transfer requires three steps:

  1. Read the file using the File API.
  2. Establish a peer connection with RTCPeerConnection.
  3. Open a data channel with RTCDataChannel.

Two considerations are critical.

File size: Small files can be loaded as a single Blob and sent whole over a reliable channel—keeping in mind that browsers cap transfer size. Larger files require chunking. Load chunks, send them with chunkID metadata so the remote peer can reassemble them, and store chunks to offline storage (like the FileSystem API) until the complete file is received.

Chunk size: There's currently a send-size limit, so chunking is mandatory for large files. The recommended maximum chunk size is 64KiB.

Once the full file arrives, trigger a download with an anchor tag:

function saveFile(blob) {
  const link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

Open-source projects like PubShare and ShareFest demonstrate the pattern in production and are good starting points.

Wider applications

Beyond file sharing, RTCDataChannel opens up:

  • Multiplayer gaming, paired with WebGL as in Mozilla's BananaBread
  • Peer-based content delivery, an approach pioneered by PeerCDN which serves web assets via peer-to-peer communication

Frameworks like PeerJS and the PubNub WebRTC SDK simplify implementation, and the API itself is widely supported. The shift in thinking is real: you can now build high-performance, low-latency apps where data moves directly between users.

Limitations and considerations

Before you build on data channels, keep a few practical constraints in mind. First, data channels are not a guaranteed delivery service by default. You control reliability through the channel configuration at creation time, choosing between ordered or unordered delivery and reliable or unreliable semantics. For critical application state, you'll want a reliable, ordered channel; for streaming data like game position updates or live media metadata, an unordered, partially reliable channel can be a better fit to avoid head-of-line blocking.

Second, data channels ride on the same connection as the audio and video streams. Bandwidth is shared, so a heavy data channel can starve your media quality and vice versa. You do not get a dedicated or prioritised pipe unless you build that logic yourself into your protocol.

Third, there is no standard signaling mechanism. The channel itself is peer-to-peer once established, but the initial handshake — exchanging SDP offers and ICE candidates — still requires a server you control. That server can be as simple as a WebSocket relay or as robust as a full WebRTC signaling service; the point is that you cannot avoid it.

Finally, check the browser landscape. Data channel support and the specific API details vary across implementations and have changed over time. Always test against the targeted browsers and keep your feature detection current.

Where to go next

For a deeper dive, the following references are useful: