Client Preferences
client.preferences holds per-client defaults the SDK reads when
no per-call options override them: which mic / camera to use, whether
to receive video by default, ICE / recovery tuning, codec ordering,
and custom userVariables attached to every call. Preferences live
in the browser, optionally persist to localStorage, and are
distinct from per-User configuration (which lives on the
platform — see Users).
This page covers how preferences fit into the SDK lifecycle. For the
full property list, see ClientPreferences.
Defaults vs. per-call overrides
Anything set on preferences applies to every subsequent
dial() that doesn’t pass a competing field.
Per-call options always win:
Use preferences for app-wide defaults (codec ordering, a tier-wide
userVariables payload). Use per-call options for situational values.
For example, codec ordering is an app-wide default — the array is a priority list of codec names, and it’s overridable per call:
Common preferences
The full surface is documented in the ClientPreferences reference. These are the ones most
apps touch, with their code defaults:
Persistence
By default, preferences live in memory only. Set
savePreferences: true to hydrate from localStorage on startup and
write back on every setter:
The following details are persisted: timeouts, ICE / recovery tuning, codec preferences, stats and
device-management flags, and userVariables.
Device selections persist separately, and are on by default. Independent of
savePreferences, the device controller writes your mic / camera / speaker
selections to localStorage (keyed by deviceId, keeping label / groupId to
re-match when IDs rotate across sessions) and restores them next time. This is
governed by the persistDeviceSelection
preference (default true); set it to false to opt out.
For a different storage backend (IndexedDB, server-side per user),
leave savePreferences off and mirror manually:
ClientPreferences is a synchronous object — there is no update$
observable. Preferences are read at dial time.
userVariables
userVariables is a free-form payload attached to every outbound call.
The receiving side (an AI agent, a SWML script, a backend) reads it.
Set on preferences for app-wide values; pass to dial() for per-call
attribution.
Time units
Timeouts on the preferences surface are exposed in seconds (stored as milliseconds internally):
Other fields use the unit of the underlying API (kbps, integer levels, etc.).
Keyframe recovery
A video stream consists of occasional keyframes — complete, self-contained frames — each followed by delta frames that encode only the change from the previous frame. A lost or corrupted delta frame corrupts every frame after it until the next keyframe arrives. The receiver can request one early via an RTCP feedback message:
- PLI — Picture Loss Indication: standard picture-loss recovery.
- FIR — Full Intra Request: forces a full intra frame from scratch, e.g. when a new participant or recorder joins mid-stream with no reference frame.
Keyframes are large, so the SDK rate-limits these requests as a burst with cooldown:
Up to keyframeMaxBurst requests are allowed per keyframeBurstWindow; once
that limit is hit, requests pause for keyframeCooldown. Defaults: three
requests per three-second window, then a ten-second cooldown.
Reference
ClientPreferences— the property surfaceSignalWire.preferences— the instanceSignalWireOptions—savePreferences,skipDeviceMonitoring,reconnectAttachedCalls,persistSessionSignalWire.dial()— per-call overrides