<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: FaceMode</title>
    <description>The latest articles on DEV Community by FaceMode (facemode).</description>
    <link>https://dev.to/facemode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F15206%2Fcc455e31-acc5-47f5-b9fc-538271fb836e.png</url>
      <title>DEV Community: FaceMode</title>
      <link>https://dev.to/facemode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/facemode"/>
    <language>en</language>
    <item>
      <title>Synthesia Interactive Avatar vs FaceMode</title>
      <dc:creator>prajwaldkadam</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/facemode/synthesia-interactive-avatar-vs-facemode-33g2</link>
      <guid>https://dev.to/facemode/synthesia-interactive-avatar-vs-facemode-33g2</guid>
      <description>&lt;p&gt;Synthesia Interactive Avatar and FaceMode both turn voice-agent speech into live, lip-synced video. If your agent already works, the useful comparison is which avatar API fits its transport, runtime, avatar requirements, and deployment constraints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.synthesia.io/reference/interactive-avatars" rel="noopener noreferrer"&gt;Synthesia's Interactive Avatar API&lt;/a&gt; supports a bring-your-own conversation stack connected to LiveKit. &lt;a href="https://facemode.io" rel="noopener noreferrer"&gt;FaceMode&lt;/a&gt; accepts your existing audio and publishes avatar media into a LiveKit or Daily room you own. Neither requires replacing your LLM simply to add a face.&lt;/p&gt;

