Voice Visualizations
When users talk to your agent instead of typing, AIVoiceGlow and AIVoiceWave give the agent a visual presence. AIVoiceGlow is a soft globe of drifting color, and AIVoiceWave is a single waveform line. Both show what the agent is doing through the way they move. Pass the agent's state:
AIVoiceGlow(
state = AIVoiceAgentState.Listening,
modifier = Modifier.size(320.dp),
)The voice agent visualizations are available from version 7.16.0 of the stream-chat-android-ai-compose artifact.
The Voice agent use case builds a whole voice screen around them: a call with your Stream Agents agent, the globe driven by the agent's state, and what was said written out as a conversation.
States
The state is an AIVoiceAgentState:
| State | The agent is... | Motion |
|---|---|---|
Idle |
Waiting to be spoken to | A calm, barely-there shimmer |
Connecting |
Not ready yet | A loader sweep with a pulsing breath |
Listening |
Hearing the user | A gentle expand and contract |
Thinking |
Working on an answer | A slow turn for the glow, a travelling wave for the line |
Speaking |
Talking | Strong motion that ebbs and pauses like speech |
The states change only the motion, never the colors, and a change of state eases from one motion into the next. Set the state straight from your agent's events, without animating it yourself.
Neither view has a size of its own: each draws in the middle of the space it's given, on a transparent background. The globe is about half as wide as the shorter side of that space, and the line about 80% as wide, so give either view a square, or the wave a wide box.
Parameters
Both views take the same parameters:
state- what the agent is doing.colors- one to five colors to paint it with. Colors past the fifth are ignored, and an empty list falls back to the default. See Colors.expressivity- how lively the motion is, from 0 (nearly still) through 1 (the default) to 2 (twice as animated). It never changes the colors.audioLevel- a function returning the live level of the voice being heard, from 0 to 1, ornull. Leave it out, and the view animates speech on its own.
AIVoiceGlow also takes a rendering, described in Touch, accessibility and rendering.
Follow the real voice
To drive the motion from the agent's voice, pass audioLevel. The view calls it once a frame, so reading a value that changes many times a second recomposes nothing. Update the level at least 15 times a second for the motion to follow the words. The glow follows the level closely, while the wave smooths it, so the line swells with each phrase rather than jittering with every syllable.
With Stream Video, that's the audioLevel of the agent as a participant of the call:
val participants by call.state.remoteParticipants.collectAsState()
val agent = participants.firstOrNull { it.userId.value == agentId }
AIVoiceWave(
state = agentState,
modifier = Modifier.size(320.dp),
colors = listOf(Color(0xFF7C4DFF), Color(0xFFFF4081), Color(0xFF1DE9B6)),
expressivity = 1.4f,
audioLevel = { agent?.audioLevel?.value },
)A participant's audioLevel updates a few times a second, which moves the view with each phrase. For motion that follows every word, measure the agent's audio track yourself, as the Android assistant sample does.
Colors
The default palette, AIVoiceVisualizationDefaults.colors(), is the theme's primary color. To use the same palette everywhere, keep it in one place and pass it as colors to each view:
val VoicePalette: List<Color> = listOf(Color(0xFF3F51B5), Color(0xFFE91E63), Color(0xFF009688))
AIVoiceGlow(state = agentState, colors = VoicePalette, modifier = Modifier.size(320.dp))Touch, accessibility and rendering
- Tapping the globe or the line bounces it, and the shader glow and the wave also ripple from the touch and under a hovering mouse or stylus. The views don't consume the touches, so a
clickableon them or on a parent still works. - With the system's animations turned off, the views barely move and ignore touches.
- TalkBack reads either view as "Voice agent", with the state as its value, such as "Listening". These texts are the
stream_ai_compose_voice_*string resources, which you can override in your app'sstrings.xml. AIVoiceGlowtakes arendering. The default,AIVoiceGlowRendering.Auto, draws with a shader on Android 13 and later, unless the device is in battery saver, and with a few Canvas gradients otherwise.ShaderandCanvasforce one or the other (Shaderstill falls back to Canvas below Android 13). The Canvas glow is cheaper, but has no marbling or ripples. The wave always draws on a Canvas.