Call Controls
Every control on a call follows the same shape: call a mutator,
subscribe to the matching $ observable for state. The server is
the source of truth — a moderator can mute you, the room can lock
itself, the platform can disconnect. Local state (let isMuted = …)
will drift; the observable won’t.
This page covers the pattern. Per-method details live in the reference.
You’ll need an active call. Call controls operate on a Call
instance — get one of these going first.
Install the SDK and create a SignalWire client with a credential provider.
Dial a destination with client.dial() to get a Call instance.
Subscribe to client.session.incomingCalls$ and call.answer().
The pattern
Every other control is the same shape with different names —
toggleMuteVideo / videoMuted$, toggleDeaf / deaf$,
toggleHandraise / handraised$, etc.
Three properties make this work:
- Toggles are idempotent. Calling
toggleMutemid-flight is safe — the SDK serializes. $observables emit on subscribe. The current value arrives immediately; no wait-for-event step.- The mutator only triggers the change; the observable closes the
loop. A moderator-initiated mute that never went through the
button still updates the UI, because
audioMuted$emits.
Where each control lives
Three objects own the controls:
Call— session-level: hangup, send DTMF, lock, hold, transfer, layout (see Layouts).SelfParticipant(call.self) — your own state: mute, deaf, hand raise, screen share, audio processing, your volume.Participant(entries incall.participants$) — moderation actions on other members. Gated by capabilities — see below.
The split mirrors server-side authorization: ending a call needs the
end capability, kicking someone needs member.remove, muting
yourself is unconditional.
Mute vs. deaf vs. hold
Mute vs. deaf
Mute silences what you send. Deaf silences what you hear. They’re independent — you can be deaf without being muted (you keep talking, but you can’t hear responses). Useful when the user steps away briefly without leaving the room.
Mute vs. hold vs. push-to-talk
Three ways to stop transmitting audio, and they’re not interchangeable:
For instant talk/silence transitions (e.g. holding spacebar), use push-to-talk — mute would feel laggy because the round-trip is visible to the user:
The local audio pipeline also gives you localAudioLevel$ for a
real-time meter and localSpeaking$ for VAD-based speaking
detection — both are observables of the local mic, computed
client-side, fast enough for ~30fps UI updates.
DTMF, timing matters
sendDigits only succeeds once status$ is
'connected'. Sending before media is negotiated will fail or be
dropped:
For interactive dialpads (digits sent as the user presses), wire the button click directly — by that point the call is connected.
Moderation — check the capability first
Methods on other participants exist (participant.mute(),
participant.remove(), participant.setPosition()), but calling them
without the corresponding capability throws server-side. Drive the UI
off SelfCapabilities.member$:
If the flag is false, hide the button. See Capabilities for the full model.
Handling unsuccessful attempts
Most toggles resolve cleanly, but three categories can throw — handle them, don’t let an unhandled rejection bubble up:
Only browser-permission errors need a try/catch on each click.
Capability errors shouldn’t be reachable — gate the button instead.
Connection-state errors are prevented by waiting on status$ (see
DTMF, timing matters).
The pure-mute toggles (toggleMute, toggleDeaf) don’t require
any browser permission — they only flip server-side state — so they
won’t throw on permission. They can still throw on capability or
connection state.
Example: control bar
Every button uses the same mutator + observable shape:
Volume sliders, audio-processing toggles, the screen-share button, and moderation actions all follow the same shape.
Reference
For the full surface — every method and every observable — see the per-class reference:
Participant— mute, deaf, hand raise, audio processing, server-mixed volumes; also the surface for moderation actions on other members (remove,end,setPosition).SelfParticipant— addsenableStudioAudio/disableStudioAudioon top ofParticipant.WebRTCCall— session-level controls:hangup,sendDigits,toggleLock,toggleHold,transfer, local-mic pipeline (setLocalMicrophoneGain,localAudioLevel$,localSpeaking$,enablePushToTalk,setPushToTalkActive).SelfCapabilities— server-authoritative flags for gating moderation UI; see Capabilities.