Simple one-to-one WebRTC video/voice and data channels
—
Send and receive text or binary data through WebRTC data channels with automatic buffering, backpressure handling, and full Node.js duplex stream compatibility.
Send data directly through the WebRTC data channel once the connection is established.
/**
* Send text/binary data to the remote peer via data channel
* @param chunk - Data to send (string, Buffer, ArrayBufferView, ArrayBuffer, or Blob)
* @throws {Error} ERR_DESTROYED - if peer is destroyed
* @throws {Error} If called before 'connect' event
*/
peer.send(chunk: string | Buffer | ArrayBufferView | ArrayBuffer | Blob): void;Usage Examples:
const peer = new Peer({ initiator: true });
peer.on('connect', () => {
// Send text data
peer.send('Hello, world!');
// Send binary data
const buffer = Buffer.from([1, 2, 3, 4, 5]);
peer.send(buffer);
// Send typed array
const uint8Array = new Uint8Array([6, 7, 8, 9, 10]);
peer.send(uint8Array);
// Send ArrayBuffer
const arrayBuffer = new ArrayBuffer(4);
const view = new DataView(arrayBuffer);
view.setUint32(0, 0x12345678);
peer.send(arrayBuffer);
});
// Don't call send() before connect
peer.send('This will throw an error!'); // ❌ ErrorUse the Node.js duplex stream interface for buffered data communication with automatic backpressure handling.
/**
* Write data to the peer connection (buffered if not connected yet)
* @param chunk - Data to write
* @param encoding - Character encoding (for strings)
* @param callback - Completion callback
* @returns false if buffer is full (backpressure)
*/
peer.write(chunk: any, encoding?: string, callback?: (err?: Error) => void): boolean;
/**
* Read data from the peer connection
* @param size - Number of bytes to read (optional)
* @returns Data read from stream or null if no data available
*/
peer.read(size?: number): any;
/**
* End the writable side of the stream
* @param chunk - Optional final chunk to write
* @param encoding - Character encoding
* @param callback - Completion callback
*/
peer.end(chunk?: any, encoding?: string, callback?: () => void): void;Usage Examples:
const Peer = require('simple-peer');
// Stream-style data sending
const peer1 = new Peer({ initiator: true });
const peer2 = new Peer();
// Setup signaling
peer1.on('signal', data => peer2.signal(data));
peer2.on('signal', data => peer1.signal(data));
// Write data (buffered until connected)
peer1.write('Message 1');
peer1.write('Message 2');
peer1.write(Buffer.from('Binary message'));
// Handle backpressure
const success = peer1.write('Large message');
if (!success) {
peer1.once('drain', () => {
console.log('Buffer drained, can write more');
});
}
// End the stream
peer1.end('Final message');Receive and handle incoming data from the remote peer.
// Data event for incoming messages
peer.on('data', (data: Buffer | string) => void);
// Stream readable events
peer.on('readable', () => void);
peer.on('end', () => void);
// Stream writable events
peer.on('drain', () => void);
peer.on('finish', () => void);Usage Examples:
const peer = new Peer();
// Handle incoming data
peer.on('data', data => {
if (Buffer.isBuffer(data)) {
console.log('Received binary data:', data.length, 'bytes');
console.log('As string:', data.toString());
} else {
console.log('Received text:', data);
}
});
// Stream-style reading
peer.on('readable', () => {
let chunk;
while ((chunk = peer.read()) !== null) {
console.log('Read chunk:', chunk);
}
});
peer.on('end', () => {
console.log('Remote peer ended the stream');
});
// Handle buffer drainage
peer.on('drain', () => {
console.log('Write buffer drained - can write more data');
});
// Handle stream finish
peer.on('finish', () => {
console.log('Local peer finished writing data');
});Use standard Node.js stream patterns for data processing.
/**
* Pipe data to another stream
* @param destination - Destination stream
* @param options - Pipe options
* @returns Destination stream
*/
peer.pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean }): T;Usage Examples:
const fs = require('fs');
const { Transform } = require('stream');
const peer1 = new Peer({ initiator: true });
const peer2 = new Peer();
// Setup signaling
peer1.on('signal', data => peer2.signal(data));
peer2.on('signal', data => peer1.signal(data));
// Pipe to file
peer2.pipe(fs.createWriteStream('received-data.txt'));
// Pipe through transform stream
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase());
}
});
peer2.pipe(upperCaseTransform).pipe(process.stdout);
// Send data once connected
peer1.on('connect', () => {
peer1.write('hello world\n');
peer1.write('this is a test\n');
peer1.end();
});Monitor and manage data channel buffering for optimal performance.
/**
* Amount of data buffered in the data channel
*/
peer.bufferSize: number;
// Events for buffer management
peer.on('drain', () => void);Usage Examples:
const peer = new Peer({ initiator: true });
peer.on('connect', () => {
// Check buffer size before sending large data
if (peer.bufferSize < 32768) { // 32KB threshold
peer.send(largeBuffer);
} else {
console.log('Buffer full, waiting for drain');
peer.once('drain', () => {
peer.send(largeBuffer);
});
}
});
// Monitor buffer size
setInterval(() => {
console.log('Current buffer size:', peer.bufferSize, 'bytes');
}, 1000);Enable object mode for sending JavaScript objects directly.
// Constructor option for object mode
const peer = new Peer({
initiator: true,
objectMode: true
});Usage Examples:
const peer1 = new Peer({ initiator: true, objectMode: true });
const peer2 = new Peer({ objectMode: true });
// Setup signaling
peer1.on('signal', data => peer2.signal(data));
peer2.on('signal', data => peer1.signal(data));
peer1.on('connect', () => {
// Send objects directly
peer1.write({ type: 'greeting', message: 'Hello!' });
peer1.write({ type: 'data', payload: [1, 2, 3, 4, 5] });
});
peer2.on('data', obj => {
console.log('Received object:', obj);
// { type: 'greeting', message: 'Hello!' }
// { type: 'data', payload: [1, 2, 3, 4, 5] }
});Handle data-related errors and edge cases.
// Data channel specific error codes
interface DataChannelError extends Error {
code: 'ERR_DATA_CHANNEL' | 'ERR_DESTROYED';
}Usage Examples:
const peer = new Peer({ initiator: true });
peer.on('error', err => {
if (err.code === 'ERR_DATA_CHANNEL') {
console.error('Data channel error:', err.message);
} else if (err.code === 'ERR_DESTROYED') {
console.error('Cannot send data - peer destroyed');
}
});
// Handle write errors
peer.write('data', 'utf8', (err) => {
if (err) {
console.error('Write failed:', err.message);
} else {
console.log('Data sent successfully');
}
});
// Safe sending function
function safeSend(peer, data) {
if (peer.destroyed) {
console.error('Cannot send - peer destroyed');
return false;
}
if (!peer.connected) {
console.error('Cannot send - peer not connected');
return false;
}
try {
peer.send(data);
return true;
} catch (err) {
console.error('Send failed:', err.message);
return false;
}
}