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.
npm i trystero
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.
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})
}Join a room and get notified as peers connect. Trystero handles discovery, connection negotiation, and splitting large messages into chunks.
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.
Start with public Nostr relays. Switch to MQTT, BitTorrent, Supabase, Firebase, IPFS, or a relay you host. The room API stays the same.
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)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})
}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 with a small example, then make it yours. Trystero is written in TypeScript and released under the MIT license.
Learn when peer-to-peer connections help, how signaling works, and what to plan for before shipping.
What “serverless” means, and where relays still fit.
Send files with progress, receive approval, and size limits.
Decide where your app’s realtime data should flow.
Compare shared rooms with connections to known peer IDs.