The web is the network

Serverless WebRTC for JavaScript apps

Connect browsers. Send messages, share files, or start a video call. Trystero handles the WebRTC setup so you can build your app without deploying a signaling server.

Get started
npm i trystero

A room, a few peers, your app

Trystero is an open source JavaScript library for peer-to-peer apps. Users join the same room and discover each other through existing signaling networks. Once connected, they exchange data over WebRTC. You decide what they build together.

Connect in a few lines

Run this in two browser tabs. When they connect, each tab sends a greeting to the other.

import {joinRoom} from 'trystero'

// Join a room with your app ID and a room ID
const room = joinRoom(
  {appId: 'com.example.myapp'},
  'room-id'
)

const hello = room.makeAction('hello')

hello.onMessage = (message, {peerId}) => {
  console.log(peerId, message)
}

room.onPeerJoin = peerId => {
  hello.send('Hello from the other tab!', {target: peerId})
}

Skip the setup

Join a room and get notified as peers connect. Trystero handles discovery, connection negotiation, and splitting large messages into chunks.

Keep data between peers

WebRTC encrypts data and media between peers. Signaling relays carry connection details, not your app’s messages. Add a shared password or your own handshake to control who joins.

Choose your signaling

Start with public Nostr relays. Switch to MQTT, BitTorrent, Supabase, Firebase, IPFS, or a relay you host. The room API stays the same.

Give your messages a name

Create an action for chat, cursor positions, or game state. Send strings, objects, and binary data to the room or a specific peer. TypeScript types help keep your messages consistent.

type CursorPosition = {x: number; y: number}
const cursor = room.makeAction<CursorPosition>('cursor')

// Send pointer positions to connected peers
document.onpointermove = e =>
  cursor.send({x: e.clientX, y: e.clientY})

// Receive the cursor position from other peers
cursor.onMessage = (pos, {peerId}) =>
  movePeerCursor(peerId, pos)

Bring a camera and a microphone

Share camera, microphone, and screen streams with peers in the room. Trystero handles the connections; you handle the call UI.

// Attach the receiver before requesting camera access
room.onPeerStream = (stream, peerId) => {
  videoElements[peerId].srcObject = stream
}

const stream = await navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true
})

// Send the stream to peers in the room
room.addStream(stream)

// Send the stream to peers who join later
room.onPeerJoin = peerId => {
  room.addStream(stream, {target: peerId})
}

Ask peers for answers

Need more than a message? Ask a peer for its current state and await the reply. Request actions also let you collect answers from several peers, with a timeout for each.

// Create a request/response action
const availability = room.makeAction('availability', {
  kind: 'request',
  onRequest: ({date}) => calendar.isFree(date)
})

// Ask a specific peer and wait for their answer
const isFree = await availability.request(
  {date: '2026-05-04'},
  {
    target: teammateId,
    timeoutMs: 1000
  }
)

if (isFree) {
  showAvailable(teammateId)
}

Start building

Start with a small example, then make it yours. Trystero is written in TypeScript and released under the MIT license.

Learn more about building peer-to-peer apps

Learn when peer-to-peer connections help, how signaling works, and what to plan for before shipping.