Conversational voice AI has a hard constraint that separates usable products from abandoned ones: latency. This article covers the architectural fit of real-time-optimized mini models in voice AI stacks, a working real-time voice implementation using the OpenAI Real-time API, and a practical latency comparison against competing options.
Table of Contents
- The Latency Problem in Voice AI
- What Makes Real-Time-Optimized Mini Models Different
- Latency Comparison: Real-Time Mini Models Across Providers
- Architecture Overview: How the Voice AI Pipeline Works
- Building a Real-Time Voice Assistant
- Real-World Use Cases
- Limitations and When to Use a Larger Model
- Choosing the Right Model for Voice AI
- Appendix: TTFT Verification Script
The Latency Problem in Voice AI
Conversational voice AI has a hard constraint that separates usable products from abandoned ones: latency. Any perceivable delay beyond roughly 300 milliseconds can disrupt the natural rhythm of dialogue, eroding user trust and making voice assistants feel robotic rather than responsive. For years, developers building voice applications faced an unpleasant tradeoff. Smarter models meant slower responses, forcing a choice between conversational intelligence and real-time speed. OpenAI's smaller, faster model variants represent a deliberate attempt to collapse that tradeoff, delivering models explicitly tuned for the speed demands of production voice pipelines without significantly degrading reasoning on routine tasks. This article covers the architectural fit of these models in voice AI stacks, a working real-time voice implementation using the OpenAI Real-time API, and a practical latency comparison against competing options.
Any perceivable delay beyond roughly 300 milliseconds can disrupt the natural rhythm of dialogue, eroding user trust and making voice assistants feel robotic rather than responsive.
Note on model selection: This article uses gpt-4o-realtime-preview as the target model for the Real-time API. OpenAI's model catalog evolves frequently; verify the latest available real-time models at platform.openai.com/docs/models before starting. If a newer real-time-capable mini model is available at the time you read this, substitute accordingly and re-validate the performance characteristics described here against your own measurements.
What Makes Real-Time-Optimized Mini Models Different
Speed and Latency Characteristics
Smaller, throughput-optimized models like gpt-4o-mini achieve meaningfully faster output generation than their full-sized counterparts. In voice applications, higher tokens-per-second output cuts time-to-first-audio-chunk directly. Faster token generation means the speech synthesis layer receives usable text (or, in speech-to-speech mode, audio deltas) sooner, shaving hundreds of milliseconds off perceived response time. At high throughput rates, these models can produce enough output within the first 100 to 150 milliseconds to begin streaming audio back to the user before the model finishes its full response.
Important: Exact throughput and TTFT figures depend on prompt length, output complexity, API tier, server load, and geographic region. Always measure your own pipeline under realistic conditions rather than relying on any single benchmark. Independent benchmarking sources such as ArtificialAnalysis.ai provide regularly updated, reproducible comparisons across providers.
Sub-200ms TTFT: What That Means for Real-Time Conversations
End-to-end latency in a voice AI pipeline encompasses the full chain: audio capture from the user's microphone, speech-to-text transcription (if applicable), model inference, and text-to-speech or audio generation streamed back to the user's speaker. When model inference TTFT drops below 200ms, total pipeline latency (including network round-trip, audio encoding, and playback buffering) can fall under 500ms under low-latency, same-region network conditions. Results will vary significantly with geographic distance, network quality, and audio encoding overhead. A commonly cited industry guideline targets sub-500ms end-to-end latency for acceptable conversational feel, though perceptual thresholds vary by context and user expectation. Research on conversational turn-taking (e.g., Levinson & Torreira, 2015) shows the picture is more nuanced than a single number suggests.
Quality vs Speed: What You Trade Off
Real-time-optimized mini models occupy a lower capability tier than full-sized models, optimized specifically for constrained, fast-response use cases. They excel at short-turn dialogue, function calling, and structured responses. These are precisely the interaction patterns that dominate voice applications. For complex multi-step reasoning, long-context analysis, or tasks requiring deep inference chains, route to larger models such as gpt-4o or the latest full-sized offering. The tradeoff is deliberate and well-defined: these models sacrifice peak reasoning depth for the throughput and latency characteristics that voice pipelines demand.
Latency Comparison: Real-Time Mini Models Across Providers
Benchmark Methodology
Comparing models across providers is inherently difficult. The figures below should be treated as approximate, directional indicators rather than precise engineering specifications. Throughput, TTFT, and end-to-end latency all vary with prompt content, token count, time of day, API tier, and geographic region. Where possible, rely on reproducible third-party benchmarks (such as ArtificialAnalysis.ai) and your own measurements under production-representative conditions.
The Comparison Table
| Metric | GPT-4o Mini (Real-time API) | Claude 3.5 Haiku | Gemini 2.0 Flash |
|---|---|---|---|
| Context Window | 128K | 200K | 1M |
| Voice/Audio Native Support | Yes (Real-time API) | No native real-time audio pipeline | Yes (Live API) |
| Relative Strengths | Integrated speech-to-speech pipeline, low TTFT | Strong text reasoning at low cost | Large context window, competitive cost |
Why no specific TTFT or tokens/sec numbers? Published benchmarks become stale quickly and vary with test methodology. Rather than present figures that cannot be independently verified here, we recommend measuring with your own prompts using the verification script in the appendix, or consulting ArtificialAnalysis.ai for current, reproducible numbers across all three providers.
OpenAI's Real-time API provides a true speech-to-speech pipeline, which is its primary architectural advantage for voice applications. Gemini 2.0 Flash competes effectively on cost and offers a 1M token context window, making it appealing for retrieval-heavy voice applications. Claude 3.5 Haiku, while a capable text model, lacks a native real-time audio pipeline equivalent, requiring developers to bolt on separate transcription and synthesis services, which adds latency and architectural complexity.
Architecture Overview: How the Voice AI Pipeline Works
Speech-to-Speech vs Speech-to-Text-to-Speech
Two fundamental architectures exist for voice AI. The traditional approach is speech-to-text-to-speech (STT-TTS): a transcription service converts user audio to text, then an LLM processes that text, and a separate TTS engine converts the LLM's text output to audio. This works but introduces latency at each conversion boundary. The alternative is speech-to-speech, where the model accepts audio input and produces audio output natively. OpenAI's Real-time API supports this second approach, eliminating the separate transcription service and its associated network hop, reducing overall pipeline latency. The model still processes audio through internal speech understanding mechanisms, but the removal of a discrete external ASR service and its network round-trip is what yields the latency improvement. Speech-to-text-to-speech remains appropriate when developers need full text transcripts for logging, compliance, or downstream processing. For pure conversational speed, speech-to-speech is the faster path.
Where the Mini Model Fits in the Stack
The pipeline is straightforward: the user's microphone captures audio, which streams to the OpenAI Real-time API. The model processes the audio input, generates a response, and streams audio chunks back to the user's speaker. The model handles both understanding and generation natively within this pipeline, eliminating the need for separate ASR or TTS services.
Building a Real-Time Voice Assistant
Prerequisites and Setup
Building a working voice assistant requires:
- OpenAI API key with Real-time API access. Note: Real-time API access may require a specific usage tier or separate enablement. Verify your API key has Real-time API permissions at platform.openai.com before proceeding.
- Node.js 20 or later.
- The latest version of the
openaiSDK, plusws,mic, andspeakerpackages. - A development environment with microphone access.
- ESM module support: The code below uses
importsyntax. Either set"type": "module"in yourpackage.jsonor use.mjsfile extensions.
Platform-specific native audio prerequisites:
The speaker and mic packages are native modules requiring build tools and OS-level audio libraries:
- Linux:
sudo apt-get install libasound2-devand ensurearecordis available (typically part ofalsa-utils). - macOS:
brew install sox(themicpackage usessoxas its audio backend on macOS). - Windows: Native audio support for these packages is limited and may require additional configuration. See each package's README for details.
- All platforms:
node-gypand its prerequisites (Python 3, a C++ compiler) must be available for native module compilation.
{
"name": "realtime-voice-assistant",
"type": "module",
"dependencies": {
"openai": "4.63.0",
"ws": "8.18.0",
"mic": "2.1.2",
"speaker": "0.5.5"
}
}
npm install
import OpenAI from "openai";
import WebSocket from "ws";
import mic from "mic";
import Speaker from "speaker";
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Replace with the current real-time–capable model; verify at platform.openai.com/docs
const MODEL = "gpt-4o-realtime-preview";
const VOICE = "alloy";
const SAMPLE_RATE = 24000;
const CHANNELS = 1;
const BIT_DEPTH = 16;
⚠️ Security note: Store your API key using a .env file and a package like dotenv, or your platform's secret management system. Do not hard-code API keys in source files.
Establishing a Real-Time WebSocket Session
The Real-time API uses a persistent WebSocket connection. The client opens a session, configures the model, selects a voice, sets turn detection parameters, and provides system instructions that shape the assistant's persona. Server-side voice activity detection (VAD) handles turn boundaries, determining when the user has finished speaking and the model should respond. The model must be specified as a query parameter in the WebSocket URL (?model=...) in addition to the session.update event.
async function createRealtimeSession() {
const url = `wss://api.openai.com/v1/realtime?model=${MODEL}`;
const ws = new WebSocket(url, {
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"OpenAI-Beta": "realtime=v1",
},
});
// Connection timeout guard
const connectionTimeout = setTimeout(() => {
console.error("WebSocket connection timeout");
ws.terminate();
}, 10_000);
ws.on("open", () => clearTimeout(connectionTimeout));
ws.on("error", (err) => {
clearTimeout(connectionTimeout);
console.error("WebSocket error:", err);
});
return ws;
}
Streaming Audio In and Out
Audio capture sends raw PCM 16-bit, 24kHz mono audio to the Real-time API as input_audio_buffer.append events. The API streams back response.audio.delta events containing audio chunks that pipe directly to the speaker. Align your audio formats exactly: mismatched sample rates or bit depths produce garbled output or silent playback. Verify your microphone's actual output format matches the configuration (on Linux, arecord --dump-hw-params can help). The event loop also handles function calls, allowing the voice assistant to invoke tools mid-conversation.
⚠️ Cost warning: The microphone stream sends audio continuously while running. In production, implement silence detection, session timeouts, and cost caps to avoid unexpected API charges from an always-open audio stream.
The tradeoff is deliberate and well-defined: these models sacrifice peak reasoning depth for the throughput and latency characteristics that voice pipelines demand.
async function startVoiceAssistant() {
const ws = await createRealtimeSession();
const speaker = new Speaker({
channels: CHANNELS,
bitDepth: BIT_DEPTH,
sampleRate: SAMPLE_RATE,
});
const microphone = mic({
rate: String(SAMPLE_RATE),
channels: String(CHANNELS),
bitwidth: String(BIT_DEPTH),
encoding: "signed-integer",
});
const micStream = microphone.getAudioStream();
// Single consolidated open handler for session config and microphone start
ws.on("open", () => {
const sessionConfig = {
type: "session.update",
session: {
model: MODEL,
voice: VOICE,
instructions: "You are a concise, helpful voice assistant. Keep responses under two sentences unless asked for detail.",
input_audio_format: "pcm16",
output_audio_format: "pcm16",
max_response_output_tokens: 150,
turn_detection: {
type: "server_vad",
threshold: 0.5,
prefix_padding_ms: 300,
silence_duration_ms: 500,
},
tools: [{
type: "function",
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
}],
},
};
ws.send(JSON.stringify(sessionConfig));
console.log(`Session configured with ${MODEL}`);
micStream.on("data", (chunk) => {
if (ws.readyState === WebSocket.OPEN) {
const base64Audio = chunk.toString("base64");
ws.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: base64Audio,
}));
}
});
microphone.start();
console.log("Listening... Speak into your microphone.");
});
ws.on("message", (data) => {
let event;
try {
event = JSON.parse(data.toString());
} catch (err) {
console.error("Failed to parse WebSocket message:", err.message);
return;
}
if (event.type === "response.audio.delta") {
const audioBuffer = Buffer.from(event.delta, "base64");
speaker.write(audioBuffer);
}
if (event.type === "response.function_call_arguments.done") {
let args;
try {
args = JSON.parse(event.arguments);
} catch (err) {
console.error("Failed to parse function call arguments:", err.message);
return;
}
if (event.name === "get_weather") {
// TODO: Replace with a real weather API call. This is a mock response for demonstration only.
const result = { temperature: "72°F", condition: "Sunny" };
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" }));
}
}
});
ws.on("error", (err) => {
console.error("WebSocket error:", err);
microphone.stop();
speaker.end();
});
// Graceful shutdown: wait for ws.close() before exiting
process.on("SIGINT", () => {
console.log("
Shutting down...");
microphone.stop();
speaker.end();
ws.close();
ws.on("close", () => process.exit(0));
// Force exit if close hangs
setTimeout(() => process.exit(1), 3_000).unref();
});
}
startVoiceAssistant();
Note on function call fields: The Real-time API's required fields for conversation.item.create with function_call_output may evolve. Consult the current Real-time API reference to confirm required fields such as call_id and output.
Optimizing for Minimum Latency
Several configuration choices directly affect perceived latency. Keep system prompts short (under 200 tokens) to minimize prefill time. Prefer server-side VAD over client-side detection because it reduces turn-handling round-trips, though the magnitude of that reduction depends on your network conditions. Set max_response_output_tokens to a reasonable cap to prevent runaway generation that delays subsequent turns: 150 tokens supports approximately 20 to 30 seconds of speech, so adjust based on your expected response length distribution. Deploy in the same geographic region as OpenAI's API endpoints to cut network round-trip time. A lower temperature (0.6 to 0.8 is a reasonable starting point for voice) produces shorter, more focused responses, though you should tune this per model and use case.
The optimized session configuration is already integrated into the createRealtimeSession() function above, including max_response_output_tokens: 150. Adjust the values there directly for your use case.
Real-World Use Cases
Customer Service Voice Bots
Low pipeline latency transforms interactive voice response (IVR) systems and support bots from frustrating hold-and-wait experiences into conversations with sub-500ms turn gaps. Function calling enables order lookups, account modifications, and status checks during live calls without noticeable pauses.
Voice-Controlled Developer Tools
When a developer asks a quick question mid-keystroke, they expect an immediate, focused answer. IDE copilots, terminal assistants, and hands-free coding workflows all depend on short-turn, high-frequency interactions, and sub-200ms-TTFT models match these patterns precisely.
Accessibility and Language Learning Applications
Real-time pronunciation feedback and conversational tutoring require latency low enough to maintain the natural cadence of dialogue practice. Any noticeable delay breaks conversational cadence, pulling the learner out of the flow that makes interactive exercises effective.
IoT and Edge-Adjacent Voice Interfaces
If a smart home device takes a full second to acknowledge a voice command, users stop trusting it. Wearables and automotive infotainment systems face the same constraint. Sub-500ms end-to-end latency meets that expectation where previous-generation models often could not.
Limitations and When to Use a Larger Model
Real-time-optimized mini models are not universal solutions. Escalate complex multi-step reasoning tasks to larger models such as gpt-4o or the latest full-sized offering, which maintain higher accuracy on challenging inference chains. Long-form content generation falls outside the mini model's design envelope. If your application requires very deep context windows, consider Gemini 2.0 Flash's 1M token context, particularly for retrieval-heavy voice assistants that reference large document sets. Cost at scale also warrants evaluation: Gemini Flash's lower per-token pricing may prove more economical for high-volume, cost-sensitive deployments. Always check current pricing at each provider's pricing page, as rates change frequently.
Choosing the Right Model for Voice AI
OpenAI's Real-time API, paired with a fast mini model, offers low TTFT, native speech-to-speech support, and an integrated audio pipeline. Whether it is the best choice depends on your latency requirements, cost constraints, context window needs, and the complexity of tasks your assistant must handle. Developers can start prototyping with the code examples above using OpenAI's Real-time API documentation as a reference. Measure your own pipeline latency under realistic conditions with the verification script below, compare across providers, and if your use case fits the short-turn, low-latency profile described here, start with the Real-time API and only add complexity when you hit its limits.
Start with the Real-time API and only add complexity when you hit its limits.
Appendix: TTFT Verification Script
Use this script to measure actual time-to-first-token for comparison against any claimed benchmarks:
import OpenAI from "openai";
const client = new OpenAI();
async function measureTTFT(model, runs = 10) {
const results = [];
for (let i = 0; i < runs; i++) {
const start = Date.now();
const stream = await client.chat.completions.create({
model: model,
messages: [{ role: "user", content: "Say hello briefly." }],
stream: true,
});
try {
for await (const chunk of stream) {
if (chunk.choices[0]?.delta?.content) {
results.push(Date.now() - start);
break;
}
}
} finally {
// Cancel the stream to release the underlying connection
stream.controller.abort();
}
}
results.sort((a, b) => a - b);
// Correct median for both odd and even length arrays
const mid = Math.floor(results.length / 2);
const median =
results.length % 2 === 0
? (results[mid - 1] + results[mid]) / 2
: results[mid];
const mean = results.reduce((a, b) => a + b, 0) / results.length;
console.log(`Model: ${model}`);
console.log(`Runs: ${runs}, Median TTFT: ${median}ms, Mean TTFT: ${mean.toFixed(0)}ms`);
console.log(`Min: ${results[0]}ms, Max: ${results[results.length - 1]}ms`);
}
// Replace with whatever models you want to compare
measureTTFT("gpt-4o-mini");




