Header Logo

Building a Realtime AI Avatar App with LiveKit Simulcast and Dynamic Bitrate Control

Building a Realtime AI Avatar App with LiveKit Simulcast and Dynamic Bitrate Control

Build a LiveKit realtime AI avatar app with simulcast, dynamic bitrate control, and WebRTC stats for stable lip-sync under variable networks.

Introduction


Realtime AI avatars are mostly a systems problem, not a model problem. The hard part is keeping audio, video, and conversational state synchronized enough that the face looks like it is actually speaking, while still staying responsive under changing network conditions. If you are building a voice agent, customer-support bot, or conversational web experience, you usually need three things at once:


  • low-latency audio handling for turn-taking,

  • a video pipeline that can adapt to bandwidth and device constraints, and

  • a control plane for sessions, authentication, and lifecycle management.


This post walks through the practical side of that stack: how simulcast and dynamic bitrate control fit into a realtime avatar app, why they matter for avatars specifically, and how to wire them into a LiveKit-based voice agent without turning the client into a fragile video application. By the end, you should be able to reason about when to send multiple video encodings, when to let the receiver or server downshift quality, and where an avatar API fits into the architecture.


Why avatars stress the video pipeline differently


With a normal webcam call, the viewer tolerates modest quality changes. With an AI avatar, the tolerance is lower in some ways and higher in others. Lower, because face motion and mouth shapes are exactly the content users are watching; artifacts around the mouth are obvious. Higher, because the “video” is synthetic, so you can choose a delivery profile that prioritizes stable lip-sync and motion smoothness over photographic detail.


That changes the optimization target. You are not just trying to maximize resolution. You are trying to preserve the perceptual cues that make the avatar feel synchronized to speech:


  • audio stays close to real time,

  • video frames arrive regularly enough that mouth motion does not stutter,

  • the transport can recover from bandwidth drops without causing long freezes, and

  • the system can recover quickly after a turn change or interruption.


This is where simulcast and bitrate adaptation matter. They give the transport more than one way to represent the same video source, so the receiver can pick an appropriate layer for the current network conditions instead of forcing a single stream to fit every client.


Simulcast in practice: multiple encodes, one source


Simulcast means the sender publishes multiple encodings of the same video track, usually at different resolutions and bitrates. A conferencing system or browser client can then subscribe to a layer that matches its current conditions. The underlying idea is simple: if the connection degrades, do not keep fighting to deliver the highest-quality stream; switch to a lower-cost representation that still preserves the avatar’s motion cues.


For avatar workloads, this is often better than a single adaptive encoder because:


  • the receiver can make a clean layer switch rather than waiting for the encoder to react,

  • congestion control has a wider operating range, and

  • you can keep the “good enough” layer visually consistent across clients.


In a LiveKit-based app, the common pattern is: the voice agent handles the conversation, the avatar publishes a talking-face video track, and the room infrastructure handles track subscription and quality selection. The important architectural detail is that the avatar video should not be treated like a static asset. It is a live media source whose quality should respond to the receiver’s actual conditions.


Dynamic bitrate control: what to change, and when


Bitrate control is not only about the encoder’s nominal target. In a realtime avatar app, you want the bitrate to reflect three competing constraints:


  1. Latency budget — if the stream buffers, lip-sync degrades quickly.

  2. Network variability — mobile and consumer broadband can change mid-session.

  3. Perceptual importance — facial motion is more important than crisp texture detail.


A practical control loop usually looks like this:


  • start with a conservative default bitrate for the avatar tier you are serving,

  • monitor WebRTC stats such as packet loss, RTT, and available outgoing bandwidth,

  • reduce bitrate quickly when congestion appears, and

  • increase bitrate more gradually to avoid oscillation.


The exact thresholds depend on your media pipeline, but the principle is stable: react faster to bad conditions than to good ones. Otherwise the stream will spend too much time probing upward and then collapsing again.


Common trade-offs and gotchas


