Light is a small TypeScript library for apps whose peers talk directly to each other: collaborative editors, multiplayer games and local-first sync engines. It turns a WebRTC data channel into two things a sync engine needs: reliable, typed RPC for operations that must arrive, and unreliable datagrams for state that is stale the moment a newer update exists.
Peer A Peer B
+-----------+ RTCDataChannel +-----------+
| sync |------- call(id, method) ------>| handler |
| engine |<------ result(id, value) ------| |
+-----------+ +-----------+
\ /
\______ signaling, only to connect ________/
Figure 1: Request and response over a data channel
Types on both ends
Each peer declares the methods it exposes as a plain TypeScript type. Light infers every call’s arguments and return type from it, so calling a method the other side doesn’t have, or passing the wrong argument, is a compile error rather than a runtime surprise.
type PeerB = {
greetUser: (name: string) => string;
};
const peer = new LightPeer<PeerA, PeerB>({ initiator: true, handlers });
const greeting = await peer.call('greetUser', 'Alice'); // string
await peer.call('greetUser', 123); // type error
Two channels, two promises
| RPC | Datagrams | |
|---|---|---|
| Delivery | Reliable and ordered | Best effort, maxRetransmits: 0 |
| Shape | Request and typed response | Topic and payload |
| Use it for | Operations, document edits, commands | Cursors, presence, game state |
A cursor position that arrives late is worse than one that never arrives, so datagrams are never retransmitted: the next update replaces the lost one.
Built for flaky networks
- Calls queue while connecting. Calls made before the channel opens, or during a reconnect, wait and go out once it is up.
- Automatic reconnection with a configurable retry budget (three by default), and events for every state change.
- Bring your own signaling. Light only needs a way to pass a few messages to set up the connection: a WebSocket, a broadcast channel, anything.
- One code path everywhere. It uses the browser’s native WebRTC and
falls back to
weriftin Node.js and Bun, so the same peer code runs in a tab, a server or a CLI.
Every message is framed with Protocol Buffers, and binary payloads go
through as raw Uint8Arrays with no base64 detour.