Skip to main content

Getting Started

Five minutes from npm install to two peers talking. If you can run a static HTML file and a Node script side by side, you can finish this page.

Install

npm install @metered-ca/peer

Or load the UMD bundle from a CDN for a static site:

<script src="https://unpkg.com/@metered-ca/peer/dist/index.umd.js"></script>
<script>
const { MeteredPeer } = window.MeteredPeer;
</script>

Get a key from the dashboard

  1. Sign up (skip if you already have an account).
  2. Dashboard → Realtime Messaging → Keys → Create key.
  3. Pick a key type:
Key typeWhen to useWhere it goes
pk_live_… publishableStatic sites, prototypes, no per-user scopingDirectly in browser code
sk_live_… secretProduction apps with per-user permissions, custom peerId, embedded TURN credsServer-side only — your backend mints JWTs

Copy the signing secret when it's shown. It's only displayed once.

For the rest of this page we'll use the pk_live_ path so the example is one file. See Authentication when you're ready to move to JWT minting.

Your first connection

SignallingClient is the smaller surface — pub/sub, no WebRTC. Good for chat, telemetry, anywhere you don't need peer-to-peer media.

import { SignallingClient } from "@metered-ca/peer";

const client = new SignallingClient({ apiKey: "pk_live_…" });

client.on("connected", ({ peerId }) => {
console.log("connected as", peerId);
});

client.on("message", ({ channel, from, data }) => {
console.log(`${from}${channel}:`, data);
});

await client.connect();
await client.subscribe("room-42");
await client.publish("room-42", { hello: "world" });

Open this in two browser tabs. Each tab gets a different peerId; one tab's publish shows up as a message event in the other.

That's the whole pub/sub model. Subscribe to channels, publish to channels, receive message events for everything you've subscribed to.

Add peers (WebRTC)

MeteredPeer is the same thing plus channel-driven peer discovery + per-peer RTCPeerConnection lifecycle + MediaStream fan-out. Use this when peers exchange media (video / audio / screen share) or want low-latency P2P data.

import { MeteredPeer } from "@metered-ca/peer";

const peer = new MeteredPeer({ apiKey: "pk_live_…" });

peer.on("peer-joined", ({ peer: remote }) => {
console.log("someone joined:", remote.id);
remote.on("track", ({ streams }) => {
document.querySelector("#remote").srcObject = streams[0];
});
});

peer.on("peer-left", ({ peer: remote }) => {
console.log("someone left:", remote.id);
});

peer.on("data", ({ senderPeerId, data }) => {
console.log(senderPeerId, "said:", data);
});

const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
peer.addStream(localStream);

await peer.join("my-room");
await peer.send({ hi: "everyone" });

What this does:

  1. join("my-room") connects the WebSocket, subscribes you to my-room, and asks the server who else is here.
  2. For every peer the server reports, peer-joined fires and the SDK opens a peer-to-peer RTCPeerConnection under the hood.
  3. The local stream you addStream'd is sent to every peer; you receive theirs on the track event.
  4. peer.send(data) broadcasts to everyone in the channel; peer.sendTo(remoteId, data) targets one peer.

Two browser tabs, same code → working call. No SDP, no ICE, no TURN config (works with embedded TURN — see Authentication).

Messages are server-routed by default

peer.send(data) is server-routed, not P2P over WebRTC DataChannel. It works before ICE completes, but it goes Browser → Metered server → Browser, so:

  • Same API call broadcasts to the channel or directs to one peer
  • Works even before the peer connection finishes negotiating
  • Each message counts against your signalling-message quota

If you need low-latency P2P data (gaming, large transfers), open a real RTCDataChannel via the escape hatch:

peer.on("peer-joined", ({ peer: remote }) => {
remote.on("state-change", ({ to }) => {
if (to !== "connected") return;
const dc = remote.pc.createDataChannel("game-state", { ordered: false });
dc.onmessage = (e) => handleGameTick(JSON.parse(e.data));
});
});

See Data Channels & Low Latency for the full pattern (including backpressure handling and the reconnect gotcha that bites every developer once).

Next step depends on what you're building

You're building…Read
WebRTC video call (with TURN, with a backend)Guide: WebRTC Video Call
WebRTC video call with no backend (static site)Guide: WebRTC No Backend
Chat / classroom / livestream chatGuide: Presence & Chat
AI agent commsGuide: AI Agent Communication
IoT pub/subGuide: IoT Telemetry
Anything going to productionGuide: Reconnect Best Practices — required reading
Moving from simple-peer / PeerJSMigration guides

If you want to switch to per-user JWTs (custom peerId, channel permissions, embedded TURN creds), the Authentication guide walks through both paths.