LatencyBreakdown
StableShows where the time went in a voice reply or a whole call, names the slowest stage in words and lists the turns that were too slow.
Also known as latency, timing breakdown, performance
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
One reply
Response time
Your team's target: 800 ms
This reply
990 ms
Over target by 190 ms
Most time: model, 520 ms (53% of the reply)
| Stage | Time | Share |
|---|---|---|
| End of speech | 180 ms | 18% |
| Speech to text | 90 ms | 9% |
| Model (slowest) | 520 ms | 53% |
| Voice | 140 ms | 14% |
| Network | 60 ms | 6% |
<LatencyBreakdown
mode="turn"
totalMs={990}
targetMs={800}
stages={[
{ key: "endpointing", ms: 180 },
{ key: "stt", ms: 90 },
{ key: "llm", ms: 520 },
{ key: "tts", ms: 140 },
{ key: "transport", ms: 60 },
]}
/>The model took most of the time, so it is named first. The reply was over the team's target.
When to use it
Helps the people who run a voice agent see why it felt slow, and which part to fix first, without reading a chart.
Made of: Heading and target · Total · Percentiles · Slowest stage line · Stacked bar · Stage table · Slow turns list
Use it for
- The detail page of one call in an agent dashboard.
- Checking one reply that felt slow while testing an agent.
- Comparing a change to the model or voice against your team's target.
Not for
- Showing people on the call that the assistant is working: use VoicePanel.
- A single number on a dashboard: use StatTile.
- Response times over days or weeks: use LineChart.
Do and avoid
Response time
Your team's target: 800 ms
This reply
990 ms
Over target by 190 ms
Most time: model, 520 ms (53% of the reply)
| Stage | Time | Share |
|---|---|---|
| End of speech | 180 ms | 18% |
| Speech to text | 90 ms | 9% |
| Model (slowest) | 520 ms | 53% |
| Voice | 140 ms | 14% |
| Network | 60 ms | 6% |
Reply time
990 ms
Do
- Name the slowest stage. A total alone does not say what to fix.
- Measure time to the first real answer, so a filler word cannot hide a slow reply.
- Link slow turns to the recording, so people can hear what happened.
Avoid
- Do not colour stages lime. Lime marks the AI, and this is about time.
- Do not use amber for a slow turn. Nobody has to act on one call.
- Do not show the target as a law. It is a goal your team set.
On a phone
- On a phone the stacked bar is hidden and each table row shows its own small bar, so the split reads as a list.
- Slow turns are full-width buttons with a 44px tap area on touch screens.
- Padding shrinks a step below 640px wide so the numbers keep their room.
Seen in products
- Retell shows end-to-end P50, P90 and P99 per call, split into speech recognition, model, voice, knowledge base and speech-to-speech.
- Vapi shows turn count, average turn latency and a split by transcriber, endpointing, model, voice and transport, with a table per turn.
- Twig suggests tracking time to the first real answer, so filler words cannot hide a slow reply.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| moderequired | "turn" | "call" | none | One reply, or the whole call with medians, percentiles and turns. |
| stagesrequired | { key: "endpointing" | "stt" | "llm" | "tools" | "tts" | "transport" | "s2s"; ms: number }[] | none | Time per stage in milliseconds, in the order they happen. For a call, pass medians. |
| totalMsrequired | number | none | Time from the end of the person's speech to the first sound of the reply. |
| percentiles | { p50: number; p90: number; p99?: number } | none | Spread of reply times across the call. |
| targetMs | number | none | Your team's goal. Shows over or within target, and lists slow turns in call mode. |
| turns | { id: string; label?: string; totalMs: number; stages: LatencyStage[] }[] | none | Every turn in the call. Those over the target are listed with their slowest stage. |
| onTurnSelect | (id: string) => void | none | Makes slow turns buttons. Use it to seek the recording. |
| title | string | none | Heading. Defaults to Response time. |
| stageLabels | Partial<Record<LatencyStageKey, string>> | none | Your own names for the stages. |
| className | string | none | Extra classes for the card. |
"Show code under the preview gives the code for the example on the stage."
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- within target
- The total is at or under the team's target: the total reads in ink with the words Within target.
- over target
- The total is over the target: it says Over target by and the number of milliseconds, in words, never by colour alone.
- slow turns
- In call mode, turns over the target are listed as buttons; pressing one calls onTurnSelect so the app can jump to that moment.
- no slow turns
- In call mode with every turn inside the target, a plain line says so instead of an empty list.
- phone
- Under 640px the stacked bar is hidden and each row of the table gets its own small bar, so nothing is squeezed.
Tokens
Change one of these and every part that uses it follows.
--chart-1--chart-2--chart-3--chart-4--chart-5--chart-neutral--chart-seq-4--surface--surface-sunken--border--danger-fg--radius-card--radius-control--dur-slow
Built in
- The stacked bar is hidden from screen readers. A real table follows with every stage, its time in ms and its share.
- The slowest stage is written in a sentence and marked as slowest in the table, not only shown by colour.
- Over and within target are written in words.
- Slow turns are buttons with their name, time and slowest stage in the label.
- The bar grows in once and stops; under reduced motion it is drawn still.
What you need to do
- Pass times in milliseconds that your code measured. Do not round them into words yourself.
- If you pass onTurnSelect, make it move your audio player and say where it moved, as the example does.
- Set targetMs from your team's own goal and call it that. Do not present it as an industry rule.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide