> ## Documentation Index
> Fetch the complete documentation index at: https://orb-ui.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Gemini Live Voice UI for React

> Connect Gemini Live API native audio to an audio-reactive React voice UI with ephemeral tokens and orb-ui.

`createGeminiLiveAdapter` owns browser microphone streaming, PCM audio playback, input/output
metering, local turn detection, interruptions, and cleanup. Your app owns the official Google GenAI
client and the short-lived Live API token.

The examples use `gemini-3.1-flash-live-preview`, the current low-latency voice-first Live model,
with the `Kore` voice.

## The simple setup

The browser provides one `connect` callback that returns an official Google Live session. orb-ui
then owns microphone capture, PCM streaming and playback, local turn detection, state mapping,
audio metering, interruptions, and cleanup. The callback exists so `@google/genai` remains an
optional, app-owned dependency instead of increasing orb-ui's runtime bundle for every user.

## Install the Google GenAI SDK

```bash theme={null}
npm install orb-ui @google/genai
```

## Mint an ephemeral token on your server

Keep the standard Gemini API key on your server. Lock the one-use token to the model and audio
configuration your client will use.

```ts theme={null}
import { GoogleGenAI, Modality } from '@google/genai'

export async function POST() {
  const model = 'gemini-3.1-flash-live-preview'
  const config = {
    responseModalities: [Modality.AUDIO],
    speechConfig: {
      voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Kore' } },
    },
    systemInstruction: 'You are a concise, friendly voice assistant.',
    realtimeInputConfig: {
      automaticActivityDetection: { disabled: true },
    },
  }
  const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY })
  const token = await client.authTokens.create({
    config: {
      uses: 1,
      liveConnectConstraints: { model, config },
      httpOptions: { apiVersion: 'v1alpha' },
    },
  })

  return Response.json({ value: token.name, model, config })
}
```

Add authentication and rate limiting appropriate for your app before exposing a token endpoint.

## Create the adapter

```tsx theme={null}
import { GoogleGenAI } from '@google/genai'
import { Orb } from 'orb-ui'
import { createGeminiLiveAdapter } from 'orb-ui/adapters'

const adapter = createGeminiLiveAdapter({
  connect: async (callbacks) => {
    const response = await fetch('/api/gemini-live-token', { method: 'POST' })
    const token = await response.json()
    if (!response.ok) throw new Error(token.error ?? 'Could not create a Gemini Live session')

    const client = new GoogleGenAI({
      apiKey: token.value,
      httpOptions: { apiVersion: 'v1alpha' },
    })
    return client.live.connect({
      model: token.model,
      config: token.config,
      callbacks,
    })
  },
})

export function GeminiLiveVoiceUI() {
  return <Orb adapter={adapter} theme="circle" aria-label="Start Gemini voice assistant" />
}
```

The server configuration disables automatic VAD because the adapter uses client-side activity
detection by default. It sends explicit `activityStart` and `activityEnd` markers from its local
speech detector, which is the tested, deterministic voice-turn path. If your Gemini session uses
server-side VAD instead, keep automatic activity detection enabled and set
`activityDetection: 'server'` in the adapter.

## Audio behavior

The adapter resamples browser microphone input to 16 kHz PCM and sends it with
`sendRealtimeInput`. Gemini's base64 PCM output is decoded, queued, and played at the sample rate
declared by each response chunk. An interruption stops all queued audio immediately.

## State and volume mapping

* token/session connection -> `connecting`
* active microphone/user speech -> `listening`
* end of detected user speech or model text work -> `thinking`
* queued native audio -> `speaking`
* interruption, `waitingForInput`, or completed playback -> `listening`
* Live session failure -> `error`
* explicit stop or closed connection -> `idle`

`inputVolume` follows the local microphone. `outputVolume` follows the PCM playback analyser.

## Output calibration

Output shaping is optional. Use `outputVolumeCalibration` only when you need provider-specific gain
or smoothing, and use a getter when values must change during an active session:

```tsx theme={null}
const adapter = createGeminiLiveAdapter({
  connect,
  outputVolumeCalibration: () => currentCalibration,
  onOutputVolumeSample: ({ raw, shaped, normalized }) => {
    console.debug({ raw, shaped, normalized })
  },
})
```

The provider playground exposes the same noise-floor, gain, curve, attack, and release controls for
calibrating a real conversation before choosing application defaults.

## Token handling

Gemini recommends ephemeral tokens for direct browser-to-Live connections. Do not ship a standard
Gemini API key in the browser. The token and connection must both use the `v1alpha` API while
ephemeral Live authentication remains on that endpoint.

## Related

* [Custom integrations](/docs/adapters/custom)
* [React voice agent UI lifecycle guide](/docs/guides/voice-agent-ui)
* [Gemini Live ephemeral tokens](https://ai.google.dev/gemini-api/docs/live-api/ephemeral-tokens)
