KUMODeck
日本語

Voice calls

For games (and any app where people talk live). A voice call lives in a realtime channel: create the channel with voice, and the players in it can talk to each other — a party, a squad, a match, a big gathering. Up to 100 people per call. Up to 8, the players connect to each other (peer to peer); from 9, the call goes through a KUMODeck relay server (SFU). The channel's size picks the mode for you, and you use the call the same way in both. It is off until you turn it on, and it needs realtime channels:

kumodeck features on realtimeChannels voice
kumodeck config push --env development

KUMODeck does not record calls. Voices are only delivered to the listeners; the connection set-up messages are passed on and dropped. KUMODeck keeps only who is in the call right now and when they joined.

Start a call#

const party = await kumo.realtime.create({ voice: true, maxMembers: 4, join: 'invited' });
await party.invite(playerId);                 // the people who may join (share party.name with them)

// Call join() from a button: the browser asks for the microphone, and audio can only start after a tap or click
await party.voice.join();                    // { mic: false } = listen only
party.voice.mute();                          // your own microphone
party.voice.unmute();
party.voice.on('speaking', ({ playerId, speaking }) => { … });   // light up who is talking
await party.voice.leave();                   // leaves the call, stays in the channel

A large call is written the same way:

const hall = await kumo.realtime.create({ voice: true, maxMembers: 60 });   // 9 or more = relay server
hall.voice.mode;                             // 'sfu'

Only a channel created with voice has a call. In any other channel ch.voice is null. The other players join the channel as usual (kumo.realtime.join(party.name)) and then ch.voice.join(). After a dropped connection the SDK joins the channel and the call again by itself.

Size and mode#

How the channel is createdMode (ch.voice.mode)
voice: true, maxMembers 8 or less (default 8)'p2p'the players connect to each other. Voices go through a relay, so nobody sees another player's IP address
voice: true, maxMembers 9 to 100'sfu'goes through the relay server automatically
voice: { mode: 'sfu' }'sfu'the relay server even for a small call (maxMembers defaults to 50). For channels where muting has to be certain (see below)
maxMembers above 100—voice_too_large

The mode is fixed when the channel is created and never changes during a call.

Rules in a relay-server ('sfu') call

  • You hear the 8 people who spoke most recently (not counting you). Nobody can follow more than a few voices at once, and this keeps what each player listens to (and what it costs) from growing with the square of the call's size. ch.voice.peers is who you hear right now (up to 8), and speaking arrives for them. ch.voice.members is everyone in the call.
  • Mute is enforced by the server. The relay server stops sending the voice of anyone muted by the owner or your server, or not on the speakers list. A modified client cannot hear them either.
  • Nobody sees another player's IP address (voices come from the relay server).

State, events and errors#

State
ch.voice.mode'p2p' or 'sfu' (read only)
ch.voice.joinedyou are in the call
ch.voice.mutedyour microphone is muted
ch.voice.canSpeakothers can hear you (false when the owner or your server muted you, or you are not on the speakers list)
ch.voice.memberswho is in the call: [{ id, canSpeak }]
ch.voice.peerswhose voice reaches you: { playerId, state, canSpeak, speaking, relayed } (in 'sfu', the top 8 you hear right now)
EventPayload
peera connection changed: { playerId, state, canSpeak, speaking, relayed }
speaking{ playerId, speaking }
membersthe whole list, whenever it changes
playerJoined / playerLeft{ id, canSpeak } / { playerId } (someone else)
self{ canSpeak } — you were muted or unmuted
closed{ reason } — see below
closed reasonWhenThe channel
leftyou left the callyou stay
kicked / bannedyou were removed from the channelyou leave it
time_limitthe call passed 4 hoursyou stay (and can join the call again)
suspendedKUMODeck staff stopped voice calls in this channel or game (to deal with abuse, for example). The call cannot be joined until they lift it; text messages keep workingyou stay
unavailablethe call cannot go on right now (your prepaid balance ran out, for example)you stay
connection_lostthe connection dropped and could not be restored—
ErrorWhen
webrtc_unavailablethe browser has no WebRTC
mic_deniedthe player refused the microphone (you can still offer { mic: false })
voice_disabledthis channel was not created with voice
voice_too_largemaxMembers above 100 (above 8 in a channel set to peer to peer)
voice_suspendedKUMODeck staff have stopped voice calls in this channel or game
voice_unavailablerelay-server calls are not available right now; try joining again a little later
voice_not_joineda call action before join()
feature_disabledfeatures.voice is off
player_requireda player without a player id (see below)
rate_limitedjoined the call again too many times (an hourly limit)

