Skip to content

Steam Voice ​

Steam records and compresses the player's microphone for you. What it hands over is compressed audio: send it to the other players as it is, and decode it on the way out.

Steam handles the microphone permission and the player's own push-to-talk settings; the game does not have to.

Methods ​

startVoiceRecording() ​

Starts recording. It keeps recording until stopVoiceRecording().

stopVoiceRecording() ​

Stops. Steam usually still has a little audio buffered, so one more getVoice() after this is worth doing.

getAvailableVoice() ​

How much compressed voice is waiting: { result, compressedSize }. result is Steam's EVoiceResult — 0 there is data, 2 not recording, 3 nothing yet.

getVoice(maxSize?) ​

Reads up to maxSize bytes (8192 by default), or null when there is none.

javascript
const chunk = await steam.getVoice();
if (chunk) send(chunk.buffer);   // plain bytes

decompressVoice(data, sampleRate?) ​

Turns compressed voice back into PCM — 16-bit mono at sampleRate, which defaults to 11025.

Examples ​

Push to talk ​

javascript
let talking = false;

async function pressTalk() {
  if (talking) return;
  talking = await steam.startVoiceRecording();
}

async function releaseTalk() {
  if (!talking) return;
  await steam.stopVoiceRecording();
  await drainVoice();   // whatever is still buffered
  talking = false;
}

async function drainVoice() {
  let chunk;
  while ((chunk = await steam.getVoice())) {
    network.send({ type: 'voice', bytes: chunk.buffer });
  }
}

// while talking, read often — Steam's buffer is not large
setInterval(() => { if (talking) drainVoice(); }, 50);

Playing what arrives ​

javascript
const ctx = new AudioContext();

async function onVoicePacket(bytes) {
  const pcm = await steam.decompressVoice(bytes, 11025);
  if (!pcm) return;

  // 16-bit signed mono, little endian
  const view = new DataView(new Uint8Array(pcm.buffer).buffer);
  const samples = new Float32Array(pcm.written / 2);
  for (let i = 0; i < samples.length; i++) {
    samples[i] = view.getInt16(i * 2, true) / 32768;
  }

  const buffer = ctx.createBuffer(1, samples.length, 11025);
  buffer.getChannelData(0).set(samples);
  const source = ctx.createBufferSource();
  source.buffer = buffer;
  source.connect(ctx.destination);
  source.start();
}

Notes ​

  • Read while recording, not only at the end: Steam's capture buffer is small and overruns quietly.
  • The bytes are Steam's own codec. Do not try to play them directly and do not re-encode them — pass them through and decode at the other end.
  • Without Steam running, getVoice() answers null and recording never starts.