Skip to content

fix(agents-ui): respect reduced motion across audio visualizers - #1477

Open
mtb24 wants to merge 2 commits into
livekit:mainfrom
mtb24:feature/wave-reduced-motion
Open

mtb24 wants to merge 2 commits into
livekit:mainfrom
mtb24:feature/wave-reduced-motion

Conversation

@mtb24

@mtb24 mtb24 commented Oct 23, 2026 •

Copy link
Copy Markdown

Problem and behavior

Audio visualizers continue moving when the browser requests reduced motion. This revision covers Bar, Grid, Radial, Wave, and Aura with a shared text-state and playback-control approach.

  • With reduced motion enabled, a visualizer starts paused and shows its current state, such as Listening, Thinking, or Speaking.
  • Consumers can use useAgentAudioVisualizerMotion with AgentAudioVisualizerToggle to put Play/Pause controls where they want them. The visualizer takes a controlled paused prop; without one, it follows the browser preference.
  • Playback affects visualization only. No track or session mute, stop, disconnect, or restart APIs are called.
  • System-preference changes reset the shared hook's playback choice to the new preference. Choices are local to the hook instance.
  • AgentAudioVisualizerState, custom paused content, and button labels support localization and custom presentation.

Example composition:

const { paused, setPaused } = useAgentAudioVisualizerMotion();

return (
  <>
    <AgentAudioVisualizerWave state={state} paused={paused} />
    <AgentAudioVisualizerToggle paused={paused} onPausedChange={setPaused} />
  </>
);

The built-in animation and audio-analysis hooks are unmounted while paused. Motion animations and shader frame requests are cleaned up when playback stops. The grid retains hidden sizing cells to avoid changing height; custom grid children remain hidden for sizing and need to control any animations they own separately.

Storybook review

Each existing visualizer story file now has a ReducedMotion story. No microphone, room connection, or credentials are required.

  1. Open Agents UI → AgentAudioVisualizerBar / Grid / Radial / Wave / Aura → Reduced Motion.
  2. With reduced motion off, the story displays instructions to enable it in the browser developer tools or system settings.
  3. Enable prefers-reduced-motion: reduce without reloading. The visualization stops, text state appears, and the button reads Play animation.
  4. Play and pause the animation, change the agent state in Controls, and toggle the browser preference again.

The earlier wave-only static silhouettes have been replaced by this text alternative.

Paused text, explicit playback, and browser-setting instruction

Paused Thinking state and Play animation control

User-selected playback and Pause animation control

Instruction shown when reduced motion is disabled

Validation

  • 512 repository tests pass, including 354 agents-ui tests.
  • Coverage includes all five visualizers, default pause, explicit playback, live preference changes, mounted state updates, root ref/HTML props, custom labels, listener cleanup, hydration, animation cleanup, and shader-loop lifecycle.
  • Build, lint, formatting, configured API checks, and local shadcn registry build pass. Each visualizer's registry entry includes the shared motion dependency.
  • Native Chromium media emulation: all five stories show the instruction when off, stop to text when on, play on request, and return to text when paused. Keyboard playback works. The built story had no console errors.
  • Default 5×5 medium grid measures 72px high in both modes; the 9×9 story measures 136px in both. Compact text was checked with the longer Connection failed label.
  • Independent review identified sizing issues that were corrected and checked in the browser.
  • Standalone agents-ui TypeScript checking retains the same 12 baseline diagnostics, with none added. The configured core API check remains disabled upstream; the React API check passes.
  • Stories use no live audio session. Audio/session behavior was checked through the code paths and volume-analysis cleanup, not a live call.

Browser measurements

@vercel

vercel Bot commented Oct 23, 2026

Copy link
Copy Markdown

@mtb24 is attempting to deploy a commit to the LiveKit Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c6e2251

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@livekit/agents-ui Patch
@livekit/component-docs-storybook Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@thomasyuill-livekit

thomasyuill-livekit commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

I support the intention of this PR, but I'm unsure this is the right design solution for users of reduced motion.

https://developer.apple.com/help/app-store-connect/manage-app-accessibility/reduced-motion-evaluation-criteria/

Well designed animations can convey information intuitively, increasing the usability and understandability of your app. Removing such animations unnecessarily might make your app experience more confusing...
[...]
If your app includes [...] ongoing motion, consider stopping the motion based on the user’s setting, or providing a control to allow the user to stop the motion.

I believe we could design a general purpose solution across all audio visualizers.

What do you think if we allowed reduced-motion users to start and stop the visualizer based on their comfort level (default to stopped). When paused, we could present state through a text to reduce the ambiguity that is inherent of a static snapshot.

@mtb24

mtb24 commented Oct 23, 2026

Copy link
Copy Markdown
Author

I support the intention of this PR, but I'm unsure this is the right design solution for users of reduced motion.

https://developer.apple.com/help/app-store-connect/manage-app-accessibility/reduced-motion-evaluation-criteria/

Well designed animations can convey information intuitively, increasing the usability and understandability of your app. Removing such animations unnecessarily might make your app experience more confusing...
[...]
If your app includes [...] ongoing motion, consider stopping the motion based on the user’s setting, or providing a control to allow the user to stop the motion.

I believe we could design a general purpose solution across all audio visualizers.

What do you think if we allowed reduced-motion users to start and stop the visualizer based on their comfort level (default to stopped). When paused, we could present state through a text to reduce the ambiguity that is inherent of a static snapshot.

Hi Thomas, I understand and agree with your concern.

The explicit text would communicate that more clearly.

I propose starting paused when the reduced-motion preference is enabled, showing the current state as text, for example: “Listening,” “Thinking,” or “Speaking”, and maybe offering a Play/Pause animation control, which would only affect the visualizer. Audio and the conversation would continue normally.

A shared approach across the visualizers makes sense. My thinking is a shared motion preference hook and composable state/control UI, so consuming apps can place the controls where desired without each visualizer needing to roll its own behavior.

Would that be acceptable?
I’m happy to revise this PR around it and add Storybook examples covering the paused state, playback, and live preference changes.

  • Ken

Copy link
Copy Markdown
Contributor

yes, that would be great
I believe you only need to add a story for reduced-motion for each of the components.
In this story if reduced-motion is not enabled text should appear instructing the viewer to toggle this option in their browser/dev-tools

@mtb24 mtb24 changed the title fix(agents-ui): respect reduced motion in wave visualizer fix(agents-ui): respect reduced motion across audio visualizers Oct 26, 2026
@CLAassistant

CLAassistant commented Oct 26, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants