Talking Avatar

Talking Avatar Open Source · API reference

Open-source talking avatar API

Control a 3D avatar from JavaScript: attach live audio, play a clip, swap the character and release resources. This API runs in your browser and is MIT licensed, free for commercial and noncommercial use. It has no REST endpoint, account or API key. Talking Avatar Realistic Avatars, the separate hosted product, has its own clients (/server and /live) and paid usage; they aren't covered here.

new TalkingAvatar(options)

Import TalkingAvatar from @interviewflowai/talking-avatar. The constructor creates the renderer, starts loading your avatar and warms up the lip-sync model. Use the npm installation guide for package requirements and a complete starter example.

TalkingAvatar constructor options
OptionTypeBehaviour
containerHTMLElementRequired. The element the avatar fills; give it a width and height.
avatarUrlstringRequired. URL of a .glb avatar with Oculus visemes, or custom names mapped below.
morphTargetsRecord<string, string | string[]>Optional. Maps each standard morph name to your avatar's name or names.
modelUrlstringDefaults to DEFAULT_MODEL_URL, this package version's model on jsDelivr. Set a local URL to self-host it.
streamDelayMsnumberDefaults to 150 ms. Delays attached stream playback so the mouth can anticipate sound; reducing it trades lip-sync smoothness for latency.
onError(error: Error) => voidReports background loading and analysis errors. Defaults to console.error; also catch rejected method promises.

Playback, state and lifecycle methods

TalkingAvatar instance members
MemberReturnsBehaviour
readyPromise<void>Resolves when the first avatar is on screen. Rejects if avatar loading fails.
attachStream(stream)Promise<void>Plays and lip syncs a MediaStream with an audio track. Replaces the previously attached stream; rejects when no audio track exists.
detachStream()voidStops playback and lip sync for the attached stream. Does not stop the source MediaStreamTrack; its owner handles that.
speak(audio)Promise<void>Plays a URL string, encoded ArrayBuffer or Blob in a browser-supported audio format. Analyses the clip before playback; resolves when it ends or is stopped.
stopSpeaking()voidStops a clip started with speak(). Calling speak() again also replaces the current clip.
setAvatar(url, morphTargets?)Promise<void>Loads a different .glb avatar, with an optional morph mapping. Resolves when the new avatar is on screen.
setStatus(status)voidAccepts "listening" (default) or "thinking". Speech detected from audio takes priority.
dispose()voidReleases rendering and audio resources. The disposed instance cannot be reused.

Connect live audio or play a recording

Use attachStream() for a microphone or your voice agent's remote WebRTC audio. It accepts a MediaStream, rather than raw PCM chunks. Your agent integration supplies the connection and signalling. Use speak() for a recording or TTS response that the browser can decode, such as MP3 or WAV.

// avatar is your TalkingAvatar instance.
// pc is the RTCPeerConnection from your voice-agent integration.
function onTrack(event) {
  if (event.track.kind !== "audio") return;
  const stream = new MediaStream([event.track]);
  avatar.attachStream(stream).catch(console.error);
}
pc.addEventListener("track", onTrack);

// When leaving the conversation:
// pc.removeEventListener("track", onTrack);
// avatar.detachStream();
// avatar.dispose();

Start sound from a user interaction. For live streams, use HTTPS or localhost so AudioWorklet is available. The avatar plays the sound itself: avoid also playing that track through an audio element or another SDK. Your app owns the source tracks and connection; stop them separately when ending a call.

React API: TalkingAvatarView

Import TalkingAvatarView from @interviewflowai/talking-avatar/react. Supply avatarUrl and size its parent. The optional stream prop attaches live audio; setting it to nullor undefined detaches playback. Changing avatarUrl swaps the avatar.

Set status to listening or thinking, and use a ref to call instance methods such as speak(). The component also accepts morphTargets, onError, className and style.modelUrl and streamDelayMs configure each created instance; changing them recreates it. Cleanup runs on unmount. In Next.js, render it in a client component.

Errors and integration checks

  • Catch promises from ready, speak(), attachStream() and setAvatar(). Use onError for background failures too.
  • If the model or avatar fails to load, check the URL, HTTP response and CORS configuration. Missing viseme morph targets prevent mouth movement.
  • If there is no sound, check browser autoplay permissions and confirm the stream contains an audio track.
  • Detach live playback and call dispose() when removing a class instance. Release your connection and microphone tracks in your own call lifecycle.

Review the source and README on GitHub for implementation details. For the demo, licensing and what is included at $0, visit Free Talking Avatar.