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
RTCPeerConnectionand data channel. You send to it, and listen to it for messages, media and leaving.
Install
Section titled “Install”npm install peer-datapeer-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.
Your first room
Section titled “Your first room”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:
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.
Run it
Section titled “Run it”-
Clone the repository and serve the examples:
Terminal window git clone https://github.com/vardius/peer-data.gitcd peer-datanpm cinpm run examples -
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.
-
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 -- --hostand open the network address it prints.
Or try it right here, in the live demo.
Across browsers and devices
Section titled “Across browsers and devices”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.