A full-mesh WebRTC swarm built on top of simple-peer


88110.4.1a year ago3 years agoMinified + gzip package size for @mattkrick/fast-rtc-swarm in KB



A full-mesh WebRTC swarm built on top of WebSockets & fast-rtc-peer


yarn add @mattkrick/fast-rtc-swarm

What is it

fast-rtc-peer offers a great API to connect 2 peers. If you'd like to connect more than 2 peers, you're on your own. That's why this exists. It uses a full mesh (vs. partial mesh) network so every client is connected to every other client. A full mesh is great for up to ~100 connections. After that, you'll probably want to move to a partial mesh & trade a little latency for memory.

How's it different from webrtc-swarm?

fast-rtc-swarm is different.

  • It's built on fast-rtc-peer, which is built on the new WebRTC v1.0 spec (transceivers instead of stage 2 tracks or stage 1 streams)
  • The signaling server doesn't have to be server sent events. It can be anything. (see reference implementation)
  • It doesn't bother the signaling server with a heartbeat. We can derive that info from the swarm by listening to disconnected on each peer. If timeouts are an issue, then add a WebSocket#ping on your server. Don't make the client do more work than necessary!
  • It only connects to 1 signaling server. Multiple servers is a proxy problem. Again, don't make the client work hard!
  • No unauthenticated-by-default signaling server CLI. I'm not gonna make it easier for you to write an insecure server :-)
  • No multiplexing streams. If you need a new data channel, open another one natively, not with expensive (and LARGE) stream packages.
  • It uses the fast-rtc protocol for the fastest connection possible

What makes it so fast?

The fast-rtc protocol completes a WebRTC handshake in only 2 round trips. Other implementations take 3 (or even 4!) It does this by keeping a peerBuffer of offers and candidates. Think of it like "pay-it-forward". You buy the person behind you a coffee, so they get a free coffee when they get to register & buy the person behind them a coffee.

Here's how it works:

Alice is the first peer to join the swarm:

  • She gives the server an OFFER that can be used by the next person to join
  • As CANDIDATES and additional OFFERS trickle in, she forwards them to the server
  • The server groups CANDIDATES and OFFERS in a single CHUNK, awaiting an ACCEPT
  • When someone ACCEPTS her CHUNK, the server REQUESTS another from her
  • As CANDIDATES and OFFERS continue to trickle in, they are forwarded to client that ACCEPTED the original chunk

When Bob joins the swarm:

  • He follows the same procedure as Alice
  • He takes one CHUNK from each client on the server
  • If Alice does not have a CHUNK readily available, Bob puts his name on her waiting list
  • On the client, Bob creates an ANSWER to each OFFER and forwards it to the signaling server
  • The signaling server forwards Bob's ANSWER to Alice
  • Alice uses Bob's ANSWER to initiate the connection

That's it! See reference implementation and the example below to see how to add it to your own server.


// client
import FastRTCSwarm from '@mattkrick/fast-rtc-swarm'

const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener('open', () => {
  const swarm = new FastRTCSwarm()
  // send the signal to the signaling server
  swarm.on('signal', (signal) => {
  // when the signal come back, dispatch it to the swarm
  socket.addEventListener('message', (event) => {
    const payload = JSON.parse(event.data)
  // when the connection is open, say hi to your new peer
  swarm.on('dataOpen', (peer) => {
    console.log('data channel open!')
  // when your peer says hi, log it
  swarm.on('data', (data, peer) => {
    console.log('data received', data, peer)


Config: A superset of fast-rtc-peer's config. To add a TURN server to the default list of ICE candidates, see fast-rtc-peer.

  • roomId: a string to define the room all the peers are joining
  • peerBuffer: the number of additional offers to keep on the server (default 0)

Methods on FastRTCSwarm

  • addStreams(streamDict): similar to fast-rtc-peer's addStreams, but updates your tracks for future peers
  • broadcast(message): send a string or buffer to all connected peers
  • close(): destroy all peer connections
  • dispatch(signal): receive an incoming signal from the signal server
  • muteTrack(trackName): mute's the track for all peers & future peers


  • swarm.on('open', (peer) => {}): fired when a peer connects
  • swarm.on('close', (peer) => {}): fired when a peer disconnects
  • swarm.on('data', (data, peer) => {}): fired when a peer sends data
  • swarm.on('signal', (signal, peer) => {}): fired when a peer creates an offer, ICE candidate, or answer.
  • swarm.on('stream', (stream, peer) => {}): fired when a peer creates or updates an audio/video track.
  • swarm.on('error', (error, peer) => {}): fired when a peer has a signaling error.
  • swarm.on('connection', (stream, peer) => {}): fired when a peer's ICE connection state changes



If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.