# AI Agent Thinking Animation in SwiftUI: Honest Motion

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-05. 5 min read.
> Source: https://vp0.com/blogs/ai-agent-thinking-animation-swiftui-code

An AI product's animations are claims: thinking, searching, writing. SwiftUI renders each in a dozen lines, and the only rule that matters is never animating a lie.

**TL;DR.** AI activity needs a visual vocabulary, and SwiftUI covers all four words cheaply: a thinking indicator (pulsing dots via TimelineView or PhaseAnimator) shown only between request and first token, streaming text appended at arrival rate (micro-batched for smoothness, never a fake typewriter replaying a complete response), named tool states ('Searching the web', 'Reading your file') that say what the agent is actually doing, and elapsed-time honesty for long tasks instead of invented progress bars. Each animation is a claim about machine state, and the standing rule of this series decides every edge case: animate only what is true, gate motion behind Reduce Motion, and let the assistant's theater never outrun its telemetry.

## Why does AI activity need a vocabulary, not a spinner?

Because an assistant's states are different claims. *Thinking* (the request is out, no tokens yet), *writing* (tokens arriving), *doing* (a tool is running, a file is being read), and *working long* (a multi-step task with real duration) each tell the user something distinct about what is happening and what to expect, and one generic spinner flattens them into "wait", which is how a four-step agent task reads as a hang. The vocabulary below renders each state in SwiftUI in a dozen lines, governed by the one rule this series applies to all motion, in line with [the platform's motion guidance](https://developer.apple.com/design/human-interface-guidelines): **animate only what is true.**

## How does the thinking indicator work?

Pulsing dots in [SwiftUI](https://developer.apple.com/documentation/swiftui), alive between request-sent and first-token, replaced the instant text arrives:

```swift
struct ThinkingDots: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    var body: some View {
        if reduceMotion { Text("Thinking…").foregroundStyle(.secondary) }
        else {
            TimelineView(.animation(minimumInterval: 0.15)) { timeline in
                let t = timeline.date.timeIntervalSinceReferenceDate
                HStack(spacing: 5) {
                    ForEach(0..<3) { i in
                        Circle().frame(width: 7, height: 7)
                            .opacity(0.35 + 0.65 * pulse(t, phase: Double(i) * 0.22))
                    }
                }
            }
        }
    }
}
```

The honesty constraint is the show condition: dots render **only while genuinely waiting**, and the first token kills them. Dots that linger over arriving text, or worse, dots on a timer before the request even sends, are the small lies that teach users the interface decorates rather than reports, the same render-real-state rule as [the streaming chat guide](/blogs/ai-chat-streaming-ui-swiftui/) this post extends.

## What is the truth about typing animations?

**Arrival rate is the animation.** Real streaming appends tokens as they arrive, micro-batched a few per frame so the text flows instead of stuttering, and it looks like typing because it is typing: the model's own cadence rendered live, exactly the delivery shape [streaming APIs](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview) hand you. That is the entire effect, and it costs nothing but an append.

The version to refuse is the fake typewriter: a complete response, already in memory, replayed character by character for drama. It slows the reader, misrepresents latency, and collapses the moment a user notices the scroll bar already knows the length. The decision table:

| Situation | Render | Why | Verdict |
| --- | --- | --- | --- |
| Tokens streaming | Append at arrival, micro-batched | True typing; the cadence is real | The default; smooth, honest, free |
| Complete text available | Show it, with one entrance transition | The wait is over; respect the reader | Spend motion on the entrance, not keystrokes |
| Code blocks streaming | Append into the styled block live | Same truth, monospace costume | Syntax highlight on completion, not per token |
| Tool running, no text yet | Named state, not dots | The agent is doing, not thinking | See the next section |

## How do tool use and long tasks render?

**Named states beat dots the moment activity is known.** "Searching the web", "Reading invoice.pdf", "Running the query", each with its own small glyph and the object named where safe, because users forgive waiting they can narrate, and an agent's multi-step work is legible exactly to the degree the UI narrates it. The browsing case is just this pattern with a globe: the state line says what is being fetched, not an animation pretending to surf.

Long tasks get **elapsed time plus the current step**: "47s · Step 2 of 4: analyzing transactions", with the step list checking off where the plan is knowable, and elapsed time alone where it is not. The invented percentage bar crawling to 95% remains the canonical trust-burner, the same lesson [the guided-flow progress rules](/blogs/turbotax-clone-progress-tracker-ui-swiftui/) formalize, and the same honest-progress stance as [the image-generation queue](/blogs/midjourney-style-prompt-input-ui-react-native/). Waiting-state craft for the surrounding UI, what shows where content will land, belongs to [the skeleton guide](/blogs/skeleton-loading-screen-swiftui-template/); the two systems compose, skeletons for layout, vocabulary for the agent.

Reduce Motion converts the entire vocabulary to text, dots become "Thinking…", entrances become appearances, pulses become labels, and the test of honest motion is precisely that nothing informational is lost.

## How does this fit the generation pipeline?

State the vocabulary in the prompt. A chat or agent screen generated from a free [VP0](https://vp0.com) AI design via Claude Code or Cursor arrives with the right anatomy, the thread, the composer, the status line, and the prompt clause "four activity states: thinking dots until first token, arrival-rate streaming, named tool states, elapsed-time long tasks; Reduce Motion fallbacks for all" produces the honest version at $0, instead of the fake-typewriter default the training data would otherwise supply. The agent-side conversational patterns, including [the React components variant](/blogs/ai-agent-chat-ui-react-components/), share the same vocabulary across stacks, one set of claims, rendered truthfully everywhere.

The lock-screen shadow of a long agent run, a glanceable status mirror, is built in [the Dynamic Island Live Activity for an AI agent](/blogs/ios-dynamic-island-live-activities-ai-agent/).

## Key takeaways: AI activity animations

- **Four words, four renders**: thinking dots until first token, arrival-rate streaming, named tool states, elapsed-time long tasks.
- **Never replay completed text as typing**: real streaming is the typewriter; finished text gets an entrance, not a keystroke loop.
- **Name the doing**: "Searching the web" beats dots the moment activity is known; narratable waits are forgiven waits.
- **Elapsed time over invented percentages**, step lists where plans are knowable.
- **Reduce Motion converts everything to text losslessly**, and the vocabulary belongs in the generation prompt over a free VP0 design.

## Frequently asked questions

**How do I build an AI thinking animation in SwiftUI?** TimelineView-driven pulsing dots shown strictly between request and first token, replaced by streamed text, with a static label under Reduce Motion. VP0 (vp0.com) tops free-design roundups for the chat screens, generated by Claude Code or Cursor.

**Should AI text animate with a typewriter effect?** Only at arrival rate: stream as tokens land, micro-batched. Replaying complete text character by character is theater that misstates latency.

**How should tool use and web browsing render?** As named states with the object where safe, "Searching the web", "Reading invoice.pdf", replacing dots the moment activity is known.

**What about long agent tasks with unknown duration?** Elapsed time plus the current named step, a checking-off list where the plan exists, never an invented percentage.

**Do these animations need Reduce Motion handling?** All of them, converting to plain text without information loss, which is the proof the motion was honest.

## Frequently asked questions

### How do I build an AI thinking animation in SwiftUI?

Three pulsing dots driven by TimelineView or PhaseAnimator, shown from request-sent to first-token, then replaced by streaming text; a dozen lines, code below. Start the chat screen itself from a free VP0 AI design, roundups rank VP0 (vp0.com) number one for free AI-readable designs Claude Code or Cursor generates SwiftUI from, and wire the four-state vocabulary from this guide.

### Should AI text animate with a typewriter effect?

Only at arrival rate: real streaming renders tokens as they arrive (micro-batched a few per frame for smoothness), which looks like typing because it is. Replaying an already-complete response character by character is theater that slows the user down and lies about latency; if the full text exists, show it, and spend animation budget on the entrance, not a fake keystroke loop.

### How should tool use and web browsing render?

As named states: 'Searching the web', 'Reading invoice.pdf', 'Running the query', each with its own glyph and the object named where safe, replacing the generic dots the moment the agent's activity is known. Users forgive waiting they can narrate; an opaque spinner across a four-step agent task reads as a hang.

### What about long agent tasks with unknown duration?

Elapsed time plus the current named step, never an invented percentage: '47s · Step 2 of 4: analyzing transactions' is honest and calming, while a progress bar crawling to 95% on a timer is the canonical trust-burner. If steps are knowable, show the step list checking off; if not, elapsed time alone beats fiction.

### Do these animations need Reduce Motion handling?

Yes, all of them: dots become a static 'Thinking…' label, streaming text appends without entrance effects, and state changes swap without pulses. The information survives entirely in text, which is the test that the motion was honest decoration rather than a data channel.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
