How to Add a Talking Avatar to a Digital Kiosk in React and FastAPI

Build a React + FastAPI digital kiosk with a realtime talking avatar, server-side sessions, WebRTC streaming, and lifecycle control.
Introduction
If you’re building a digital kiosk in React, the hard part is usually not the UI chrome. It’s the interaction loop: capture a visitor’s attention, let them ask a question naturally, and respond with a voice agent that feels present instead of like a sterile form. Adding a talking avatar is a good fit here, because the face gives the voice agent a visual anchor and makes the experience easier to discover from across a room.
This post shows the architecture behind a kiosk-style avatar experience and the implementation choices that matter in practice: how the browser app should handle video, audio, and session state; how a backend should mint and manage realtime sessions; and where a service like Protoface fits when you need a lip-synced, realtime avatar rather than a static video asset.
By the end, you should be able to wire up a React frontend, a FastAPI backend, and a realtime avatar session with clear boundaries around auth, streaming, and deployment.
Start with the interaction model, not the pixels
A kiosk avatar is usually a realtime media problem disguised as a UI problem. The avatar is not an animated GIF or a background video. It is typically a WebRTC stream or similarly low-latency media session driven by an agent. The browser renders incoming video, plays audio, and sends user input back to the backend or agent layer. The important properties are:
Low latency: if the avatar responds too slowly, the illusion breaks.
Synchronized audio and mouth motion: lip sync has to track the spoken response, not just any animation loop.
Session identity: the kiosk needs a server-issued session that can be authenticated and revoked.
Network resilience: kiosk networks are often flaky, captive, or locked down.
In React terms, that means your frontend should be mostly a viewer/controller: it mounts the video element, subscribes to status updates, and handles user interactions like “start conversation,” “mute,” “restart session,” and “idle timeout.” The actual avatar generation and voice agent logic should live elsewhere.
Reference architecture for a React + FastAPI kiosk
A clean split is:
React app: kiosk UI, video container, mic controls if needed, and session lifecycle state.
FastAPI backend: creates avatar sessions, stores any app-specific metadata, and returns short-lived session credentials or embed URLs.
Realtime media service: handles streaming avatar video and audio, plus the agent’s voice turn-taking.
That separation matters because you should not expose long-lived API keys to the browser. The kiosk frontend can be fully public, but privileged operations belong on the server. For a kiosk, this also gives you a place to enforce policies like session duration, per-device limits, or IP-based restrictions.
A practical implementation detail: keep the frontend ignorant of provider-specific secrets. The browser should ask your backend for a session, then connect using whatever temporary token, iframe URL, or client configuration the backend returns.
FastAPI: issue sessions server-side
Your FastAPI service should own the privileged call that creates or configures a realtime avatar session. The exact request shape depends on the provider, but the pattern is consistent: accept a kiosk request, validate it, call the avatar API, and return only the minimum data needed by the browser.
Example skeleton:
The key idea is that the browser gets an ephemeral connect URL or session token, not your API key. If you need kiosk-specific behavior, compute it here: choose voice, choose avatar, set instructions, or attach a tenant/customer identifier.
React: mount the video stream and manage lifecycle
On the client, the UI should treat the avatar like a media endpoint. You usually want a responsive container that can render a <video> element, overlay controls, and transition states like loading, connecting, live, and reconnecting.
A simple pattern is to fetch the session from FastAPI, then attach the returned connection data to your media layer:
There are two implementation details worth calling out:
Use
playsInlinefor kiosk displays, especially on Safari-based devices, to avoid unwanted fullscreen behavior.Think carefully about audio policy. Kiosk devices may need explicit user interaction before audio can autoplay. If the device is headless or always-on, you may need a bootstrap tap or a hardware-level kiosk policy.
If you are rendering the avatar full-screen, make the layout forgiving. The face should stay centered and readable at a distance, and the video container should tolerate aspect ratio changes without stretching the face.
Session control, turn-taking, and kiosk-specific gotchas
The interaction model for a kiosk is usually different from a chatbot widget. A kiosk often has one active conversation at a time, should reset itself after inactivity, and may need to speak first. That changes how you manage state.
Some useful patterns:
Idle timeout: if nobody interacts for N seconds, end the session and return to an attract loop.
Single-session locking: prevent two visitors from opening competing conversations on the same physical kiosk.
Explicit reset: when a session ends, fully disconnect the media transport and create a fresh session rather than trying to rewind state in place.
Network recovery: automatically reconnect once, but fall back to a visible error state if the session is truly broken.
Another subtle issue is lip sync quality. If your agent response is generated in multiple chunks or if you stitch together separate audio segments, the avatar can look slightly off even when the audio sounds fine. For kiosk use, it is usually better to prefer a media path that keeps the avatar’s video generation tightly coupled to the speech output, rather than bolting together independent video and TTS pipelines.
Where Protoface fits in this stack
For developers using a voice-agent backend, the cleanest integration is often server-side: create or manage the avatar session from FastAPI, then hand the browser an ephemeral connection handle. You can do that via the REST API or the Python SDK, depending on whether you prefer direct HTTP calls or an application-level client.
Example REST call from the backend:
And a minimal Python SDK-style pattern:
Exact field names and response shapes are documented in the API reference, but the operational principle is what matters here: keep keys on the server, create sessions programmatically, and let the kiosk browser consume only short-lived session data. For a quick path from prototype to working demo, the public docs at docs.protoface.com are the right place to confirm the current request/response contract.
Deployment notes for kiosk environments
Kiosks fail in predictable ways, so design for them up front.
Preload the app: render the React shell immediately, then connect the media session asynchronously.
Show connection state: users should know whether the avatar is listening, speaking, or reconnecting.
Use a watchdog: if the app becomes unresponsive, refresh the browser process or recycle the session.
Keep the backend stateless when possible: session state can live in the provider and your own datastore, not in process memory.
If you are starting from a voice agent rather than a blank slate, there is also a useful plugin path for LiveKit-based stacks, documented in the relevant repository and package listings on GitHub and PyPI. That route is appropriate when the voice agent already exists and you want to give it a synchronized face without rewriting the agent layer.
Conclusion
The basic recipe is straightforward: let React handle kiosk UX, let FastAPI own privileged session creation, and keep the avatar/media layer isolated from your public frontend. That gives you a maintainable boundary between presentation, auth, and realtime streaming, which is exactly what you want when the kiosk is expected to run all day with minimal supervision.
If you want to implement this with a managed realtime avatar layer, start by reading the docs, then wire up a backend session endpoint and a minimal React video surface. From there you can tune voice, instructions, idle behavior, and session policy for the specific kiosk experience you’re building.
