@rill/client
Browser SDK: connect, media, labeled data. Hides SDP and ICE.
Browser SDK. SDP, ICE, and RTCDataChannel stay inside the package. Mint token with @rill/server. Walkthrough: Your app.
npm i @rill/clientUntil a v* tag exists, use this repo’s sdk/ workspace.
import { Rill } from "@rill/client";
const video = document.querySelector("video")!;
async function mintFreshToken(): Promise<string> {
return "paste-jwt";
}
const call = await Rill.connect({
url: "https://rill.example.com",
token: "paste-jwt",
onToken: () => mintFreshToken(),
});
call.on("remote", (remote) => {
video.srcObject = remote?.camera ?? null;
});
call.on("error", ({ code }) => console.error(code));
call.on("ended", () => console.log("call ended"));
await call.microphone.enable();
await call.camera.enable();Connect
Rill.connect(opts: ConnectOptions): Promise<Call>
type ConnectOptions = {
url: string;
token: string;
onToken?: () => string | Promise<string>; // Called once on `token_expired`. Return a freshly minted JWT.
speaker?: boolean; // Hidden speaker for `remote.microphone`. Default on. Pass `false` to opt out.
};Rill.connect— Join with a short-lived JWT. Resolves after the signaling welcome + first offer/answer.speakerdefaults to on: a hidden<audio>playsremote.microphone. PassonTokento rotate once ontoken_expired.ConnectOptions—Rill.connectoptions.urlis the public origin (HTTPS or the site that proxies/v1/signal).
Media
call.microphone: LocalDevice
call.camera: LocalDevice
call.screen: LocalDevice
call.remote: Remote | null
call.speaker: Speaker | null
type LocalDevice = {
enable: (deviceId?: string) => Promise<void>; // Capture this kind. Pass `deviceId` to switch if already on.
disable: () => Promise<void>;
readonly kind: TrackKind;
readonly enabled: boolean;
readonly stream: MediaStream | null; // Local MediaStream when enabled; SDK still hides the PeerConnection.
};
type Remote = {
id: string;
name?: string;
microphone: MediaStream | null;
camera: MediaStream | null;
screen: MediaStream | null;
};call.microphone— Local microphone.enable()isgetUserMedia+replaceTrack.call.screen— Local screen share (getDisplayMedia). Browser “stop sharing” callsdisable().call.remote— Other participant, ornullwhile waiting / after they leave.call.speaker— Hidden speaker forremote.microphone, ornullwhenspeaker: false.Remote— Other participant. Bindcamera/screento<video>. Do not attachmicrophoneto a second<audio>.
Data
call.send(label: string, data: string | Uint8Array): Promise<void>call.send— Send on a labeled data channel. Queues while the local channel isconnecting. Resolves onceRTCDataChannel.sendaccepts the payload (not after remote delivery). A missing peer is not an error — the SFU drops live relay like unpublished camera. Max payload 128 KiB (str0m write buffer); chunk larger snapshots in the app.
Events and hang-up
call.on<K extends keyof CallEvents>(type: K, fn: (payload: CallEvents[K]) => void): () => void
call.state: CallState
call.disconnect(): Promise<void>
type CallEvents = {
remote: Remote | null;
local: LocalSnapshot;
state: CallState;
ended: Record<string, never>;
error: CallError;
data: DataMessage;
};
type DataMessage = {
label: string;
data: string | Uint8Array;
};call.on— Subscribe. Returns unsubscribe.remote— other participant’s streams, ornullwhen they leavelocal—{ microphone, camera, screen }enabled flagsstate—connecting|connected|reconnecting|disconnectedended— slot destroyed (call_endedorserver_shutdown)error—{ code, message }data— labeled message{ label, data }. Do not subscribe fromuseCall.
call.state— Signaling + ICE lifecycle.call.disconnect— Leave this browser. Does not destroy the call; the other person stays.CallEvents— Event map forcall.on.useCalldoes not subscribe todata.DataMessage— Payload fromcall.on("data").datais a string or a copy of the binary frame.
Errors
type ErrorCode =
| "unauthorized"
| "forbidden"
| "call_not_found"
| "call_full"
| "call_ended"
| "participant_invalid"
| "token_expired"
| "overloaded"
| "connection_failed"
| "permission_denied"
| "replaced"
| "server_shutdown"
| "unsupported"
| "internal";ErrorCode— Public errorcodestrings.call_fullis a third join;replacedis last-writer-wins.