Header Logo

How to Customize a Realtime AI Avatar’s Appearance with Agora for Video Apps

How to Customize a Realtime AI Avatar’s Appearance with Agora for Video Apps

Learn to customize realtime AI avatar appearance in Agora video apps with server-side profiles, presets, and low-latency sync.

Introduction


If you are building a realtime video app, “customizing the avatar’s appearance” usually means more than picking a face image. You need the avatar to fit the product context: match the brand, look consistent with the UX, and behave correctly under realtime constraints like low latency, frame synchronization, and switching between devices and network conditions.


This post covers the practical side of that problem: how to think about avatar appearance as part of a realtime video pipeline, what is actually safe to customize, and where those settings belong so you do not ship fragile client-side logic. By the end, you should have a clear mental model for styling an interactive AI avatar for a video app and a concrete path for wiring that up in production.


What “appearance” means in a realtime avatar system


In a normal video app, appearance is mostly an asset problem: camera feed, cropping, background, overlays, maybe some CSS. With a talking AI avatar, appearance spans both media generation and presentation.


At a minimum, you usually control some combination of:


  • Identity: the avatar persona or face model used for the session.

  • Visual style: realistic, stylized, branded, or product-specific presentation.

  • Framing: how the face is cropped, positioned, or scaled in the app UI.

  • Background and composition: transparent, solid color, scene backdrop, or integrated panel.

  • Session-specific behavior: voice, instructions, and whether a particular avatar configuration is used for a given user or workflow.


The important distinction is that not every “appearance” decision belongs in the browser. If you are generating a synchronized talking face in realtime, the avatar pipeline has to stay consistent across frames, audio timing, and session state. That means you want appearance configuration to be explicit, server-side, and versioned like any other backend resource.


Model the avatar as a server-side asset, not a frontend component


The biggest mistake I see is treating the avatar like a UI widget. It is not. It is a realtime media endpoint with state. If you push too much of the setup into the frontend, you get hard-to-debug problems:


  • Different clients render the same avatar differently.

  • Session state drifts between tabs, reloads, or device changes.

  • API keys or privileged configuration leak into the browser.

  • You cannot reliably reproduce a user’s setup when debugging.


Instead, create the avatar configuration once, store it as backend state, and reference it when you start a session. The UI should only consume the resulting stream or embed.


A practical pattern is:


  1. Define an avatar record with a stable ID and the appearance profile you want.

  2. Attach session-time settings such as voice, instructions, or layout variant.

  3. Start a realtime session that binds the avatar to the user’s conversation.

  4. Render the resulting media in your app using WebRTC or an embed.


This separation matters because appearance often changes less frequently than conversation state. You might A/B test a branded avatar in one flow and a more neutral one in another, but the session mechanics stay the same.


Use the transport to preserve the look you want


For realtime avatars, “appearance” is affected by the transport as much as by the rendering assets. If the stream is low quality, jittery, or incorrectly cropped, the avatar will look wrong even if the face asset itself is correct.


When integrating with a video app, pay attention to these implementation details:


  • Aspect ratio: Decide whether the avatar is portrait, square, or landscape before you build the UI around it.

  • Letterboxing vs. cropping: Cropping can preserve size but cut off shoulders or jaw motion; letterboxing preserves the full frame.

  • Frame consistency: Avoid client-side transforms that can reflow the canvas or cause visible jumps.

  • Background handling: If you expect the avatar to sit on top of a branded page, make sure the composition supports that from the start.

  • Latency budget: Lip sync is sensitive to delay; anything that adds rendering or network overhead will make the avatar feel off.


In practice, the right answer is often to standardize a small number of appearance presets and map them to application states. For example: “support chat,” “sales demo,” and “in-product assistant” may each use different framing and styling, but all share the same session lifecycle.


Implement appearance through stable session configuration


When you build this yourself, use a backend API to create or update the avatar profile and then start the realtime session with the correct profile ID. The exact fields depend on your setup, but the shape is usually the same: identity + style + session parameters.


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


That is intentionally schematic. The specific request body and available fields are documented in the API reference, but the architectural point is the same: store appearance as a backend resource and keep the browser out of it.


If you need to update an avatar for a specific campaign or customer segment, do that in the backend and roll it out by switching which avatar ID a session uses. Do not try to mutate the visual configuration ad hoc from the client on every page load.


Where the LiveKit plugin fits if you already have a voice agent


If your app already has a LiveKit voice agent, the cleanest way to add a face is to keep the voice logic as-is and attach the avatar at the agent layer. That gives you a synchronized video face without rewriting your conversation stack.


The quickstart repo is useful if you want to see the surrounding integration patterns, but for LiveKit specifically the relevant surface is the livekit-plugins-protoface plugin published on PyPI. In the plugin model, the agent keeps producing speech and the avatar tracks it as a media participant, which is exactly what you want for lip-synced talking-head behavior.


The rough flow looks like this:


# Illustrative shape only; check the plugin docs for the exact API
# Illustrative shape only; check the plugin docs for the exact API
# Illustrative shape only; check the plugin docs for the exact API


The main advantage here is operational. You already have one realtime system managing audio, interruptions, and turn-taking; the avatar becomes another synchronized output, not a separate frontend animation.


That also makes appearance easier to manage. The avatar ID and its styling live in your backend or dashboard, while the LiveKit agent only references the configured avatar. If you later change the facial style, framing, or brand treatment, you do it once in the avatar configuration rather than in every client.


Browser embeds are useful when you do not want to own the media stack


For some apps, especially marketing pages or lightweight interactive experiences, an iframe embed is the simplest way to control appearance without exposing backend credentials. This is a good fit when the browser should never hold an API key and you want the avatar experience to be isolated from the host app’s codebase.


That trade-off is straightforward: less control over the underlying media pipeline, but much less integration complexity. You still choose the avatar and session behavior on the server side, and the embed renders the result with guardrails like origin allowlisting and rate limits.


Use this model when the “appearance” problem is mostly about placing the avatar into a page cleanly, not when you need deep control over compositor behavior or a custom WebRTC stack.


Practical design rules for avatar appearance in video apps


After a few integrations, a pattern emerges. The best avatar customizations are the ones that survive real users, real bandwidth, and real product iterations. A few rules help:


  • Prefer presets over one-off tweaks. Define a small set of approved appearance variants and reuse them across flows.

  • Keep visual config server-side. The browser should render, not decide, what the avatar looks like.

  • Test on low-end devices. A style that looks fine on desktop can become awkward when frames are constrained or resized.

  • Validate with live audio. A good still frame is not enough; appearance has to hold up while the mouth moves and the head turns.

  • Plan for session boundaries. If a user reconnects, the avatar should come back in the same visual state.


If you need more implementation detail, the docs at docs.protoface.com are the right place to check the exact request fields, session lifecycle, and supported avatar configuration options.


Conclusion


Customizing a realtime AI avatar’s appearance is mostly about disciplined system design: keep the avatar configuration server-side, treat appearance as a stable asset, and align it with the transport and UI from the start. If you do that, the avatar will look consistent across sessions, devices, and product surfaces instead of breaking as soon as you add realtime constraints.


If you are wiring this into an existing voice agent or web app, start by defining one or two appearance presets and make them reproducible through your backend. Then validate the result in an actual realtime session, not just a static preview.


For API details, SDK usage, and working examples, check the documentation and the quickstarts in the GitHub repositories linked above.

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.