Mediasoup DataChannels vs WebSockets: an architectural experiment

Hi everyone!

I recently published a small experiment exploring an idea that had been in the back of my mind for quite some time: using Mediasoup DataChannels as a replacement for WebSockets.

It’s not a new library or framework, but rather a proof of concept and an architectural exploration, so I wasn’t completely sure whether Integrations was the right category. If there’s a better one, feel free to move the thread.

The repository implements a minimal chat application entirely on top of Mediasoup DataProducers/DataConsumers and DirectTransports, with the goal of understanding:

  • how practical DataChannels are outside their usual media-related use cases,

  • what architectural trade-offs appear compared to WebSockets,

  • where the complexity really comes from,

  • and, perhaps more interestingly, in which scenarios this architecture actually starts making sense.

The conclusion is a bit nuanced: I don’t think DataChannels should replace WebSockets for generic messaging, but I also found some media-centric scenarios where using the same Mediasoup infrastructure for both media and data becomes surprisingly compelling.

I wrote a detailed article explaining both the implementation and the reasoning behind the experiment:

The complete PoC is also available on GitHub:

I’d be very interested to hear whether anyone else has explored similar ideas, or if there are use cases where you’ve found Mediasoup DataChannels particularly effective beyond their typical role.

In my last 2 companies we have migrated application signaling from WebSocket to DataChannels entirely. DataChannels survive to network changes (WebSocket doesn’t unless you build a complete TCP/SCTP-like messaging protocol on top of it, which absolutely nobody does or nobody does it correctly). With reliable DataChannels the client application can survive to temporal network disruptions without having to refresh/reset the full application state from scratch. This is because reliable DataChannels buffer sending messages so those buffered messages finally reach the endpoint (in order) once ICE reconnects/restarts.

So, being 100% sure and based on real experience, I would never say that using DataChannels instead of WebSocket is experimental or risky or immature. The only extra complexity in DataChannels is that in order to send big messages from browser to server (bigger than 262000 KB) the application needs to implement chunking of messages and reassembly in receiver side. And also, the application needs to manage buffered amount, since the native sending buffer in browsers is smaller than when using WebSocket.

Thanks a lot for sharing your experience, this is exactly the kind of feedback I was hoping for.

My initial impression was actually the same. I don’t think DataChannels are immature either, just underused. I’ve already used them in production for two-stage signaling (and, of course, for file sharing with ShareIt!), but they always seem to play a secondary role compared to media streams. It’s great to hear you’ve been using them successfully in production for application signaling.

I wasn’t aware of the message chunking limitation (good to know!), and I definitely hadn’t considered the resilience across network changes. That’s a really compelling advantage over WebSockets that I overlooked.

My article was mostly focused on the additional architectural complexity (and potentially the extra server-side memory usage) when using Mediasoup DataProducers/DataConsumers for generic message routing, as well as the longer connection setup time. But your point about transport resilience is a very strong argument for considering DataChannels again, even outside the Mediasoup-centric architectures I initially had in mind.

It’s interesting that both perspectives can coexist: for some architectures the additional complexity isn’t justified, while for others the transport properties alone can more than compensate for it.

Out of curiosity, were those production systems also using Mediasoup DataProducers/DataConsumers for signaling, or WebRTC DataChannels more generally?

mediasoup. And with the new built-in SCTP stack of mediasoup, mediasoup can send DaraChannel messages of unlimited size to clients, although maximum size IN RECEPTION allowed in libwebrt based browsers is around 4 mb.

That’s even more interesting than I expected :slightly_smiling_face:

The resilience argument already made me rethink some of my assumptions, but knowing you’ve been doing this in production with Mediasoup itself gives a lot more weight to the approach.

Also, thanks for the clarification about the new SCTP implementation and the browser-side limits. I’ll update the article/README to mention it, since it’s valuable information for anyone experimenting with DataChannels.

Thank you for sharing the POC!

In MiroTalk SFU, I use SCTP data channels over WebRTC for the public chat feature, with an automatic fallback to Socket.IO in case of any issues or connectivity problems.

What connectivity issues are you facing to have a fallback on socket.io? Firewall-alike ones? I though TURN was only needed for media, is SCTP also blocked there sometimes?

The WebSocket fallback is just there as an extra safety net. Public chat uses WebRTC SCTP data channels by default, while the AI chat and private chat are already handled through WebSocket in the chat section. Since that infrastructure was already in place, I added a fallback to WebSocket for the public chat as well, so the chat can continue working if the WebRTC data channel is unavailable, for example during a peer connection restart or due to a browser-specific WebRTC issue…

Shouldn’t the private chat be using the DataChannels exclusively instead of using the WebSockets? It’s said, with End2End Encription it doesn’t matter, but a direct DataChannel connection is simpler and more secure…

IMHO, the current MiroTalk SFU architecture is reasonable: mediasoup DataChannels fit real-time in-room events (public chat…), while WebSockets are often simpler for private messages because the backend already handles auth, presence, routing…

In mediasoup’s SFU model, a “private” DataChannel message is not client-to-client P2P: it goes client → SFU → client. DataChannels don’t bypass the server, they’re just another transport over the existing client–SFU WebRTC connection.

WebRTC DataChannels (DTLS) and WebSockets (TLS) both encrypt in transit, but neither is end-to-end encrypted by default. If you need the SFU/server unable to read messages, add application-layer E2EE.

  • DataChannel: real-time room events / broadcast
  • WebSocket: signaling, presence, private messaging (esp. with persistence/offline)
  • E2EE: true confidentiality when required

That makes sense to keep the current architecture.

My confusion cames from here, I though private ones were client-to-client in your implementation.

In mediasoup there isn’t a client-to-client private DataChannel the way there is in pure WebRTC P2P.

Even if the application calls it private, that simply means the SFU or application server routes the message only to the intended recipient. The transport path is still:

sender → SFU → recipient

In other words, “private” refers to message routing, not network topology.

A true client-to-client private DataChannels require a separate direct RTCPeerConnection between the two peers (mesh), outside the normal mediasoup SFU model, which is what we do in MiroTalk P2P (Mesh-based).