&lt;p&gt;This is a documentation-based comparison by FaceMode, not an independent performance benchmark. Sources were checked on October 2, 2026. Prices, access rules, and plugin support can change; conflicting vendor documentation is called out rather than resolved by guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synthesia Interactive Avatar vs FaceMode: the short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Synthesia Interactive Avatar&lt;/th&gt;
&lt;th&gt;FaceMode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Conversation stack&lt;/td&gt;
&lt;td&gt;Bring your own STT, LLM, and TTS; optional hosted TTS (&lt;a href="https://docs.synthesia.io/reference/interactive-avatars" rel="noopener noreferrer"&gt;docs&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Keep your voice stack; supply its audio (&lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;product summary&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media transport&lt;/td&gt;
&lt;td&gt;LiveKit-only in current operational docs (&lt;a href="https://docs.synthesia.io/reference/ia-operational-trust" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Customer-owned LiveKit or Daily room (&lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration paths&lt;/td&gt;
&lt;td&gt;Documented Python LiveKit plugin and direct REST endpoints; Pipecat marked coming soon (&lt;a href="https://www.synthesia.io/features/avatars/interactive-avatars" rel="noopener noreferrer"&gt;product page&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;LiveKit Python/TypeScript plugins, Python Pipecat plugin, direct REST + WebSocket (&lt;a href="https://docs.facemode.io/integrations/overview" rel="noopener noreferrer"&gt;integrations&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avatars&lt;/td&gt;
&lt;td&gt;Synthetic and personal avatars; operational docs exclude stock actor avatars (&lt;a href="https://docs.synthesia.io/reference/ia-operational-trust" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Front-facing portrait uploads and five library styles (&lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;product summary&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparable latency figure&lt;/td&gt;
&lt;td&gt;No numeric audio-to-rendered-frame figure in the sources reviewed&lt;/td&gt;
&lt;td&gt;Approximately 960ms audio-to-rendered-frame target, not a measured result from this comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing and trial&lt;/td&gt;
&lt;td&gt;Product page: $0.12/min and 500 free minutes; operational docs: $0.10/min (&lt;a href="https://www.synthesia.io/features/avatars/interactive-avatars" rel="noopener noreferrer"&gt;pricing sources&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Private beta: 3,600 credits / 60 minutes; paid top-ups not open yet (&lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;beta terms&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shared capabilities matter as much as the differences. Bring-your-own models and room ownership are not FaceMode-exclusive features. A useful evaluation starts with those similarities, then tests the details that actually change your implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: both can keep your voice stack
&lt;/h2&gt;

&lt;p&gt;Synthesia's &lt;a href="https://www.synthesia.io/post/interactive-avatar-api-real-time-avatars" rel="noopener noreferrer"&gt;announcement&lt;/a&gt; describes an open architecture: customers supply their STT, LLM, and optionally TTS, while Synthesia provides real-time avatar rendering. Its &lt;a href="https://docs.synthesia.io/reference/ia-livekit-plugin-reference" rel="noopener noreferrer"&gt;plugin reference&lt;/a&gt; says the plugin replaces &lt;code&gt;session.output.audio&lt;/code&gt;, so it can lip-sync the speech your agent already produces.&lt;/p&gt;

&lt;p&gt;FaceMode follows a similar division of responsibility. Your agent handles conversation logic and produces speech. FaceMode takes that audio, generates synchronized avatar media, and publishes it as a participant in your room. Your frontend subscribes through the room SDK it already uses, as described in the &lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;FaceMode product summary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Consequently, switching LLMs is not the main reason to choose one over the other. If your application mixes one company's transcription with another company's language model and a third company's TTS, both document a path for preserving that composition.&lt;/p&gt;

&lt;p&gt;The transport boundary is a clearer difference. Synthesia's current operational documentation explicitly lists LiveKit-only transport. FaceMode's direct API accepts a room binding for LiveKit or Daily. That makes FaceMode relevant to a Daily-based application without implying that every third-party voice platform already has a tested, native FaceMode integration.&lt;/p&gt;

&lt;p&gt;Be specific about what “keep your stack” means. You still need to route audio correctly, handle session lifecycle, and build a frontend that plays media. An avatar layer does not remove those responsibilities or make an existing voice agent production-ready by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration paths: LiveKit, TypeScript, and Pipecat
&lt;/h2&gt;

&lt;p&gt;For a Python LiveKit application, Synthesia documents &lt;code&gt;livekit-plugins-synthesia&lt;/code&gt; and the &lt;code&gt;livekit.plugins.synthesia&lt;/code&gt; import namespace. The &lt;a href="https://docs.synthesia.io/reference/ia-livekit-plugin-reference" rel="noopener noreferrer"&gt;plugin requirements&lt;/a&gt; specify Python 3.10 or later and LiveKit Agents 1.8.2 or later. Its marketing page uses a different installation name, so follow the plugin reference and quickstart for exact package instructions.&lt;/p&gt;

&lt;p&gt;The following is the integration excerpt from that reference, without its inline annotation. It belongs in an existing worker with &lt;code&gt;session&lt;/code&gt;, &lt;code&gt;ctx&lt;/code&gt;, environment credentials, and a workspace-accessible avatar ID already configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit.plugins&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;synthesia&lt;/span&gt;

&lt;span class="n"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;synthesia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;synthesia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AvatarConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avatar_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;avatar-id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reference says the room must already be connected and &lt;code&gt;avatar.start()&lt;/code&gt; must run before &lt;code&gt;AgentSession.start()&lt;/code&gt;. It returns after the avatar joins and publishes video. This is an attachment excerpt, not a standalone script; use Synthesia's &lt;a href="https://docs.synthesia.io/reference/interactive-avatar-minimal-quickstart" rel="noopener noreferrer"&gt;minimal quickstart&lt;/a&gt; for the complete worker and browser app.&lt;/p&gt;

&lt;p&gt;FaceMode's Python package is &lt;code&gt;livekit-plugins-facemode&lt;/code&gt;, imported as &lt;code&gt;livekit_plugins_facemode&lt;/code&gt;. Its &lt;a href="https://docs.facemode.io/integrations/livekit-agents-python" rel="noopener noreferrer"&gt;Python guide&lt;/a&gt; requires Python 3.13 or later and LiveKit Agents 1.7 or later. The same attachment boundary looks like this, adapted from FaceMode's integration README with required environment values made explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit_plugins_facemode&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AvatarSession&lt;/span&gt;

&lt;span class="n"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;avatar_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;livekit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_WORKER_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here too, the room and agent session already exist. Start the Python agent with audio output disabled, wait for avatar media using the documented helper, and shut the avatar session down when the call ends. The &lt;a href="https://facemode.io/blog/livekit-voice-agent-avatar" rel="noopener noreferrer"&gt;LiveKit avatar tutorial&lt;/a&gt; covers those surrounding steps.&lt;/p&gt;

&lt;p&gt;FaceMode also ships &lt;code&gt;@facemode/agents-plugin-facemode&lt;/code&gt; for TypeScript and &lt;code&gt;pipecat-facemode&lt;/code&gt; for Python pipelines. A Pipecat avatar integration is not just an interchangeable LiveKit snippet: FaceMode's service taps TTS frames and handles returned media, with transport output configuration depending on how the pipeline is connected. Follow the &lt;a href="https://docs.facemode.io/integrations/overview" rel="noopener noreferrer"&gt;integration overview&lt;/a&gt; rather than assuming all transports use identical setup.&lt;/p&gt;

&lt;p&gt;Synthesia's product page mentions Python and Node in one section, but its FAQ says the plugin currently supports Python LiveKit Agents only; the reviewed reference is Python-specific. The same page marks Pipecat as coming soon. Treat those as availability questions to confirm before choosing a runtime, not proof that undocumented paths cannot exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avatar styles and custom creation
&lt;/h2&gt;

&lt;p&gt;Synthesia's announcement describes photorealistic avatars and branded Style Avatars powered by Express-3. However, its &lt;a href="https://docs.synthesia.io/reference/ia-operational-trust" rel="noopener noreferrer"&gt;operational limitations&lt;/a&gt; say Interactive Avatar currently uses synthetic and personal avatars, not stock actor avatars. Its minimal quickstart repeats that restriction and explains how to obtain an Interactive ID from an eligible workspace avatar.&lt;/p&gt;

&lt;p&gt;That distinction prevents a common purchasing mistake: a large catalog on a video-generation website does not mean every avatar in it can be used in a real-time session. Validate the specific appearance you want through the interactive product, using the actual account and API access you plan to deploy.&lt;/p&gt;

&lt;p&gt;FaceMode documents five styles: realism, semi-realism, 3D animation, anime, and flat vector. It also accepts a front-facing portrait for a custom avatar. Those are documented product capabilities, not evidence that its visual quality is better than Synthesia's.&lt;/p&gt;

&lt;p&gt;For a branded tutor, compare readability and character consistency during a full lesson. For a support agent, inspect listening behavior and lip sync across several utterances. Use the same script and reference style wherever the products permit it, rather than comparing each vendor's most favorable demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency: compare the same measurement
&lt;/h2&gt;

&lt;p&gt;FaceMode's published target is approximately &lt;strong&gt;960ms from audio to rendered frame&lt;/strong&gt;. It is not a promise of 960ms from the user's last word to a response, and it is not a result measured for this article. Transcription, turn detection, LLM generation, TTS, networking, and playback all affect the experience a user sees.&lt;/p&gt;

&lt;p&gt;The Synthesia pages reviewed describe real-time operation but do not provide a directly comparable audio-to-rendered-frame latency figure. Its quickstart discusses a preflight optimization for the voice pipeline; that is not an avatar rendering benchmark. “Not published in the reviewed sources” is different from “slow.”&lt;/p&gt;

&lt;p&gt;Measure at least three separate timings in your evaluation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Session startup:&lt;/strong&gt; request creation to the first playable video track.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response onset:&lt;/strong&gt; end of the user's turn to audible speech and visible response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interruption recovery:&lt;/strong&gt; user interruption to stopped playback and return to listening.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the same region, voice model, utterance, and client conditions. Record both typical and slower responses, including cold starts. A target for one stage cannot establish which complete system is faster, and neither successful socket negotiation nor a room join proves that media is already playing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing, free access, and production limits
&lt;/h2&gt;

&lt;p&gt;Synthesia's &lt;a href="https://www.synthesia.io/features/avatars/interactive-avatars" rel="noopener noreferrer"&gt;Interactive Avatar product page&lt;/a&gt; advertises &lt;strong&gt;500 free Interactive Avatar minutes&lt;/strong&gt; and pay-as-you-go at &lt;strong&gt;$0.12/minute&lt;/strong&gt; , with no commitment or minimum. Its &lt;a href="https://docs.synthesia.io/reference/ia-operational-trust" rel="noopener noreferrer"&gt;operational documentation&lt;/a&gt; instead says 10 credits per minute, or &lt;strong&gt;$0.10/minute&lt;/strong&gt;. These public sources disagree. Confirm the account-specific rate before estimating a production bill.&lt;/p&gt;

&lt;p&gt;At the advertised $0.12 rate, 1,000 avatar minutes would cost $120 for that layer alone. That is arithmetic based on the product page, not a quote: your voice models, media infrastructure, and other services remain separate costs. Do not use prices for Synthesia's scripted-video plans as a substitute for Interactive Avatar pricing.&lt;/p&gt;

&lt;p&gt;FaceMode's &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;beta terms&lt;/a&gt; provide &lt;strong&gt;3,600 credits, equivalent to 60 minutes&lt;/strong&gt; , with one credit metering one second of streamed video. Credit purchases are disabled during beta and top-ups open at general availability. There is no published paid rate in the reviewed FaceMode product summary, so this comparison cannot establish which service is cheaper in production.&lt;/p&gt;

&lt;p&gt;FaceMode's beta also has ten-minute sessions, one concurrent session per account, and a minimum remaining balance of 60 credits to start. Synthesia's operational docs list one concurrent session for Freemium and 100 for paid plans. Check current terms for both products before building load-test or rollout plans.&lt;/p&gt;

&lt;p&gt;Free allowances are evaluation budgets, not substitutes for availability, quotas, or production support. Count the minutes needed for testing startup, interruptions, multiple voices, and failure recovery, not just one polished demonstration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each product is a stronger fit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Synthesia is a stronger candidate when your immediate path is documented Python LiveKit integration and you need a publicly offered paid service.&lt;/strong&gt; Its Python baseline is lower than FaceMode's, its current product page describes general availability, and it publishes an initial usage rate. Its announcement also describes enterprise security certifications and workspace controls; validate their scope for your procurement requirements rather than assuming they apply identically to every deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FaceMode is a useful Synthesia alternative when your requirements include its documented TypeScript LiveKit plugin, Python Pipecat integration, or Daily room output through the direct API.&lt;/strong&gt; Its portrait-upload workflow and five library styles may also match your design requirements. Those benefits come with explicit private-beta access and limits.&lt;/p&gt;

&lt;p&gt;Neither choice should be justified by claiming the other forces a different LLM, owns your room, or lacks all stylized avatars. The public sources do not support those claims. The real decision is which documented integration and operating model fits the application you are shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose an interactive avatar API
&lt;/h2&gt;

&lt;p&gt;Before committing to either product, run the same acceptance checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm your runtime, framework version, and media transport have a documented path.&lt;/li&gt;
&lt;li&gt;Verify your intended avatar is available for interactive use, not only rendered video.&lt;/li&gt;
&lt;li&gt;Check startup, speech, interruptions, and idle behavior in an actual room.&lt;/li&gt;
&lt;li&gt;Keep API credentials and worker tokens server-side; give the browser its own room token.&lt;/li&gt;
&lt;li&gt;Confirm pricing, concurrency, session duration, and access in your account.&lt;/li&gt;
&lt;li&gt;Inspect reconnect and shutdown behavior, including what happens after a failed session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Does Synthesia have interactive avatars?
&lt;/h3&gt;

&lt;p&gt;Yes. The &lt;a href="https://www.synthesia.io/features/avatars/interactive-avatars" rel="noopener noreferrer"&gt;current Interactive Avatar page&lt;/a&gt; describes a generally available API that renders real-time conversations, not just scripted video. This question also appears in the autocomplete suggestions reviewed for this post. The important next check is whether your particular workspace avatar and integration path are supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is FaceMode a Synthesia alternative?
&lt;/h3&gt;

&lt;p&gt;For adding real-time visual output to existing voice-agent audio, it is an alternative worth testing. It is not a replacement for every Synthesia product, such as its broader scripted-video creation workflow. Compare the interactive APIs on their documented transports, runtimes, limits, and actual media behavior, while accounting for FaceMode's private-beta access.&lt;/p&gt;

&lt;p&gt;If your goal is to understand how these avatar APIs differ from platform-level launches, read the &lt;a href="https://facemode.io/blog/muse-dots-gemini-avatar-vs-facemode" rel="noopener noreferrer"&gt;Muse Avatar, OpenAI Dots, and Gemini Live Avatar comparison&lt;/a&gt;. If you already have a LiveKit agent and want to try the FaceMode path, use the &lt;a href="https://facemode.io/blog/livekit-voice-agent-avatar" rel="noopener noreferrer"&gt;LiveKit avatar plugin walkthrough&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a FaceMode proof of concept, &lt;a href="https://console.facemode.io/waitlist" rel="noopener noreferrer"&gt;request beta access&lt;/a&gt; or start with the &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;direct API quickstart&lt;/a&gt;. Choose on documented fit and a tested workflow, not on a comparison table that hides the constraints.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>livekit</category>
      <category>saas</category>
    </item>
    <item>
      <title>Muse Avatar, OpenAI Dots, Gemini Live Avatar vs FaceMode</title>
      <dc:creator>prajwaldkadam</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/facemode/muse-avatar-openai-dots-gemini-live-avatar-vs-facemode-3j63</link>
      <guid>https://dev.to/facemode/muse-avatar-openai-dots-gemini-live-avatar-vs-facemode-3j63</guid>
      <description>&lt;p&gt;Muse Avatar, OpenAI Dots, and Gemini Live Avatar put different kinds of visual presence around AI agents. For a developer, the first question is not which looks best: it is whether the product supplies an agent experience or a talking avatar API you can attach to the voice agent you already run.&lt;/p&gt;

&lt;p&gt;Meta's &lt;a href="https://research.meta.ai/blog/bringing-your-muse-to-life" rel="noopener noreferrer"&gt;Muse Realtime Avatar research announcement&lt;/a&gt; appeared on September 23, 2026. Google's &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-8-live-with-live-avatar/" rel="noopener noreferrer"&gt;Gemini 3.8 Live with Live Avatar announcement&lt;/a&gt; followed on September 24, and OpenAI &lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;introduced Dots&lt;/a&gt; on September 29. They are related developments, but they are not interchangeable integration options.&lt;/p&gt;

&lt;p&gt;This is FaceMode's documentation-based category comparison, checked on October 2, 2026. We have not run an independent visual-quality or latency benchmark across these products. The differences below describe documented product boundaries, not a claim that one provider wins every workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;What the reviewed sources describe&lt;/th&gt;
&lt;th&gt;Developer integration question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://research.meta.ai/blog/bringing-your-muse-to-life" rel="noopener noreferrer"&gt;Meta Muse Avatar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Avatar rendering coupled to Muse Realtime Voice; images can drive expressive characters&lt;/td&gt;
&lt;td&gt;The research post does not document an external PCM-to-avatar API for your room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;OpenAI Dots&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Persistent assistants with cloud computers, connected apps, messaging, and voice calls&lt;/td&gt;
&lt;td&gt;An assistant product, not a documented lip-synced video output API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-8-live-with-live-avatar/" rel="noopener noreferrer"&gt;Gemini Live Avatar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Avatar capability natively paired with Gemini 3.8 Live, available in Gemini Enterprise&lt;/td&gt;
&lt;td&gt;Evaluate as part of the Gemini Live conversation stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;FaceMode&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Audio-driven avatar API and plugins, publishing into your LiveKit or Daily room&lt;/td&gt;
&lt;td&gt;Evaluate as an added visual layer for your existing audio pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table separates categories before comparing features. A hosted assistant may be the right product for an employee who wants work delegated. A video-generation layer may be the right component for a team building a customer-facing voice application. Both needs are real; they do not share the same acceptance criteria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meta Muse Realtime Avatar: shared speech tokens and video
&lt;/h2&gt;

&lt;p&gt;Meta's research post describes Muse Realtime Voice and Muse Realtime Avatar as a single streaming system. The voice model produces speech tokens carrying both content and delivery. An audio decoder turns those tokens into speech, while the avatar model consumes the same stream to generate the visual performance.&lt;/p&gt;

&lt;p&gt;The avatar model is an audio-driven Diffusion Transformer conditioned on the speech-token stream, reference media, and recent video context. It produces video in short causal chunks and carries recent generated context into the next chunk. That is the technical mechanism Meta describes for maintaining continuity during a live conversation.&lt;/p&gt;

&lt;p&gt;Meta reports &lt;strong&gt;448×768 portrait video at 25fps&lt;/strong&gt; and approximately &lt;strong&gt;870ms from the end of a user's turn to the first byte of a synchronized voice-and-video response&lt;/strong&gt;. This is a vendor-reported end-of-turn response metric, not the time taken to render an isolated audio frame and not a measured browser playback delay from our own test.&lt;/p&gt;

&lt;p&gt;The post also describes company-run live-call preference tests against Runway Characters and HeyGen LiveAvatar. Raters preferred Muse overall, while the mannerism comparison with Runway was not statistically distinguishable from parity. That result should be attributed to Meta's evaluation and its tested conditions, not turned into a universal ranking of every avatar API.&lt;/p&gt;

&lt;p&gt;For integration planning, the important limit is what the post actually documents. It explains Muse's coupled voice-and-avatar system and notes that its examples do not all reflect avatars available in the Muse app. It does not provide a public recipe for sending your own PCM audio to Muse Avatar or publishing its output into your LiveKit room. That is a limit of the reviewed documentation, not proof that Meta will never offer such a product.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI Dots: an assistant with a visual identity
&lt;/h2&gt;

&lt;p&gt;OpenAI's &lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;Dots announcement&lt;/a&gt; describes always-on agents powered by GPT-6 Astra. They have cloud computers, can use a browser, connect to more than 4,000 apps through plugins, and work on goals over time. Users can message or call them through ChatGPT, with Slack and Teams also described as communication channels.&lt;/p&gt;

&lt;p&gt;This is a persistent-assistant architecture. Its value is task continuity: a user gives the agent context and goals, then reviews work or decisions rather than supplying every next instruction. The announcement includes examples such as investigating software feedback, updating research analysis, and preparing content for approval.&lt;/p&gt;

&lt;p&gt;The cartoon-like identity shown around Dots is not equivalent to a streaming avatar renderer. The launch article does not document an endpoint that accepts a developer's audio stream and returns a lip-synced video participant. Calling it an “avatar” in news coverage does not make it a substitute for a real-time avatar API.&lt;/p&gt;

&lt;p&gt;OpenAI says rollout begins across &lt;strong&gt;Pro, Business Premium, and Enterprise&lt;/strong&gt; plans in eligible markets. Those access details matter if you want a personal or organizational assistant. They do not answer whether your product can stream its own TTS into a custom face; that is a different capability and is not documented in the reviewed announcement.&lt;/p&gt;

&lt;p&gt;Choose Dots for the assistant workflow it actually offers. Do not evaluate it as though its cloud computer, connected apps, and background tasks were features that a narrowly scoped audio-to-video renderer ought to provide. FaceMode does not replace that agent harness or perform those tasks on your behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini Live Avatar: a native conversational video capability
&lt;/h2&gt;

&lt;p&gt;Google's &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-8-live-with-live-avatar/" rel="noopener noreferrer"&gt;announcement&lt;/a&gt; presents Gemini 3.8 Live with Live Avatar as near-real-time visual presence natively coupled to Gemini's live dialogue model. It includes lip sync, expressions, multimodal conversation, and asynchronous tool execution while dialogue continues.&lt;/p&gt;

&lt;p&gt;Google says the feature can transition across &lt;strong&gt;97 languages&lt;/strong&gt; , adapting speech-to-speech synchronization and expressions. It also describes a preset avatar library and custom avatars created from reference images, with custom creation available through enterprise allowlisting. These are Google's published capabilities, not independently tested quality claims from this article.&lt;/p&gt;

&lt;p&gt;The announcement says Live Avatar is available in &lt;strong&gt;Gemini Enterprise&lt;/strong&gt; and links developers to API documentation. It also describes SynthID watermarking for audio and video output. For organizations already evaluating Gemini's live conversational stack, those capabilities belong in the procurement and technical review.&lt;/p&gt;

&lt;p&gt;What the announcement does not establish is a drop-in renderer for an arbitrary external TTS stream. Its architecture is described as paired with Gemini 3.8 Live. That is narrower than saying “all your infrastructure must be Google,” which the launch post alone does not prove. Review the actual enterprise API and deployment terms before drawing conclusions about external clients, networking, or room integration.&lt;/p&gt;

&lt;p&gt;A team adopting Gemini for conversation generation is making a different decision from a team that already has a working STT/LLM/TTS pipeline and wants only video output. The distinction is about the integration boundary, not whether either architecture is inherently better.&lt;/p&gt;

&lt;h2&gt;
  
  
  FaceMode: a talking avatar API for existing voice agents
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;FaceMode's product summary&lt;/a&gt; describes a focused audio-to-video layer. Your agent retains its speech recognition, language model, voice synthesis, and turn detection. FaceMode receives the audio it produces, renders a lip-synced avatar, and publishes media as a participant in a LiveKit or Daily room you own.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.facemode.io/integrations/overview" rel="noopener noreferrer"&gt;integration overview&lt;/a&gt; covers LiveKit Agents plugins for Python and TypeScript, a Python Pipecat integration, and the direct REST/WebSocket path. The direct API is useful when your application produces audio outside those frameworks, provided you can implement the documented session lifecycle and PCM negotiation.&lt;/p&gt;

&lt;p&gt;That does not mean every hosted voice service or conferencing application is already supported natively. Verify that your platform exposes the audio you need and that your intended output can use the documented room transport. “Can stream audio” is an input capability, not proof of an end-to-end integration with every product bearing an API label.&lt;/p&gt;

&lt;p&gt;FaceMode documents a front-facing portrait upload workflow and five library styles: realism, semi-realism, 3D animation, anime, and flat vector. Its published latency target is approximately &lt;strong&gt;960ms from audio to rendered frame&lt;/strong&gt;. That target is not a guarantee of total conversational response time or instantaneous interruption cutoff.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; describes private-beta access, 3,600 free credits equivalent to 60 minutes, one credit per second of streamed video, ten-minute sessions, and one concurrent session per account. Paid top-ups are not open during beta. Those operating limits should be part of the decision, especially if your next step is production deployment rather than a proof of concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency claims are not directly comparable
&lt;/h2&gt;

&lt;p&gt;Meta's approximately 870ms number begins at the end of the user's turn and ends at the first byte of synchronized response. FaceMode's approximately 960ms target begins with supplied audio and concerns its rendering path. Comparing the two numbers as a speed ranking would combine different timing boundaries and a reported result with a product target.&lt;/p&gt;

&lt;p&gt;The reviewed Dots announcement is not an avatar-rendering benchmark. Google's Live Avatar announcement does not publish a directly comparable numeric latency figure. Missing numbers should remain missing, rather than being inferred from words such as “real-time” or “near-real-time.”&lt;/p&gt;

&lt;p&gt;For an application test, record session startup, end-of-turn response onset, media synchronization, and interruption recovery separately. Use the same voice settings, client region, network conditions, and utterances where the architectures permit it. Keep cold-start observations separate from ongoing turns.&lt;/p&gt;

&lt;p&gt;This also makes failures easier to diagnose. A delayed LLM response, slow avatar startup, and browser audio playback restriction can all look like “the avatar is slow” while requiring different fixes. A comparison article should not flatten those into one unexplained millisecond figure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each product is a useful fit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Muse Avatar:&lt;/strong&gt; investigate it when you care about Meta's integrated consumer-facing embodiment research, shared speech-token architecture, or expressive character generation. Its research demos provide technical context, but the reviewed post is not an external integration guide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI Dots:&lt;/strong&gt; evaluate it when you want a persistent assistant working across connected applications with review and permission controls. It addresses delegated work, not just a visual response to supplied speech.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Live Avatar:&lt;/strong&gt; evaluate it when Gemini's live dialogue stack and enterprise access fit your application. The announced multilingual synchronization, tools, and custom-avatar pathway are genuine reasons to include it in that review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FaceMode:&lt;/strong&gt; test it when your voice agent already exists and its audio needs a visual layer in a documented LiveKit or Daily room. Account for private-beta limits and test media readiness, not only API responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FaceMode is not the only provider in the last category. Other vendors also offer bring-your-own-stack real-time avatar components. For a more direct comparison, read &lt;a href="https://facemode.io/blog/synthesia-interactive-avatar-vs-facemode" rel="noopener noreferrer"&gt;Synthesia Interactive Avatar vs FaceMode&lt;/a&gt;, which examines shared architecture as well as differences in runtimes, transport, pricing, and access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a face to an existing LiveKit voice agent
&lt;/h2&gt;

&lt;p&gt;If you choose the component route, attach an avatar session inside the worker you already run. This FaceMode excerpt is adapted from the Python integration README. It assumes &lt;code&gt;agent_session&lt;/code&gt; and a connected &lt;code&gt;ctx.room&lt;/code&gt; already exist, and that environment credentials are configured on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit_plugins_facemode&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AvatarSession&lt;/span&gt;

&lt;span class="n"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;avatar_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;livekit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete flow also starts the agent with direct audio output disabled, waits for avatar media, and handles shutdown. The &lt;a href="https://facemode.io/blog/livekit-voice-agent-avatar" rel="noopener noreferrer"&gt;LiveKit avatar plugin tutorial&lt;/a&gt; covers those steps and the browser subscription path. Use the &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;direct API quickstart&lt;/a&gt; if you are not using a framework plugin.&lt;/p&gt;

&lt;p&gt;Keep credentials separate: the avatar worker receives a server-side publisher token, while the browser gets its own room token. A read-only viewer and a user publishing a microphone have different permissions. An API key or worker token should never be the shortcut used to make a browser demo connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to answer before choosing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do you need an assistant or a renderer?&lt;/strong&gt; If the requirement is autonomous work across apps, compare agent products and their controls. If it is visual output for existing speech, compare real-time avatar APIs and transports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can you keep the voice pipeline you already have?&lt;/strong&gt; FaceMode documents that path explicitly. Google's launch describes native pairing with Gemini Live. Meta's research describes shared voice tokens. Dots is a packaged assistant. Check documentation for the specific integration boundary rather than assuming the marketing term “avatar” implies the same contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What will you actually test?&lt;/strong&gt; Start with a short real conversation: join, speak, interrupt, pause, resume, and end. Inspect video, audio, and session cleanup. Then check pricing and concurrency with the account you will deploy, not only the public demo.&lt;/p&gt;

&lt;p&gt;The goal is to choose the right layer for your application. If FaceMode's component model fits, &lt;a href="https://console.facemode.io/waitlist" rel="noopener noreferrer"&gt;request beta access&lt;/a&gt; or read the &lt;a href="https://docs.facemode.io" rel="noopener noreferrer"&gt;FaceMode docs&lt;/a&gt; before wiring it into your agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Primary sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://research.meta.ai/blog/bringing-your-muse-to-life" rel="noopener noreferrer"&gt;Meta: Bringing Your Muse to Life&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;OpenAI: Introducing Dots&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-8-live-with-live-avatar/" rel="noopener noreferrer"&gt;Google: Introducing Gemini 3.8 Live with Live Avatar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;FaceMode product summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;FaceMode direct API quickstart&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>news</category>
      <category>saas</category>
    </item>
    <item>
      <title>LiveKit Avatar Plugin: Add a Face to Your Voice Agent</title>
      <dc:creator>prajwaldkadam</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/facemode/livekit-avatar-plugin-add-a-face-to-your-voice-agent-4pni</link>
      <guid>https://dev.to/facemode/livekit-avatar-plugin-add-a-face-to-your-voice-agent-4pni</guid>
      <description>&lt;p&gt;Use the FaceMode &lt;strong&gt;LiveKit avatar plugin&lt;/strong&gt; to turn your existing voice agent's speech into lip-synced video in the room you already run. This guide covers the Python and TypeScript attachment paths, then shows how a browser subscribes to the avatar's media.&lt;/p&gt;

&lt;p&gt;Your STT, LLM, TTS, and conversation logic stay in your agent. The avatar layer receives its audio and publishes synchronized audio/video as another room participant, following the model described in &lt;a href="https://docs.livekit.io/agents/models/avatar/" rel="noopener noreferrer"&gt;LiveKit's virtual avatar overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The examples below are integration excerpts validated against FaceMode's plugin READMEs and &lt;a href="https://docs.facemode.io/integrations/overview" rel="noopener noreferrer"&gt;integration docs&lt;/a&gt;. They assume you already have a working LiveKit agent; they are not standalone worker applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;p&gt;Four things, all server-side:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A FaceMode account.&lt;/strong&gt; Sign-up is waitlist-gated during the private beta at &lt;a href="https://console.facemode.io/signup" rel="noopener noreferrer"&gt;console.facemode.io/signup&lt;/a&gt;. Approved testers get 3,600 free credits - 60 minutes of avatar video, no card required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A FaceMode API key&lt;/strong&gt; (&lt;code&gt;fm_...&lt;/code&gt;) from the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An avatar ID&lt;/strong&gt; for the face that should speak. Pick from the library across five art styles, or upload one front-facing portrait and make it your own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A LiveKit room you own, plus a publisher token.&lt;/strong&gt; The token must grant the FaceMode worker &lt;code&gt;room_join&lt;/code&gt;, &lt;code&gt;can_publish&lt;/code&gt;, and &lt;code&gt;can_subscribe&lt;/code&gt; on your room.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The access and beta limits come from the &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;FaceMode quickstart&lt;/a&gt;. Use a separate participant identity for the avatar worker and for each viewer. Reusing a token identity for multiple connections is not a way to give every participant the same permissions.&lt;/p&gt;

&lt;p&gt;Set the credentials as environment variables on your backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"fm_your_key_here"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_avatar_id"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LIVEKIT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"wss://your-project.livekit.cloud"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LIVEKIT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;publisher-token&amp;gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Keep every token in this guide on the server. The browser only ever receives the room URL and its own subscribe-only viewer token - never the API key, never the publisher token.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The 30-second mental model
&lt;/h2&gt;

&lt;p&gt;FaceMode is not another agent framework. It is one job done well: turn the audio your agent already produces into a lip-synced avatar, published into the room you already run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your agent (STT -&amp;gt; LLM -&amp;gt; TTS)
        |
        v (the TTS audio you already have)
    FaceMode -- lip-synced video frames, ~960ms audio-to-frame target
        |
        v
  Your LiveKit room -- the avatar joins as an ordinary participant
        |
        v
  Your frontend -- subscribes with the livekit-client SDK you already ship

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing sits between your app and your room. FaceMode joins as a participant, publishes its video track, and leaves when the session ends. If you removed FaceMode tomorrow, your agent would keep working exactly as before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1A: Python LiveKit avatar integration
&lt;/h2&gt;

&lt;p&gt;If your agent runs on LiveKit Agents for Python, install the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;livekit-plugins-facemode

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.facemode.io/integrations/livekit-agents-python" rel="noopener noreferrer"&gt;FaceMode Python integration guide&lt;/a&gt; requires Python 3.13+ and LiveKit Agents 1.7+. In your existing asynchronous worker entrypoint, &lt;code&gt;ctx&lt;/code&gt; is its job context and &lt;code&gt;agent_session&lt;/code&gt; is the &lt;code&gt;AgentSession&lt;/code&gt; you have already configured with speech and model providers. The attachment excerpt is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room_io&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit_plugins_facemode&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AvatarSession&lt;/span&gt;

&lt;span class="n"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;avatar_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;livekit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIVEKIT_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;room_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;room_io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RoomOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details worth noticing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;audio_output=False&lt;/code&gt;.&lt;/strong&gt; The avatar publishes synchronized audio and video. Disabling the agent's separate audio path avoids duplicate playback; it is not a guarantee against every possible synchronization issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;room&lt;/code&gt; object.&lt;/strong&gt; You hand the plugin the same LiveKit URL and publisher token you already manage. FaceMode never creates or replaces your room.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;wait_for_join()&lt;/code&gt;.&lt;/strong&gt; A one-line confirmation that the avatar participant is actually in the room before you consider the session live.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Success means &lt;code&gt;wait_for_join()&lt;/code&gt; resolves and the avatar's video track is available in the room, not merely that the REST request succeeded. The &lt;a href="https://docs.facemode.io/integrations/livekit-agents-python" rel="noopener noreferrer"&gt;Python integration docs&lt;/a&gt; distinguish session creation, WebSocket negotiation, and media readiness.&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;avatar.start()&lt;/code&gt; creates a FaceMode session over REST, opens the worker WebSocket, negotiates the audio format, and forwards TTS frames as PCM. After a started session loses its transport, the plugin attempts up to two reconnects with fresh one-time tokens. Exhaustion surfaces an error; reconnect is not a guarantee that every outage is invisible.&lt;/p&gt;

&lt;p&gt;Optionally, pin the upstream input provider so the backend knows which TTS is driving the audio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;avatar_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;input_provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elevenlabs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# deepgram, gemini, gnani, elevenlabs, openai, cartesia, sarvam, custom
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any TTS works. If it can stream audio, it can drive a face.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1B: TypeScript LiveKit avatar integration
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;@facemode/agents-plugin-facemode&lt;/code&gt; with Node 20+ and &lt;code&gt;@livekit/agents&lt;/code&gt; 1.x, as specified in the &lt;a href="https://docs.facemode.io/integrations/livekit-agents-js" rel="noopener noreferrer"&gt;TypeScript integration guide&lt;/a&gt;. This is an alternative to the Python route, not a second worker you need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @facemode/agents-plugin-facemode

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this inside your worker entrypoint with an already-configured &lt;code&gt;agentSession&lt;/code&gt;. The session-start portion is adapted from the TypeScript docs so the excerpt includes the audio-output setting as well as the avatar attachment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@livekit/agents&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AvatarSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@facemode/agents-plugin-facemode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AvatarSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FACEMODE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;avatarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FACEMODE_AVATAR_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepgram&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// optional&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agentSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;livekit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LIVEKIT_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LIVEKIT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agentSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a helpful voice assistant.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;outputOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;audioEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForJoin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin replaces the agent session's audio tail: TTS frames flow to FaceMode as binary &lt;code&gt;pcm_s16le&lt;/code&gt;, interruptions send a &lt;code&gt;cancel_utterance&lt;/code&gt; control, and reconnects are handled for you. Sample rates from 8kHz to 48kHz are accepted, with 8, 11.025, 12, 16, 22.05, 24, 32, 44.1, and 48kHz as the preferred set covering common TTS outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: render the LiveKit avatar in the browser
&lt;/h2&gt;

&lt;p&gt;This is the part developers expect to be hard and is actually the easiest. The avatar is just a participant in your room, so your existing LiveKit client code subscribes to it like any other video track.&lt;/p&gt;

&lt;p&gt;Mint a &lt;strong&gt;separate, subscribe-only viewer token&lt;/strong&gt; for a read-only viewer and pass it down with the room URL. In the browser, register the subscription listener before connecting and insert the elements returned by &lt;code&gt;track.attach()&lt;/code&gt; into the DOM. This excerpt is adapted from &lt;a href="https://docs.livekit.io/home/client/tracks/subscribe" rel="noopener noreferrer"&gt;LiveKit's track subscription guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RoomEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;livekit-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RoomEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TrackSubscribed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;audio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// render the avatar&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewerToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your own viewer token&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success is visible video plus audible speech, not just a connected room. In a multi-participant app, filter tracks by the avatar participant identity instead of rendering every subscribed participant. Your real app should also detach media on unsubscribe and disconnect the room when its viewer component unmounts.&lt;/p&gt;

&lt;p&gt;This is a read-only viewer example. A user who talks to the agent needs microphone publication in your existing voice UI and appropriate grants on their own token. Do not give them the worker's publisher token. Browser autoplay policies can require a user gesture to enable audio; use LiveKit's playback controls in that interaction rather than interpreting silent playback as an avatar failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interruptions, silence, and the in-between moments
&lt;/h2&gt;

&lt;p&gt;Real conversations are messy, and the avatar behaves accordingly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Barge-in.&lt;/strong&gt; The &lt;a href="https://docs.facemode.io/integrations/livekit-agents-python" rel="noopener noreferrer"&gt;plugin docs&lt;/a&gt; say an agent interruption sends &lt;code&gt;cancel_utterance&lt;/code&gt;. That control is not evidence of zero-delay playback cancellation; test the visible and audible result in your actual client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silence between utterances.&lt;/strong&gt; The &lt;a href="https://facemode.io/llms-full.txt" rel="noopener noreferrer"&gt;FaceMode product summary&lt;/a&gt; describes crossfading into an idle loop between utterances. Check that this remains visible when your voice agent is waiting on a long-running tool call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup budgets.&lt;/strong&gt; The Python docs allow up to 240 seconds for readiness polling and WebSocket opening, but use a separate 15-second protocol acknowledgement budget and a 30-second default media-join timeout. Those are timeout settings, not a measured cold-start duration or a guaranteed successful startup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A short pitfalls checklist
&lt;/h2&gt;

&lt;p&gt;Before your first session, check the five things that bite most often:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Token grants.&lt;/strong&gt; The publisher token must allow join, publish, and subscribe. If it pins a fixed participant identity, make the room policy allow that identity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets stay server-side.&lt;/strong&gt; API key and publisher token never reach the browser. Only the room URL and a subscribe-only viewer token do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;audio_output=False&lt;/code&gt;&lt;/strong&gt; on the agent session, or you will hear double.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declared sample rate must match the actual PCM.&lt;/strong&gt; The plugin locks the format at negotiation and rejects a mismatch mid-stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beta limits.&lt;/strong&gt; Sessions auto-end after 10 minutes, one concurrent session per account, and a session needs at least 60 credits of remaining balance to start.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: validate media, failure recovery, and shutdown
&lt;/h2&gt;

&lt;p&gt;A LiveKit avatar integration crosses several boundaries: your voice agent, the FaceMode API, its ingestion WebSocket, the avatar participant, and browser playback. Diagnose the boundary that failed rather than treating all five as one connection.&lt;/p&gt;

&lt;p&gt;Start with a short utterance whose beginning and end are easy to recognize. Confirm that only one audio path is audible and that the visible avatar is the participant you intended to subscribe to. Then interrupt a longer sentence. Observe whether the agent stops generating speech, whether cancellation reaches the avatar, and whether already-buffered client media continues playing. This separates agent behavior from transport and playback behavior.&lt;/p&gt;

&lt;p&gt;Next, test a pause with no speech. Idle animation should not be mistaken for proof that the agent is ready to answer, and a connected room should not be mistaken for proof that the ingestion socket is healthy. Keep the voice agent's own state indicator alongside the video so users can distinguish listening from waiting.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.facemode.io/integrations/livekit-agents-python" rel="noopener noreferrer"&gt;Python lifecycle guide&lt;/a&gt; documents &lt;code&gt;await avatar.stop()&lt;/code&gt; or &lt;code&gt;aclose()&lt;/code&gt; for shutdown. The TypeScript guide documents the equivalent methods. Wire these into your existing call cleanup path, including failures; an abandoned browser tab should not be your only cleanup mechanism. The canonical &lt;code&gt;ended&lt;/code&gt; acknowledgement confirms the protocol message, not that every backend cleanup action has finished.&lt;/p&gt;

&lt;p&gt;If you test reconnect behavior, do it with disposable development sessions. The plugin only attempts recovery after negotiation has completed; intentional shutdown and fatal protocol errors do not trigger the same recovery path. Do not write code that retries a consumed one-time WebSocket token or replays previously sent audio.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the latency target does and does not mean
&lt;/h2&gt;

&lt;p&gt;FaceMode publishes an approximately 960ms audio-to-rendered-frame target. Your total voice-agent response time also includes turn detection, transcription, LLM generation, TTS, transport, and client playback. A sub-second target for the avatar layer does not imply the whole conversation responds within one second.&lt;/p&gt;

&lt;p&gt;When comparing providers, measure the same utterance from the same boundary. Keep session startup separate from steady-state response latency. Also keep interruption recovery separate: a pipeline can begin speaking quickly and still feel poor if it cannot stop naturally when the user cuts in.&lt;/p&gt;

&lt;p&gt;Record observations from a real room rather than using a console-only voice test. Console mode can validate conversation logic without exercising the video subscription and browser playback paths. These are suggested acceptance checks, not benchmarks we ran for this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a plugin or the direct talking avatar API
&lt;/h2&gt;

&lt;p&gt;Use the plugin when LiveKit Agents already owns your speech pipeline. It captures the framework's audio frames and lifecycle events, so you do not have to reproduce its WebSocket protocol handling. Use the direct talking avatar API when your application produces audio outside that framework and you can manage session creation, PCM negotiation, and shutdown yourself.&lt;/p&gt;

&lt;p&gt;That choice does not automatically prove compatibility with every hosted voice-agent platform. A provider that cannot expose its generated audio, or whose calls are phone-only, may need an additional transport or product integration. FaceMode's documented room output is LiveKit or Daily, not a claim of native support for every conferencing system.&lt;/p&gt;

&lt;p&gt;For a comparison with another bring-your-own-stack provider, see &lt;a href="https://facemode.io/blog/synthesia-interactive-avatar-vs-facemode" rel="noopener noreferrer"&gt;Synthesia Interactive Avatar vs FaceMode&lt;/a&gt;. Both preserve existing conversation models; their documented runtimes, transport paths, and access limits are the useful differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  LiveKit avatar plugin: short version
&lt;/h2&gt;

&lt;p&gt;Inside your existing asynchronous Python worker, connect the room, attach the configured avatar, start the configured agent session with direct audio disabled, and wait for video. This recap uses the &lt;code&gt;room&lt;/code&gt; binding and objects configured in Step 1A; it is not a standalone script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;room_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;room_io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RoomOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;room&lt;/code&gt; is the server-side dictionary containing &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, and the worker &lt;code&gt;token&lt;/code&gt; from the earlier example. Stop the avatar through your call's cleanup path once the conversation ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  From zero to a talking face
&lt;/h2&gt;

&lt;p&gt;The whole flow is: install the plugin, hand it your room, keep your agent code. If you want the raw HTTP and WebSocket protocol instead - any language that can hold a socket open works - the &lt;a href="https://docs.facemode.io/quickstart" rel="noopener noreferrer"&gt;direct API quickstart&lt;/a&gt; walks it end to end.&lt;/p&gt;

&lt;p&gt;For the broader category distinction, read &lt;a href="https://facemode.io/blog/muse-dots-gemini-avatar-vs-facemode" rel="noopener noreferrer"&gt;Muse Avatar, OpenAI Dots, Gemini Live Avatar vs FaceMode&lt;/a&gt;. It separates platform agent experiences from avatar components you integrate into a product.&lt;/p&gt;

&lt;p&gt;Ready to see your agent talk? &lt;a href="https://console.facemode.io/waitlist" rel="noopener noreferrer"&gt;Join the beta&lt;/a&gt; - 60 free minutes, no card - or skim the &lt;a href="https://docs.facemode.io" rel="noopener noreferrer"&gt;docs&lt;/a&gt; first.&lt;/p&gt;

</description>
      <category>livekit</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>webrtc</category>
    </item>
  </channel>
</rss>
