Header Logo

TypeScript Guide to Making Realtime AI Avatars Work with Keyboard-Only and Switch Access

TypeScript Guide to Making Realtime AI Avatars Work with Keyboard-Only and Switch Access

TypeScript tips for accessible realtime AI avatars: keyboard nav, switch access, focus management, ARIA, and streaming UI stability.

Introduction


Keyboard-only and switch access are the baseline interaction modes you need to respect if you want a realtime avatar to be usable by everyone, not just people with a mouse and a fast hand. The hard part is that avatars are often implemented as visually rich, highly interactive UI elements: they animate on speech, react to hover, take focus, and sometimes expose controls that are easy to break with custom event handling.


This post covers the practical mechanics: how to make a realtime AI avatar reachable by tab order, usable through switch scanning, compatible with assistive technology, and safe in a streaming/WebRTC-style application where state changes are happening continuously. By the end, you should be able to design an avatar experience that does not trap focus, does not depend on pointer events, and still feels responsive enough for a live conversational agent.


Start with interaction semantics, not animation


The first mistake is treating the avatar as the primary control surface. For accessibility, the avatar is usually presentation; the controls are what matter. If the avatar can be started, paused, muted, or reconfigured, those actions need real semantic controls that keyboard and switch users can reach.


Use native elements whenever possible:


  • <button> for start/stop, mute, retry, and mode toggles.

  • <input> or <textarea> for user prompts.

  • <dialog> or a well-managed region for settings.


Then decide what the avatar itself is. In many cases it should be a passive region with a label, not a control. If it is focusable at all, it should have a clear purpose such as “Avatar preview” or “Current agent speaking status.”


A useful rule: if the user cannot do anything meaningful by focusing the avatar, do not make it focusable. Let it be read-only visual output, and keep the actionable surface separate.


Keyboard access: tab order, focus management, and visible state


Keyboard-only users depend on predictable focus order. That sounds basic, but realtime UIs often violate it by conditionally rendering controls, moving them around as speech state changes, or using divs with click handlers instead of semantic buttons.


Keep these constraints in mind:


  1. Tab order should follow the visual order of primary actions.

  2. Never remove focus just because the avatar state changes.

  3. If you open a modal, trap focus inside it and restore focus when it closes.

  4. Do not use tabindex="0" on everything. Use it only when an element truly needs keyboard focus and does not already have semantic focusability.


For a live avatar, you also need to think about status changes. If the agent starts speaking, the user may need a non-visual indication that audio is active. Use an aria-live region sparingly for important state transitions such as “Agent connected,” “Listening,” “Speaking,” or “Connection lost.” Avoid dumping every animation tick into assistive technology.


<button type="button" aria-pressed="false">Mute avatar audio</button>
<video aria-label="AI avatar speaking" muted playsinline><

<button type="button" aria-pressed="false">Mute avatar audio</button>
<video aria-label="AI avatar speaking" muted playsinline><

<button type="button" aria-pressed="false">Mute avatar audio</button>
<video aria-label="AI avatar speaking" muted playsinline><


That example is intentionally simple. The important part is that the user can operate the experience without pointer input, and that the status updates are useful rather than noisy.


Switch access: design for sequential navigation and low action count


Switch access is not just “keyboard, but slower.” In practice, users may rely on scan-based navigation or a single-switch input that cycles through actionable items. That means the number of focusable targets matters a lot more than it does in a typical desktop UI.


For switch users, optimize the following:


  • Minimize focusable elements on the main screen. Every extra icon button increases scan time.

  • Prefer grouped actions under a single menu when the actions are secondary.

  • Keep the primary flow linear: prompt input, send, cancel, mute, maybe settings.

  • Avoid hidden hover-only controls; they do not exist for switch users.


This is where product design and engineering overlap. A beautiful avatar panel with six tiny overlays might look fine to a mouse user, but it can be a terrible experience for someone scanning through every focusable node. If you need richer controls, put them behind an explicit settings button so the top-level interaction surface stays small.


Also remember that a switch user may spend several seconds moving through focusable items. Time-sensitive UI changes can be disorienting. If the avatar auto-expands captions, opens a popover, or reorders controls mid-scan, you are making the interaction harder than it needs to be.


