Index of every page on this site: llms.txt
All skills
elevenlabs avatar

/realtime

@238659a official
by elevenlabselevenlabs/skills503 stars
85
Run it yourself

Use ElevenLabs Realtime models (`eleven_realtime_v1_mini`, `eleven_realtime_v1_max`), the low-latency models built for voice conversation. Use when the user wants to create an ElevenAgents agent on a Realtime model or switch an existing agent's Voice mode to Realtime, set `conversation_config.realtime_model`, choose between Realtime Mini and Max, or debug a Realtime agent that won't publish or transfer. Also use when migrating a voice app built on OpenAI's Realtime API and SDK (`@openai/agents-realtime`, `gpt-realtime`) to the OpenAI-compatible ElevenLabs Realtime API, including the `client_secrets` mint, the WebSocket transport, and audio after leaving WebRTC (closes with 4001/4003, no audio). Not for choosing an agent's LLM or TTS model in Custom mode, Scribe realtime speech-to-text, or migrating Chat Completions or Responses API text calls.

Use this Skill: https://skilld.dev/gh/elevenlabs/skills/realtime

Nothing lands on disk. Nothing to clean up.

Fork this Skill

Edit a local copy. It keeps the author and licence.

referencesbrowser-audio.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Browser audio over WebSocket

The OpenAI Realtime SDK wires microphone and speaker automatically only for its WebRTC transport. ElevenLabs Realtime is WebSocket-only, so the app captures and plays audio itself. Both directions use PCM16, 24 kHz, mono.

This reference uses wavtools, the helper OpenAI uses in its own WebSocket examples. Any capture path that produces 24 kHz PCM16 ArrayBuffer chunks works, for example an AudioWorklet the app already has.

npm install @openai/agents-realtime wavtools

Implementation

Framework-agnostic; in React, hold the returned handle in a useRef and call stop() on unmount.

import {
  OpenAIRealtimeWebSocket,
  RealtimeAgent,
  RealtimeSession,
} from "@openai/agents-realtime";
import { WavRecorder, WavStreamPlayer } from "wavtools";

const SAMPLE_RATE = 24000;

export async function startVoiceSession() {
  const tokenResponse = await fetch("/api/token");
  if (!tokenResponse.ok)
    throw new Error(`Token request failed (${tokenResponse.status})`);
  const { value: clientSecret } = await tokenResponse.json();

  const recorder = new WavRecorder({ sampleRate: SAMPLE_RATE });
  const player = new WavStreamPlayer({ sampleRate: SAMPLE_RATE });
  await recorder.begin();
  await player.connect();

  const session = new RealtimeSession(
    new RealtimeAgent({
      name: "Assistant",
      instructions:
        "You are a helpful, friendly voice assistant. Keep replies concise.",
    }),
    {
      transport: new OpenAIRealtimeWebSocket({
        url: "wss://api.elevenlabs.io/v1/convai/realtime",
      }),
      model: "eleven_realtime_v1_mini",
      config: {
        outputModalities: ["audio"],
        audio: { output: { voice: "brian" } },
      },
    },
  );

  session.on("audio", (event) => {
    player.add16BitPCM(event.data, event.responseId);
  });
  session.on("audio_interrupted", () => {
    player.interrupt();
  });
  session.on("error", (event) => {
    console.error(event.error);
  });

  try {
    await session.connect({ apiKey: clientSecret });
    await recorder.record((data) => {
      session.sendAudio(data.mono as unknown as ArrayBuffer);
    });
  } catch (err) {
    await stop();
    throw err;
  }

  async function stop() {
    await recorder.quit().catch(() => {});
    player.interrupt();
    session.close();
  }

  return { session, stop };
}

Microphone audio streams continuously; server-side VAD decides when the user's turn ends, so there's no push-to-talk logic to port unless the app had one.

Pitfalls

  • data.mono is mistyped. wavtools types the record() callback's mono as Int16Array, but at runtime it's already the PCM16 ArrayBuffer. Pass it straight to sendAudio. Reading .buffer off it, or wrapping it in a new typed array, sends garbage.
  • Start from a user gesture. recorder.begin() prompts for the microphone and player.connect() creates an AudioContext. Browsers block audio contexts created outside a click or keypress, so call startVoiceSession() from the handler that used to start the WebRTC session.
  • Stop playback on interruption. audio_interrupted fires when the user barges in. player.interrupt() discards audio already queued; without it, the assistant keeps talking over the user.
  • Match the sample rate everywhere. Recorder, player, and the mint body's audio.input.format.rate all use 24000. A mismatch plays back too fast, too slow, or as noise.
  • Tear down all three. Quit the recorder (releases the microphone indicator), interrupt the player, and close the session. A leftover recorder keeps the microphone open after the conversation ends.

Replacing WebRTC-specific code

WebRTC-era code WebSocket replacement
new OpenAIRealtimeWebRTC({ audioElement, mediaStream }) new OpenAIRealtimeWebSocket({ url }) plus the recorder and player above
<audio autoplay> element fed by the peer connection WavStreamPlayer; remove the element
getUserMedia stream passed as mediaStream WavRecorder.begin() (pass a deviceId to pick an input)
Peer-connection event handlers (changePeerConnection, ICE state) Remove; watch session.on("error") and transport events instead

Source: SKILL.md on GitHub

skilld matched fixed text patterns in SKILL.md and file names. Patterns miss obfuscated code.

skilld run checks every file with the same patterns. It asks for approval before it loads a Skill with a behavior marked Needs approval.

1 warning3d3 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    This skill provides safe technical instructions and code examples for developers working with ElevenLabs Realtime models and migrating from OpenAI Realtime API. It emphasizes secure API key management and utilizes official vendor tools and libraries.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: MEDIUM · 2 issues

Signed by skilld at 238659a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 13 hours ago.

Activeupdated 4 days ago

README badge

README badge for elevenlabs/skills/realtime