There are a few failure modes that show up repeatedly in realtime avatar apps:


  • Over-optimizing for resolution. A higher-resolution face with unstable frame delivery looks worse than a lower-resolution face that stays smooth.

  • Ignoring audio-first latency. If video adapts well but audio lags, the avatar still feels off. The conversational loop is anchored by speech timing, not visual fidelity.

  • Letting the encoder thrash. Rapid bitrate changes create visible pumping and can make the avatar look inconsistent across turns.

  • Skipping session-level controls. A browser-facing avatar needs auth, rate limiting, and clean session lifecycle handling just as much as a media pipeline does.


If you are debugging this kind of system, inspect the WebRTC stats before you blame the model. If mouth motion looks late, check jitter, retransmissions, decode delay, and whether the client is being forced to hold a higher-quality layer than the network can sustain. If the avatar looks blocky only during speaking bursts, you may have a bitrate floor that is too low for the peak motion in the mouth region.


How this fits into a LiveKit voice agent


The cleanest place to add an avatar is at the agent boundary. Your voice agent already owns the conversation loop: user audio in, model response out, interrupt handling, and turn-taking. The avatar should subscribe to that state and emit synchronized talking video rather than trying to infer conversation timing independently.


In LiveKit, that usually means attaching a plugin that can turn the agent’s response stream into a live avatar track. Protoface provides that integration via the LiveKit/Pipecat video service path and the published LiveKit Agents plugin. The key point is not the package name; it is the division of responsibility. The agent decides what should be said and when a turn starts or stops. The avatar service decides how to render and publish the talking face with the right media characteristics.


A minimal sketch, with exact fields omitted because they vary by setup, looks like this:


# illustrative only; check the docs for exact config names
# illustrative only; check the docs for exact config names
# illustrative only; check the docs for exact config names


If you are integrating at the media-server layer rather than directly inside the agent, this is where simulcast matters most. The track can be published once, but the room can adapt the subscribed layer to the client’s current capacity without forcing you to rebuild the avatar stream for every viewer.


Where Protoface helps without leaking complexity


Protoface is useful here because it separates the avatar control plane from the client media path. You can create and manage avatars and sessions over the REST API, or do it programmatically with the Python SDK, while keeping the actual browser or app transport focused on WebRTC delivery. For session creation from a backend, the shape is straightforward:


curl -X POST https://api.protoface.com/sessions \
}'
curl -X POST https://api.protoface.com/sessions \
}'
curl -X POST https://api.protoface.com/sessions \
}'


The exact request fields depend on the API version, but the pattern is what matters: authenticate on the server, create a session, and hand the resulting session details to your agent or embed. That keeps secrets out of the browser and makes it easy to enforce per-session policy, including duration and rate limits. For the Python SDK and the official docs, start with the documentation and adapt the examples to your session model.


Operational guidance: choose quality tiers intentionally


Because usage is billed by quality tier, the technical choice and the cost choice are the same decision. For avatar apps, I recommend thinking in terms of product surfaces:


  • High-attention surfaces like sales demos or guided onboarding can justify a higher tier because the avatar is the product.

  • Utility surfaces like support triage often do fine with a balanced tier, as long as lip-sync and facial motion remain stable.

  • Embedded widgets should be conservative by default; the browser environment is more variable than a managed app client.


Also plan for graceful degradation. If the network cannot sustain your preferred layer, it is better to reduce visual fidelity than to stall the session. Users are far more sensitive to the avatar freezing mid-sentence than to a softer image.


Conclusion


Realtime AI avatars are a media-systems problem wrapped around a conversation system. Simulcast gives you multiple delivery options for the same face, and dynamic bitrate control lets you move between them without making the experience brittle. The goal is not perfect video; it is stable, low-latency, synchronized motion that survives real network conditions.


If you are building this on LiveKit, keep the agent responsible for conversational state and let the avatar layer focus on synchronized rendering and transport adaptation. If you want a concrete starting point, check the quickstarts in the Protoface ecosystem, read through the docs, and wire up a small end-to-end session before you tune quality tiers or bitrate policy. The fastest way to get this right is to instrument it early and watch the WebRTC stats while you talk to it.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.