Pipecat UI
Components

Bot Audio

Bot audio output with a shared volume store, mute toggle, slider, and popover control.

View in Storybook
Bot volume
80%

Installation

pnpm dlx shadcn@latest add @pipecat/bot-audio

Usage

Mount BotAudioOutput once in place of PipecatClientAudio — it plays the bot's audio track and binds its volume to a shared store. Then drop controls anywhere:

import {
  BotAudioOutput,
  BotAudioControl,
  BotVolumeSlider,
  useBotAudio,
} from "@/components/pipecat/bot-audio";

<PipecatClientProvider client={client}>
  <BotAudioOutput />
  <BotAudioControl label="Bot volume" /> {/* popover button */}
  <BotVolumeSlider /> {/* inline slider */}
  <BotVolumeSlider orientation="vertical" />
</PipecatClientProvider>;

All controls stay in sync through the useBotAudio zustand store (volume, setVolume) — no prop drilling. The popover control disables itself while disconnected. Speaker routing follows the selected output device via setSinkId.

The slider is a mute toggle, the track, and a percent readout; the mute button drops the volume to 0 and restores the last audible level when clicked again. The popover shows the same slider without its label.

Mobile Safari: unlock inside the tap

iOS Safari only allows playback that starts inside a user gesture. The bot track arrives a few seconds after the Connect tap, so autoPlay is refused and the page stays silent. Call unlockBotAudio synchronously in the tap handler; it calls play() on the output element (silent, since no track is attached yet) so later playback of the bot track counts as user-activated. The Console block does this for you:

import { unlockBotAudio } from "@/components/pipecat/bot-audio";

<ConnectButton
  onConnect={() => {
    unlockBotAudio();
    void connect();
  }}
  onDisconnect={disconnect}
/>;

It is safe to call on every platform and is a no-op before BotAudioOutput mounts.

Key props

PropTypeDescription
volume / onVolumeChangenumber / (v: number) => voidControlled volume on the views; the connected exports wire the store
labelstringLabel text; doubles as the slider's accessible name
noLabel / noPercent / noMuteButtonbooleanHide the label, percent readout, or mute toggle
orientation + Slider propsBase UI SliderEverything else on BotVolumeSliderView passes through to the slider primitive (orientation, disabled, step, …)
sliderPropsPartial<BotVolumeSliderViewProps>On the popover control: overrides merged onto the embedded slider
variant / sizeButton propsPopover trigger styling; icon sizes (icon, icon-sm, …) hide the label

On this page