From your server (secret key)#

Your server can set up a channel with a call: PUT /v1/realtime/channels/@name with { "voice": true } (and your usual join, send, maxMembers, allow, speakers). The size decides the mode as above; { "voice": { "mode": "sfu" } } uses the relay server even for a small call, and { "voice": { "mode": "p2p" } } works only up to 8 people (voice_too_large from 9). To grow an existing peer-to-peer channel to 9 or more, send { "voice": { "mode": "sfu" } } together with maxMembers (maxMembers alone does not change the mode and fails with voice_too_large). GET shows voice (with its mode) and who is in the call. The voice setting cannot change while someone is in the call (409 voice_in_use).

In a peer-to-peer channel every voice goes through a relay by default, so players never see each other's IP address. Only your server can turn that off for a channel with { "voice": { "relayOnly": false } } (players who connect directly cost nothing to relay). Players under 13 are always relayed. The setting has no effect in a relay-server channel.

Mute, kick and ban#

The channel's rules apply to the call: the owner's (or your server's) mute, a send: 'invited' channel's speakers list, kick and ban.

  • Kick and ban cut the connection itself. The player leaves the channel and the call, and their access to the relay is revoked at once. Players you ban from your game cannot join any channel. A kicked player may join again if the channel lets them in; to keep someone out, ban them.
  • How mute works depends on the mode. In a relay-server ('sfu') call the server stops sending that voice. In a peer-to-peer ('p2p') call KUMODeck tells everyone that the muted player may not speak, and each listener's SDK stops playing that voice. The audio itself still reaches the others, so a modified client could play it. If muting has to be certain (calls with strangers, for example), create the channel with voice: { mode: 'sfu' } — or kick.
  • A display name proves nothing. Anyone can pick the same name as another player, or one like "Admin". Show who is the owner or staff from player ids (ownerId), never from the name.
  • What players say is your responsibility. Decide who may speak, and keep a way to act quickly: mute, kick, ban. When a call is abused, KUMODeck staff may also stop voice calls, and only voice calls, in a channel or a game (suspended).

Limits#

People per call100. Peer to peer: up to 8 (maxMembers defaults to 8 with voice: true, 50 with { mode: 'sfu' })
Who you hearpeer to peer: everyone in the call. Relay server: the 8 who spoke most recently, not counting you
One call4 hours, then closed with time_limit. Players stay in the channel and can join the call again
Who can joinplayers with a player id (the default — the SDK signs players in as guests). Not in open channels
Channelsonly @ channels created with voice (by a player or your server). Not channels without @

When your prepaid balance runs out, new calls cannot start and people in a call are taken out of it (closed with unavailable), like the other player-facing features.

Cost#

Charged as KUMODeck's usage fee (at cost), from your prepaid balance. What costs is the audio delivered to each listener. Silent players send almost nothing (the SDK sends no audio during silence), and peer-to-peer pairs that connect directly cost nothing.

Peer to peer (up to 8):

For 1 hourOne person talks at a timeEveryone talks all the time
2 peopleabout 0.13 centsabout 0.26 cents
4 peopleabout 0.4 centsabout 1.5 cents
8 peopleabout 0.9 centsabout 7.2 cents

Relay server (9 or more). Each player hears at most 8 others, so the cost grows roughly in line with the number of people:

For 1 hourAbout 2 people talking at onceEveryone talks all the time
16 peopleabout 4 centsabout 16 cents
50 peopleabout 13 centsabout 51 cents
100 peopleabout 26 centsabout $1