Journal

AI Agent Thinking Animation in SwiftUI: Honest Motion

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.

AI Agent Thinking Animation in SwiftUI: Honest Motion: a glass app tile showing the VP0 logo on a pink and blue gradient

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: animate only what is true.

How does the thinking indicator work?

Pulsing dots in SwiftUI, alive between request-sent and first-token, replaced the instant text arrives:

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 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 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:

SituationRenderWhyVerdict
Tokens streamingAppend at arrival, micro-batchedTrue typing; the cadence is realThe default; smooth, honest, free
Complete text availableShow it, with one entrance transitionThe wait is over; respect the readerSpend motion on the entrance, not keystrokes
Code blocks streamingAppend into the styled block liveSame truth, monospace costumeSyntax highlight on completion, not per token
Tool running, no text yetNamed state, not dotsThe agent is doing, not thinkingSee 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 formalize, and the same honest-progress stance as the image-generation queue. Waiting-state craft for the surrounding UI, what shows where content will land, belongs to the skeleton guide; 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 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, 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.

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.

What VP0 builders also ask

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.

Part of the Native Apple & SwiftUI: The iOS Ecosystem hub. Browse all VP0 topics →

Keep reading

Skeleton Loading Screen in SwiftUI: Template & Rules: a glass iPhone app-grid icon on a mint and teal gradient
Guides 5 min read

Skeleton Loading Screen in SwiftUI: Template & Rules

How to build skeleton loading screens in SwiftUI: the redacted modifier, a shimmer overlay, layout-matched placeholders, and when not to skeleton at all.

Lawrence Arya · June 5, 2026
Image Outpainting Brush Tool UI in SwiftUI: the App Store logo on a glass tile over a blue gradient with bubbles
Guides 6 min read

Image Outpainting Brush Tool UI in SwiftUI

The model generates; the app builds the spec. A PencilKit mask layer, feathered edges as the quality lever, and cost shown before every tap.

Lawrence Arya · June 7, 2026
Midjourney-Style Image Grid Selector UI in SwiftUI: a glass photo icon surrounded by chat, music, heart, camera and shopping app icons on a pastel gradient
Guides 6 min read

Midjourney-Style Image Grid Selector UI in SwiftUI

The grid is the decision screen: each cell a state machine, tap-to-focus selection, first-class re-roll, and honesty about cost, wait, and failures.

Lawrence Arya · June 7, 2026
Pet Breed Identifier Camera AI UI in SwiftUI: a glass iPhone app-grid icon on a mint and teal gradient
Guides 5 min read

Pet Breed Identifier Camera AI UI in SwiftUI

The model classifies; the app presents uncertainty honestly. On-device Core ML, top-three confidences, capture coaching, and a fun estimate, not a pedigree.

Lawrence Arya · June 7, 2026
AI Essay Grader Feedback Highlight UI: Teacher in the Loop: a glass app tile showing the VP0 logo on a pink and blue gradient
Guides 5 min read

AI Essay Grader Feedback Highlight UI: Teacher in the Loop

Design an AI essay grading UI: span-anchored highlights, rubric-mapped feedback categories, the teacher approval pass, and student views built for revision.

Lawrence Arya · June 5, 2026
App Onboarding Wizard Boilerplate: Earn Every Step: the App Store logo on a glass tile over a blue gradient with bubbles
Guides 4 min read

App Onboarding Wizard Boilerplate: Earn Every Step

An onboarding wizard boilerplate built honestly: steps that earn their existence, skip as a first-class affordance, in-context permissions, and a payoff landing.

Lawrence Arya · June 5, 2026