@metered-ca/realtime โ the open-source WebRTC library
@metered-ca/realtime is a free, open-source (MIT) WebRTC library for the browser โ about 12.5 KB gzipped, with zero dependencies. It bundles the three things every WebRTC app otherwise rebuilds from scratch โ signalling, connection negotiation, and TURN โ and adds the one thing PeerJS and simple-peer never solved: auto-reconnect that survives network drops.
npm install @metered-ca/realtime
Peer-to-peer WebRTC without the boilerplateโ
With the native WebRTC API โ or with simple-peer โ you hand-wire SDP, ICE, signalling, and reconnection yourself. With @metered-ca/realtime you join a channel and every other peer shows up as an object with events:
import { MeteredPeer } from "@metered-ca/realtime";
const peer = new MeteredPeer({ apiKey: "pk_live_..." }); // browser-only, no backend
await peer.join("room-42");
peer.on("peer-joined", ({ peer: remote }) => {
remote.on("stream-added", ({ stream }) => { remoteVideo.srcObject = stream; });
});
const cam = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
peer.addStream(cam);
Signalling runs over the free Metered Realtime server; TURN is Open Relay, auto-injected. There's nothing to host.
Get your free keyโ
The SDK's apiKey is your signalling publishable key (pk_live_...) โ separate from the TURN REST API_KEY used to fetch Open Relay credentials directly. With @metered-ca/realtime the publishable key is all you need: TURN is auto-injected, so there's no second key to manage. Both are free. Sign up for a free Metered account, then:
- Go to Dashboard โ Realtime Messaging โ Keys โ Create key and choose the Publishable type.
- Under "What can this key do?", make sure
Sendis checked.Subscribe,Publish, andPresenceare on by default โ leave them on. - Copy the
pk_live_โฆvalue and pass it asapiKey.
Send is off by default โ turn it on
For publishable keys, Send starts unchecked. @metered-ca/realtime uses it to exchange SDP and ICE candidates between peers, so without it peer.join() and peer.addStream() succeed but the video never negotiates โ peers never establish their RTCPeerConnection. This is the #1 reason a first call connects with no video.
Full walkthrough: WebRTC quickstart โ
A modern PeerJS & simple-peer alternativeโ
PeerJS and simple-peer are the classic WebRTC abstractions. simple-peer is a lower-level wrapper โ you bring your own signalling and reconnection โ so the head-to-head below is with PeerJS, the closest like-for-like library. Neither restarts the media connection when the network changes, the single biggest reason production WebRTC calls drop; @metered-ca/realtime bundles signalling, reconnection, and TURN out of the box.
| Feature | @metered-ca/realtime | PeerJS |
|---|---|---|
| Bundle size, gzippedยน | ~13 KB | ~30 KB |
| Built-in signalling | โ free, managed | PeerServer / Cloud |
| Auto-reconnect (media) | โ WebSocket + ICE-restart | broker only |
| Identity preserved across drops | โ | โ |
| Perfect negotiation | โ | โ |
| Multi-stream + per-stream metadata | โ | โ |
| Free TURN included | โ 20 GB/mo | โ |
| TypeScript-native | โ | community types |
| License | MIT | MIT |
ยน Sizes via Bundlephobia, June 2026 โ PeerJS 1.5.5 and @metered-ca/realtime 1.0.8 (which ships a 13 KB UMD build). @metered-ca/realtime bundles signalling, reconnection, and TURN and still comes in at under half of PeerJS.
Already on one of them? The drop-in migration guides map every concept across: Migrating from PeerJS โ ยท Migrating from simple-peer โ
Auto-reconnect & perfect negotiationโ
The reason calls drop on PeerJS and simple-peer is that they don't recover when the network changes. @metered-ca/realtime recovers on three layers automatically:
- WebSocket reconnect with exponential backoff.
- ICE-restart ladder โ re-establishes the media path on Wi-Fi โ cellular roams and NAT rebinds.
- Identity-preserving reconcile โ your
RemotePeerreferences stay valid through transient drops; the underlying connection is swapped out silently.
It also uses perfect negotiation, so you never hit glare or "who's the initiator?" race conditions.
Data channelsโ
Send messages to the whole channel or to one peer:
peer.on("data", ({ senderPeerId, data }) => console.log(senderPeerId, data));
await peer.send({ type: "chat", text: "hi everyone" }); // broadcast to the channel
await peer.sendTo(otherPeerId, { ping: Date.now() }); // direct to one peer
For high-frequency game state or large transfers, open a peer-to-peer RTCDataChannel via the documented escape hatch (remote.pc).
Screen sharing & multi-streamโ
Route camera and screen through a single peer, each labelled so the receiver can tell them apart:
const screen = await navigator.mediaDevices.getDisplayMedia({ video: true });
peer.addStream(screen, { role: "screen" });
remote.on("stream-added", ({ stream, metadata }) => {
if (metadata?.role === "screen") screenVideo.srcObject = stream;
else cameraVideo.srcObject = stream;
});
The whole stack is freeโ
- SDK โ free, open source (MIT), ~12.5 KB.
- Signalling โ Metered Realtime, free up to 100 connections and 100,000 messages per month.
- TURN โ Open Relay, 20 GB/month free.
No credit card to get started.
Get a free keyPlatformsโ
One mental model across every platform โ same wire protocol, same free signalling + TURN behind each SDK. Write the model once, run it everywhere.
| Platform | Status |
|---|---|
| JavaScript / TypeScript (browser + Node 18+) | โ Available now |
React Native (via react-native-webrtc) | โ Available now |
| Flutter | ๐ Coming soon |
| iOS (Swift) | ๐ Coming soon |
| Android (Kotlin) | ๐ Coming soon |
React Native runs the same package โ see the React Native guide.
Want early access to the native mobile SDKs (Flutter, iOS, Android)? Tell us โ
FAQโ
Is it really open source? Yes โ MIT licensed, ~12.5 KB gzipped, zero dependencies.
Does it work with React or Node.js?
Yes โ it runs in any modern bundler, and React works out of the box. The pub/sub layer (SignallingClient) also runs in Node 18+ for SSR, tests, and server-side agents; the WebRTC layer (MeteredPeer) needs a browser. A React example is in the docs.
How is it different from PeerJS?
PeerJS is point-to-point and doesn't restart the media connection when the network changes. @metered-ca/realtime is channel-based, auto-reconnects across network changes (WebSocket + ICE restart), and bundles signalling + TURN. See the PeerJS migration guide.
Do I need a backend?
No โ a pk_live_ publishable key runs entirely in the browser. Add backend-minted JWTs when you want per-user permissions and stable peer IDs.
Which browsers are supported? Chrome 90+, Firefox 90+, and Safari 15+.
@metered-ca/realtime is built on the free Metered Realtime signalling server and the Open Relay TURN server โ a complete, free, open-source-first WebRTC stack.