Web
@aurix/web-sdk for browsers and Electron — WebTransport, WebRTC and WebSocket transports, Opus in a worklet, device handling, effects.
Install#
npm i @aurix/web-sdkShips as ESM with types. Requires a secure context (HTTPS or localhost) for microphone access.
Connect and join#
import { AurixClient } from '@aurix/web-sdk';
const t = await fetch('/voice/token', { method: 'POST' }).then((r) => r.json()); // your backend
const voice = new AurixClient({
apiUrl: 'https://eu-central.cell.aurix.run', // your project's cell endpoint (Keys & SDK)
wsUrl: t.endpoint.ws_url,
token: t.token,
refreshToken: () => fetch('/voice/token', { method: 'POST' }).then((r) => r.json()).then((x) => x.token),
transport: 'auto', // webtransport → webrtc → websocket; or pin one
});
await voice.connect();
await voice.joinChannel(t.channels[0].id);connect() resolves with the SessionInfo; the microphone is requested lazily on the first join unless you call voice.setInputDevice() earlier. Attach playback to an element you control (voice.attachAudioOutput(audioEl)) or let the SDK create one; call voice.resumeAudio() from a user gesture if autoplay is blocked.
Transports#
| Transport | When | Notes |
|---|---|---|
| WebTransport | Chromium 97+, Firefox 114+ | HTTP/3 over QUIC straight to the node; lowest latency under loss |
| WebRTC | Everywhere | Opus over SRTP with TURN fallback |
| WebSocket | Last resort | TCP tunnel; works behind strict proxies |
Read voice.mediaTransport after connecting to show it in diagnostics; voice.on('mediaTransport', …) fires when it changes.
Events#
voice.on('participantJoined', ({ channelId, participant }) => …); // also participantLeft / participantUpdated
voice.on('speaking', ({ userId, speaking }) => …);
voice.on('stats', (s) => console.log(s)); // RTT, loss, jitter, MOS
voice.on('networkQuality', (q) => …);
voice.on('sessionClosed', ({ reason }) => …); // connectionState for the full state machinePositional audio#
voice.updatePosition(channelId, { x, y, z }, { yaw, pitch, roll }); // 10–20 HzThe SDK renders remote participants with the Web Audio panner; voice.channelAudioPolicy(channelId) tells you what the server configured.
Devices, mute, levels#
await voice.setInputDevice(deviceId); await voice.setOutputDevice(deviceId);
voice.setInputGain(1.2); voice.setOutputVolume(0.8); voice.setOutputMuted(false);
voice.setTransmission('ptt'); voice.transmitToChannel(teamChannelId);
voice.setParticipantVolume(userId, 0.5); voice.setParticipantMuted(userId, true);Effects (setVoiceEffects), visemes (setVisemes), audio injection (injectAudio for bots and tests) and server-side ducking are available on the same client — see the typed API in the package.
Electron and kiosks#
Works unchanged; WebTransport needs Chromium ≥ 97 and a certificate the node advertises (handled automatically with the hash-pinned certificates Aurix nodes issue).
Channel patterns#
| Pattern | Channels per player | Notes |
|---|---|---|
| Co-op / squad | one team | mute = PTT off; nothing positional |
| Shooter with proximity | positional (shard) + team (squad) | transmit on the team channel by default, hold a key to speak locally |
| Open world | positional (shard), optional whisper for direct calls | roster_radius keeps the participant list small |
| Event / stage | audience-shaped channel, hosts with priority | thousands of listeners, a few speakers |
The transmit channel is a client-side choice: join several, send to one, hear all with receive. Positional audio needs positions roughly 10 times a second; between updates the node interpolates.
Latency and quality#
Expect 30–60 ms mouth-to-ear on the same continent over QUIC, more over TURN. Opus runs at 32 kbps with DTX by default; the spending ladder may lower it to 16 kbps when your forecast exceeds the limit. Server-side noise suppression is on by default and costs nothing on the client.
Testing without a full build#
- Two players: the rooms demo in a browser plus your build — they share a cell, so a token from your backend joins the same channel by name.
- Mic path: an
echochannel plays your own voice back through the real pipeline. - Network: throttle UDP on one machine and watch the transport fall back to WebRTC / WebSocket in the SDK's connection state.
Troubleshooting#
| Symptom | Check |
|---|---|
| connected, no audio | capture started? channel grant speak/receive? OS microphone permission? |
| audio but no spatialisation | positions sent on the positional channel id, not the team channel |
| random drops on mobile | the SDK pauses capture in the background by design; resume on foreground |
QUOTA_EXCEEDED on connect | plan concurrency cap or spending ladder step 4 — see Spending limit |