Real-time streaming adds a second accessibility problem: motion and state churn


Realtime avatar UIs usually involve WebRTC or another streaming transport for audio/video, plus an agent loop that reacts to user input. This means the screen can be updating even when the user is still navigating. Accessible design here is mostly about stability.


Two practical issues show up often:


1. Don’t tie focus to stream events. A common anti-pattern is resetting the component tree when the stream reconnects, which blows away focus and forces keyboard users back to the top of the page. Stream status should update in place. If the transport reconnects, keep the UI node stable and update state inside it.


2. Don’t overload assistive tech with rapid changes. Avatars may generate lip-sync frames dozens of times per second. None of that should be announced. Assistive technology should hear meaningful state changes only, not animation frames.


If you expose captions or transcript text, treat them as a separate region with its own update policy. A transcript can be useful for accessibility, but only if it is readable, not constantly reflowing, and not stealing focus from the user’s current task.


For motion-sensitive users, also consider reduced-motion preferences. The avatar can still speak and lip-sync, but avoid extra idle motions, flashing transitions, or aggressive zoom effects when prefers-reduced-motion is set.


How to think about the avatar component in React or similar frameworks


Most problems in this area come from treating the avatar as a leaf component that owns too much UI state. A better model is:


  • The transport layer owns connection state.

  • The conversation layer owns transcript, turn-taking, and audio state.

  • The avatar view renders speaking / listening / idle.

  • The control layer remains semantic and keyboard-first.


That separation keeps re-renders from touching focusable controls when the stream updates. It also makes it easier to test accessibility because the controls do not depend on video timing.


In React, for example, avoid using changing keys on the parent container for reconnects unless you truly want a full remount. Use stable IDs and update props. If you need to swap sources or reinitialize a stream, do it in an effect rather than by tearing down the whole UI.


function AvatarPanel({ status, onMute, onSend }) {
}
function AvatarPanel({ status, onMute, onSend }) {
}
function AvatarPanel({ status, onMute, onSend }) {
}


The code is ordinary on purpose. Accessible realtime UI usually comes from not doing anything clever enough to break the browser’s built-in behavior.


Where Protoface fits: keep the avatar transport separate from the accessible UI


This is the kind of problem a developer platform should help with: the avatar stream should be easy to attach, while your application keeps control of focus, labels, and input semantics. With a Protoface avatar inside a voice agent, the avatar can handle the synchronized talking face while your app remains responsible for the accessible interaction model.


If you are using the LiveKit path, the plugin is the practical integration point. The agent does the conversation work; the plugin adds the realtime face without forcing you to rebuild your UI around the video stream. See the implementation and examples in the plugin repository or the Pipecat integration guide if you are working in that stack.


The benefit from an accessibility standpoint is that you can keep the avatar as a visual output channel and continue to expose a standard form, buttons, captions, and status text for keyboard and switch users. In other words: let the avatar be the face, not the interface.


Testing checklist that catches the real failures


Before shipping, run through a few tests that are cheap and usually reveal the actual bugs:


  • Tab through the page from top to bottom. Can you reach every action without a mouse?

  • Can you operate the full conversation loop with only Enter, Space, arrows, and Escape?

  • Does reconnecting the stream preserve focus?

  • Do live speech updates avoid spamming screen readers?

  • Is there a clear visible focus ring on every interactive control?

  • If the avatar fails to load, does the page still work?


That last one matters a lot. Accessibility is not just about alternative input; it is also about graceful degradation. If the video face is unavailable, the agent should still be usable as a text or audio interface.


Conclusion


Making realtime AI avatars work with keyboard-only and switch access is mostly an exercise in discipline: use semantic controls, preserve focus, minimize interactive clutter, and treat the avatar stream as presentation rather than the primary UI. The realtime part does not change the accessibility fundamentals; it just makes the failure modes more frequent and more visible.


If you are building on Protoface, keep the avatar integration isolated from the accessible controls and follow the docs for the exact session and stream setup. Start with the documentation, then wire in the avatar through the surface that matches your stack. If you want to sanity-check the developer experience end-to-end, the quickstarts in the GitHub org are a good place to see the patterns in a runnable form.

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.