Skip to content

Getting started

peer-data connects browsers over WebRTC. Three things make a connection:

  • PeerData is this browser. You give it the signaling it reaches other peers through.
  • A room is a name peers join with peer.connect(name). Joining tells the others in it you are there.
  • A participant is each other peer in the room, with its own RTCPeerConnection and data channel. You send to it, and listen to it for messages, media and leaving.
Terminal window
npm install peer-data

peer-data is an ES module with types. It runs in current Chrome, Edge, Firefox and Safari. In a Vite app, Vite shows how to run signaling on Vite’s own server.

Peers need a way to find each other before WebRTC can connect them directly: that is signaling. Between tabs of the same browser, BroadcastChannelSignaling needs no server, so it is the quickest way to start.

This chat joins a room called chat, sends each line to everyone in it, and reports how many peers it can talk to:

examples/chat/chat.ts
import { PeerData, type Participant, type Signaling } from 'peer-data';
export interface Line {
from: string;
text: string;
}
export interface ChatEvents {
/** A line from someone else. */
line(line: Line): void;
/** The number of peers this one can talk to changed. */
peers(count: number): void;
}
/** Joins the chat as name. Lines go to everyone connected; theirs arrive through on.line. */
export function joinChat(signaling: Signaling, name: string, on: ChatEvents) {
const peer = new PeerData(signaling);
const room = peer.connect('chat');
const connected = new Set<Participant>();
room.on('participant', (participant) => {
participant
.on('connected', () => {
connected.add(participant);
on.peers(connected.size);
})
.on('message', (data) => {
const line = parseLine(data);
if (line) on.line(line);
})
.on('disconnected', () => {
if (connected.delete(participant)) on.peers(connected.size);
});
});
return {
say: (text: string): void => {
room.send(JSON.stringify({ from: name, text } satisfies Line));
},
leave: (): void => {
peer.close();
},
};
}
/** A line from a peer, or undefined if it sent something else: peers can send anything. */
function parseLine(data: string | ArrayBuffer): Line | undefined {
if (typeof data !== 'string') return undefined;
try {
const value: unknown = JSON.parse(data);
if (typeof value !== 'object' || value === null || !('from' in value) || !('text' in value)) return undefined;
const { from, text } = value;
return typeof from === 'string' && typeof text === 'string' ? { from, text } : undefined;
} catch {
return undefined;
}
}

Use it with signaling between tabs:

import { BroadcastChannelSignaling } from 'peer-data';
import { joinChat } from './chat';
const chat = joinChat(new BroadcastChannelSignaling(), 'Ada', {
line: ({ from, text }) => console.log(`${from}: ${text}`),
peers: (count) => console.log(`connected to ${count}`),
});
// Later, from a form:
chat.say('hello');

room.send sends to the participants in the room at that moment, so a line said while no one else is there reaches no one. A participant that has joined but isn’t connected yet gets it once its channel opens.

  1. Clone the repository and serve the examples:

    Terminal window
    git clone https://github.com/vardius/peer-data.git
    cd peer-data
    npm ci
    npm run examples
  2. Open the chat at http://localhost:5173/chat/ in two tabs. Each tab says who it is and how many peers it is connected to; what you type in one appears in the other.

  3. Open http://localhost:5173/socket/ in two browsers. This chat signals through the socket.io server the examples run, so it works between browsers. To open it on another device on your network, serve the examples with npm run examples -- --host and open the network address it prints.

Or try it right here, in the live demo.

BroadcastChannel only reaches tabs of the same browser. To connect browsers anywhere, signal through a server, and give the connections ICE servers so they can find a way through routers and firewalls:

import { io } from 'socket.io-client';
import { PeerData, SocketChannel } from 'peer-data';
const peer = new PeerData(new SocketChannel(io('https://signaling.example.com')), {
configuration: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
});

A STUN server lets most peers connect directly. Peers behind stricter networks need a TURN server to relay their traffic; add it to iceServers with its credentials.