Ship a voice avatar that feels good, not just one that works. Symptom-indexed fixes, a latency budget, interruption handling, and reconnect patterns.
Getting an avatar on screen is the quickstart. Getting a voice experience that feels responsive and reliable is this page. Read the first section by symptom when something is wrong; read the rest before you launch.
The Avatar fills its container, so a parent with no size renders nothing.
flex: 1 alone — a flex
child can collapse to zero height).import "@avatarfactory/react/styles.css".<Avatar /> is inside <AvatarProvider>.See Styling for the full container recipe.
call mode the browser must grant microphone permission when the
session starts. If the prompt was dismissed, nothing is captured.getSessionToken function (a rejected token fails the connection silently to
the user).Echo happens when the avatar's output audio is picked up by the microphone.
That's the idle timeout, not a bug. A call session ends after roughly
2 minutes of no interaction. If you need the session to persist, keep it
active or reconnect on demand (below). See Limits & Quotas.
Network drops happen. Listen for the error/disconnect events via
useAvatarEvent and reconnect — see
Reconnect patterns.
Perceived responsiveness is the whole product. Decide your target up front — for a natural back-and-forth, aim for a sub-second gap between the user finishing speaking and the avatar starting to respond — then treat every feature as a tradeoff against that budget.
Measure each stage of the pipeline separately so you know where the time goes:
getSessionToken round-trip. Cache/keep-alive your
backend so this isn't a cold start every call; measure your own p50/p95.call mode, the gap from the user finishing
speaking to the avatar starting to respond is ~700–900ms (measured).Optimize the stage that's actually largest in your measurements. Don't guess — a slow token endpoint is the most common self-inflicted latency and the easiest to fix.
You can feel faster without being faster:
isListening / isSpeaking from
useAvatar so the user sees the avatar react
the instant they speak, even before audio returns.Real conversations interrupt. When the user starts speaking while the avatar is
talking, stop the avatar promptly — read isSpeaking and call stop() (or your
mode's interrupt path) so the user isn't talking over a monologue. Test the
"barge-in" case explicitly; it's the difference between a demo and a product.
Treat disconnects as expected, not exceptional — and in most apps, there is nothing to build.
A fatal error ends the session, not the avatar. stop() keeps the avatar
painted so restarting is cheap, and isIdle flips back to true, so your
existing start button reappears on its own and is the retry:
error.code), so the user knows the
button is worth pressing. See Error Handling.errorFallback covers the one case with nothing to show: the avatar never
loaded. Put a retry inside it if your own controls aren't visible there.fatal && retryable is the
wrong test for it: SESSION_IDLE_TIMEOUT would rebuild a session that idles
out again minutes later, and SERVER_CLOSE_REQUESTED would walk straight back
into the rate limit that closed you.MIC_PERMISSION_DENIED), tell the
user what to change — the start button can't fix it.