PeerLite
Lightweight WebRTC browser library that supports video, audio and data channels.
Features
- Lightweight! 3kb (gzipped)
- Zero dependencies
- Ships with TypeScript definitions
- Uses modern WebRTC APIs
- "Perfect negotiation" pattern
- Support for renegotiation of connection
- ICE candidate batching
Installation
yarn add peer-lite
Usage
Two peers connecting locally
import Peer from 'peer-lite';
const peer1 = new Peer();
const peer2 = new Peer();
peer1.on('signal', async (description) => {
await peer2.signal(description);
})
peer2.on('signal', async (description) => {
await peer1.signal(description);
})
peer1.on('onicecandidates', async (candidates) => {
const promises = candidates.map(async candidate => peer2.addIceCandidate(candidate));
await Promise.all(promises);
});
peer2.on('onicecandidates', async (candidates) => {
const promises = candidates.map(async candidate => peer1.addIceCandidate(candidate));
await Promise.all(promises);
});
peer1.on('streamRemote', (stream) => {
document.querySelector('#video1').srcObject = stream;
});
peer2.on('streamRemote', (stream) => {
document.querySelector('#video2').srcObject = stream;
});
(async () => {
const stream = await Peer.getUserMedia();
peer1.addStream(stream);
peer2.addStream(stream);
peer1.start();
})();
Peer connection with fake signalling server
import Peer from 'peer-lite';
const peer = new Peer();
const fakeSocket = new Socket();
peer.on('signal', async (description) => {
fakeSocket.emit('signal', description);
});
peer.on('onicecandidates', async (candidates) => {
fakeSocket.emit('onicecandidates', candidates);
});
peer.on('streamLocal', (stream) => {
document.querySelector('#videoLocal').srcObject = stream;
});
peer.on('streamRemote', (stream) => {
document.querySelector('#videoRemote').srcObject = stream;
});
fakeSocket.on('signal', async (description) => {
await peer.signal(description);
});
fakeSocket.on('onicecandidates', async (candidates) => {
const promises = candidates.map(async candidate => peer.addIceCandidate(candidate));
await Promise.all(promises);
});
(async () => {
const stream = await Peer.getUserMedia();
peer.addStream(stream);
peer.start();
})();
Examples
See more examples here with signalling server.
API
Constructor
new Peer(Options)
Peer Options
interface PeerOptions {
batchCandidates?: boolean;
batchCandidatesTimeout?: number;
id?: string;
config?: RTCConfiguration;
offerOptions?: RTCOfferOptions;
enableDataChannels?: boolean;
channelLabel?: string;
channelOptions?: RTCDataChannelInit;
sdpTransform?: (sdp: string) => string;
}
Peer API
interface Peer {
constructor(options?: PeerOptions);
init(): RTCPeerConnection;
start({ polite }?: {
polite?: boolean | undefined;
}): void;
signal(description: RTCSessionDescriptionInit): Promise<void>;
addIceCandidate(candidate: RTCIceCandidate): Promise<void>;
send(data: string | Blob | ArrayBuffer | ArrayBufferView, label?: string): boolean;
addDataChannel(label?: string, options?: RTCDataChannelInit): void;
getDataChannel(label?: string): RTCDataChannel | undefined;
destroy(): void;
status(): RTCIceConnectionState;
isConnected(): boolean;
isClosed(): boolean;
get(): RTCPeerConnection;
getStreamLocal(): MediaStream;
addStream(stream: MediaStream, replace?: boolean): void;
removeStream(stream: MediaStream): void;
addTrack(track: MediaStreamTrack): void;
removeTrack(track: MediaStreamTrack): void;
removeTracks(tracks: MediaStreamTrack[]): void;
replaceTrack(track: MediaStreamTrack, newTrack: MediaStreamTrack): Promise<void>;
on<E extends keyof PeerEvents>(event: E, listener: PeerEvents[E]): TypedEmitter<PeerEvents>;
off<E extends keyof PeerEvents>(event: E, listener: PeerEvents[E]): TypedEmitter<PeerEvents>;
offAll<E extends keyof PeerEvents>(event?: E): TypedEmitter<PeerEvents>;
}
Peer Events
interface PeerEvents {
error: (data: { id: string; message: string; error?: Error }) => void;
connecting: VoidFunction;
connected: VoidFunction;
disconnected: VoidFunction;
status: (status: RTCIceConnectionState) => void;
signal: (description: RTCSessionDescriptionInit) => void;
onicecandidates: (iceCandidates: RTCIceCandidate[]) => void;
streamLocal: (stream: MediaStream) => void;
streamRemote: (stream: MediaStream) => void;
channelOpen: (data: { channel: RTCDataChannel }) => void;
channelClosed: (data: { channel: RTCDataChannel }) => void;
channelError: (data: { channel: RTCDataChannel; event: RTCErrorEvent }) => void;
channelData: (data: {
channel: RTCDataChannel;
source: 'incoming' | 'outgoing';
data: string | Blob | ArrayBuffer | ArrayBufferView;
}) => void;
}
Testing
The tests run inside a headless Chrome and Firefox with Playwright
and @playwright/test.
These run quickly and allow testing of WebRTC APIs in real browsers.
Run Tests (Chrome only)
yarn test
Run Tests (Chrome + Firefox)
CI=true yarn test
Similar Projects