CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-simple-peer

Simple one-to-one WebRTC video/voice and data channels

Pending
Overview
Eval results
Files

data.mddocs/

Data Communication

Send and receive text or binary data through WebRTC data channels with automatic buffering, backpressure handling, and full Node.js duplex stream compatibility.

Capabilities

Direct Data Sending

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!'); // ❌ Error

Stream Interface

Use 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');

Data Reception

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');
});

Piping and Transform Streams

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();
});

Buffer Management

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);

Object Mode

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] }
});

Error Handling

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;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-simple-peer

docs

connection.md

data.md

index.md

info.md

media.md

tile.json