How to Show What AI Thinks: Patterns of Agent Progress Animation
Main chat
A chat for vibe coders: news, guides, live cases, marketplace, and finding executors.
Spinner is designed for operations with a length of 0.5-2 seconds. The LLM query takes 4-15 seconds. Multi-step task of the agent - up to a few minutes. During this time, the spinner ceases to work as an indicator of progress and becomes a source of anxiety: “something is happening?”, “the system is hovering?”, “is it worth waiting?”
Designing AI interfaces requires a new motion dictionary.
Why old patterns don’t work
A traditional bootloader assumes a binary state: either finished or not. The spinner button is the result.
AI processing is different. This is a series of steps with different durations, with intermediate results, with the possibility of a partial response that already carries value. And most importantly, the user does not know how long to wait. There is no progress bar that goes up to 100%. This is a fundamentally different type of uncertainty.
Three problems that correct AI progress animation solves:
Worry. "Is the system working or hovering?" the spinner doesn't respond. Streaming output or progress log.
Loss of control. The user does not see what is happening. Expandable process details provide control without interruption.
**Unpredictable. **Unknown when it will end. The specific steps in progress log make the expectation meaningful.
Five patterns
Streaming output is the most efficient
Text appears token by token as it is generated. The user sees the progress in real time and starts reading before the answer is over.
**Why it works: There is no sense of expectation. We see the process. You can evaluate the direction of the response before it is completed – and stop if necessary.
- Animation: * Quiet. Just the emergence of new symbols. Cursor-blink is not required, typing indicator is not required.
// React: accumulation of tokens in the state
const [text, setText] = useState('')
// When receiving each token:
setText(prev => prev + token)
// Rendering with autoscroll to the bottom
useEffect(() => {
containerRef.current?.scrollTo(0, containerRef.current.scrollHeight)
}, [text]
Progress log - narrative instead of spinner
A sequence of short messages about what the agent is doing right now. Not status is a narrative.
Analyzing the request...
I am looking for relevant data from 3 sources.
I found 12 matches, filtered by date.
Forming an answer...
**Why it works: * The user understands that the system is active and what it is doing. Expectation becomes meaningful - it has a structure.
Animation: Each line appears fade-in (opacity 0→1, translateY 4→0, 200ms). The current step is with a pulsating point. Completed with a tick.
@keyframes logEntry {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
.log-item { animation: logEntry 0.2s ease-out forwards; }
.log-item.active::after {
content: '●';
animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
Skeleton with gradual filling
The structure of the response is shown in advance as a skeleton - filled in as it is ready. For structured content: cards, tables, lists.
**Why it works: * The user sees what comes. No emptiness. There is no anxiety about the unknown - the shape of the result is already known.
.skeleton {
background: linear-gradient(
90deg,
var(--color-surface) 25%,
var(--color-surface-hover) 50%,
var(--color-surface) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
}
Animated thinking indicator
Three points with typing-animation is the standard of messengers. It works for conversational AI with a short response time (up to 3-4 seconds).
After 5 seconds, it begins to look like a freeze. Use only as a first step - then switch to progress log or streaming.
Expandable process details
The agent works – the user sees a short status. By click - a detailed log of each step. Progressive disclosure: Details are available but not imposed.
Pattern: collapsed by default with the "show details" icon. Deployed through height: 0 → auto (interpolate-size: allow-keywords in CSS or through JavaScript).
What's not working
**Determinate progress bar without real progress. If the bar moves regardless of the actual state, the user feels it. Trust is falling fast.
Percentage without context. 47% accomplished. Without understanding what counts, the number is meaningless and creates a false sense of predictability.
Spinner longer than 4 seconds as the only indicator. After 4 seconds, the user does not know whether to wait any longer to close the page. We need an additional signal.
Progress animation without a stop button. An agent’s multi-step task may go wrong. The user should be able to stop at any time.
Prompt for Codex/Claude Code
Implement the AI response progress pattern for [component].
Patterns depending on the type of response:
1. Text answer < 5 seconds: streaming output (accumulation of tokens in the state)
2. Text Answer > 5 seconds: typing indicator (3 seconds) → streaming
3. Multi-step task: progress log with specific steps
4. Structured content: skeleton with shimmer → gradual filling
General requirements:
The stop button is visible as long as the agent is working.
- After 4 seconds unchanged: show text status "still working..."
When streaming: autoscroll to the last token
In case of error: an explicit message with the "Try again" button
Short answer (< 5 seconds):
Streaming output or typing indicator
Long answer (> 5 seconds):
Progress log with specific steps
The stop button is always visible
Structured content:
Skeleton with shimmer → filling
Any AI processing:
● No spinner as the only indicator > 3 seconds
● After 4 seconds: text status "still working"
● There is a cancel/stop mechanism.
● In case of error: specific message + retry
Why is it more important than it seems
In 2023, most AI interfaces featured a spinner while the model was thinking. Users are used to it. Waited. Sometimes they updated the page, not knowing if it was hanging.
In 2026, expectations changed. Users know that LLM can think seconds and minutes. They know that you can see the process through streaming. A spinner without progress is not perceived as “normal” but as “this product has not been taken care of.”.
Motion for AI progress is no longer a “pleasant addition.” This is a basic expectation that affects the credibility of the product.
Different patterns for different types of AI tasks
Not all AI tasks are the same in terms of UX progress.
Synchronous requests (< 3 seconds). The user clicked → wait → got the result. Typing indicator or skeleton is sufficient. Streaming is not necessary – the answer comes quickly.
Asynchronous requests (3-15 seconds). The user clicked → sees the process → receives the result as it is ready. Streaming for text, skeleton for structure. The stop button is mandatory.
Agent Tasks (> 15 seconds, maybe minutes). The user gave the task → the agent works several steps → the result. Progress log with specific steps. Expandable details. Notification when ready (if the user has left to do otherwise).
One pattern for all three types is a mistake.
When to show the stop button
Always. No exceptions.
Any AI process can go wrong: wrong query interpretation, endless loop, too long. The user should be able to stop at any time.
The stop button should be:
- Visible all the time while the agent is working (not hidden behind the click)
- Large enough for confident pressing (minimum 44×44px)
- Destructive in appearance - but not intimidating (not a red button with a danger icon)
After clicking “Stop” – show what you have done, not just clean the screen.
What happens when the answer comes in parts
Many AI systems return the result not in one piece but in parts as they are ready. The first card is ready in 2 seconds, the second in 4, the third in 7.
Proper pattern: Show each piece as it is ready, with a smooth appearance. Skeleton for those who are not ready yet. Don’t wait until everything is ready, it’s a waste of perception time.
Example: a dashboard with four metrics. Each is downloaded independently. Do not show the skeleton for 7 seconds and then all four at once - show each as ready. The user is already reading the first while the rest are downloaded.
Accessibility: Progress Animations and Assistive Technology
Screen readers don't see animated progress. If the progress log is updated visually but without ARIA announcements, the user with the screen reader does not know what is happening.
Minimum solution: aria-live="polite" on the progress log container. Each new message will be delivered automatically.
<div aria-live="polite" aria-label="Статус выполнения задачи">
<p>Анализирую запрос...</p>
</div>
For streaming output: aria-live="off" on a streaming container (each token is separately too noisy) + aria-live="polite" on the final result.
The Stop button should have a clear aria-label: Stop the task, not just Stop.