Guide
WebRTC without a signaling server
WebRTC needs a way for peers to find each other before they can connect. Trystero gives you that discovery layer without making you deploy a matchmaking backend.
What "without a signaling server" means
A WebRTC connection still starts with signaling: peers exchange connection offers, answers, and network candidates. In many apps, that signaling is a custom WebSocket server you have to deploy, scale, secure, and keep online.
Trystero removes that project-specific server. Peers join a room through a signaling strategy such as Nostr, MQTT, BitTorrent, Supabase, Firebase, IPFS, or a WebSocket relay. Once the WebRTC connection is established, your app data flows directly between peers.
The basic pattern
Pick an appId, join a room, and create actions for the realtime things your app sends. The room ID can be a document ID, lobby code, call ID, or any other shared key.
import {joinRoom} from 'trystero'
const room = joinRoom({appId: 'com.example.whiteboard'}, documentId)
const draw = room.makeAction('draw')
draw.onMessage = (stroke, {peerId}) => {
renderRemoteStroke(peerId, stroke)
}
canvas.onpointermove = event => {
draw.send({
x: event.clientX,
y: event.clientY,
pressure: event.pressure
})
}When to bring your own relay
Serverless discovery is a good default for demos, multiplayer experiments, local-first tools, collaborative editors, browser-to-browser file transfer, and apps where you want low infrastructure overhead.
Use your own relay when you need tighter operational control, allowlists, compliance boundaries, regional routing, or private infrastructure. The Trystero API stays the same; only the import and relay config change.
import {joinRoom} from '@trystero-p2p/ws-relay'
const room = joinRoom(
{
appId: 'com.example.whiteboard',
relayConfig: {
urls: ['wss://relay.example.com']
}
},
documentId
)