Basic Voice Chat Component

Here’s a complete example of a React component that creates a voice conversation using the useConversation hook:

import React, { useState } from "react";
import { type SessionConfig } from "@outspeed/client";
import { useConversation } from "@outspeed/react";

const getEphemeralKeyFromServer = async (config: SessionConfig) => {
  const tokenResponse = await fetch("/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(config),
  });

  const data = await tokenResponse.json();
  if (!tokenResponse.ok) {
    throw new Error("Failed to get ephemeral key");
  }

  return data.client_secret.value;
};

const sessionConfig: SessionConfig = {
  model: "outspeed-v1",
  instructions: "You are a helpful but witty assistant named Alfred.",
  voice: "david", // see the voices page for all available voices
  turn_detection: {
    type: "semantic_vad",
  },
  first_message: "Hello, how can I assist you with Outspeed today?",
};

export default function VoiceChat() {
  const [sessionCreated, setSessionCreated] = useState(false);
  const [isConnecting, setIsConnecting] = useState(false);

  const conversation = useConversation({
    sessionConfig: sessionConfig,
  });

  const startSession = async () => {
    try {
      setIsConnecting(true);
      const ephemeralKey = await getEphemeralKeyFromServer(sessionConfig);
      await conversation.startSession(ephemeralKey);

      // Listen for session creation event
      conversation.on("session.created", (event) => {
        console.log("Session created:", event);
        setSessionCreated(true);
        setIsConnecting(false);
      });
    } catch (error) {
      console.error("Error starting session:", error);
      setIsConnecting(false);
    }
  };

  const endSession = async () => {
    try {
      await conversation.endSession();
      setSessionCreated(false);
    } catch (error) {
      console.error("Error ending session:", error);
    }
  };

  if (isConnecting) {
    return (
      <div className="voice-chat">
        <h2>Connecting...</h2>
        <p>Please wait while we establish the connection.</p>
      </div>
    );
  }

  if (sessionCreated) {
    return (
      <div className="voice-chat">
        <h2>🎙️ Voice Chat Active</h2>
        <p>You can now speak with the AI assistant!</p>
        <button onClick={endSession} className="end-button">
          End Session
        </button>
      </div>
    );
  }

  return (
    <div className="voice-chat">
      <h2>Voice AI Assistant</h2>
      <p>Click the button below to start a voice conversation.</p>
      <button onClick={startSession} className="start-button">
        Start Voice Chat
      </button>
    </div>
  );
}
Want to use a different voice? See all available voices you can choose from.

Advanced Features

Event Handling

You can listen to various events during the conversation:

// Listen for speech detection
conversation.on("input_audio_buffer.speech_started", () => {
  console.log("User started speaking");
});

conversation.on("input_audio_buffer.speech_stopped", () => {
  console.log("User stopped speaking");
});

// Listen for AI responses
conversation.on("response.text.delta", (event) => {
  console.log("AI response text:", event.delta);
});

conversation.on("response.audio_transcript.delta", (event) => {
  console.log("AI speech transcript:", event.delta);
});

Text Input

You can also send text messages programmatically:

const sendTextMessage = () => {
  conversation.sendText("Tell me about the weather today");
};

return (
  <div>
    {/* ... other components */}
    <button onClick={sendTextMessage}>Send Text Message</button>
  </div>
);

Mute Control

Control the microphone state by passing micMuted prop to useConversation:

const [isMicMuted, setIsMicMuted] = useState(false);

const conversation = useConversation({
  micMuted: isMicMuted,
});

const toggleMute = () => {
  setIsMicMuted(!isMicMuted);
};

return (
  <div>
    {/* ... other components */}
    <button onClick={toggleMute}>{isMicMuted ? "🔇 Unmute" : "🎤 Mute"}</button>
  </div>
);

Volume Control

Control the AI’s voice volume by passing volume prop to useConversation (value between 0 and 1):

const [volume, setVolume] = useState(0.8);

const conversation = useConversation({
  volume: volume,
});

return (
  <div>
    {/* ... other components */}
    <label>
      Volume: {Math.round(volume * 100)}%
      <input
        type="range"
        min="0"
        max="1"
        step="0.1"
        value={volume}
        onChange={(e) => setVolume(parseFloat(e.target.value))}
      />
    </label>
  </div>
);

Error Handling

Always implement proper error handling for a robust user experience:

const [error, setError] = useState<string | null>(null);

const conversation = useConversation({
  sessionConfig: sessionConfig,
  onError: (err) => {
    console.error("Conversation error:", err);
    setError(err.message);
  },
});

// Display error to user
if (error) {
  return (
    <div className="error-state">
      <h2>❌ Error</h2>
      <p>{error}</p>
      <button onClick={() => setError(null)}>Try Again</button>
    </div>
  );
}

Next Steps