All articles

The spinner is a lie

Loading patterns built for deterministic systems fail completely in agentic contexts — silence, loops, and dead ends have no visual language yet, and that's a design problem nobody's solving.

A spinner means "I'm working." Your agent isn't working. It's thinking, or it's looping, or it's stuck, or it already failed and hasn't told you. A spinner says all four with the same animation. That's not feedback. That's a shrug.

A spinner animation with four contradictory labels — thinking, looping, stuck, already failed — showing why one animation can't communicate four states

The spinner was designed for a world that doesn't exist anymore. Request goes out, response comes back, the gap is short and predictable. You spin for 400ms and the user never thinks about it. That contract held because the system was deterministic: one input, one path, one bounded wait.

Agents broke the contract. A single prompt can fan out into a dozen tool calls, run for minutes, double back on itself, and partially fail in the middle. The wait isn't 400ms — it's four minutes. And during those four minutes the spinner tells the user exactly nothing. Is it reasoning? Retrying? Hung? Already done with step three and stalled on step four? Same spin. The animation that used to mean "don't worry" now means "I have no idea, and neither do you."

Here's the cost. A 15-minute gap in communication breaks trust faster than an actual error does. When an agent goes quiet, users don't assume it's working hard — they assume it's broken, and they bail. The worst failure mode isn't the loud crash. It's the silent one: the agent looks busy, returns something, and nothing flagged that it went sideways three steps ago. Silence reads as competence right up until it reads as betrayal.

The deeper problem is that we have no visual language for the states agents actually produce. We have spinners for "waiting" and error toasts for "broke." We have nothing for silence (running but mute), loops (working but going nowhere), and dead ends (failed but still animating). Those three states are where agent UX lives now, and most products render all of them as a spinner. You can't fix what you refuse to name.

What does getting it right look like? Stop showing that the agent is working and start showing what it's doing. The pattern emerging across teams that take this seriously is a layered status model, matched to duration:

  • Match the indicator to the wait. Sub-10-second task: a spinner is fine, that contract still holds. Multi-minute task: you need a progress panel that shows the plan and checks off steps. Multi-hour task: you need a dashboard the user can leave and return to. Using a spinner for a four-minute job is a category error.
  • Show the plan, not the activity. "Pulled 3 sources → drafting summary (in progress) → awaiting your review" tells the user where they are. A spinner tells them to wait and hope. A plan that lights up step by step turns a black box into a flight tracker.
A vertical checklist showing an agent's plan with steps completing in real time — search and read file done, draft in progress, review pending
  • Give silence, loops, and dead ends their own visuals. Silence gets a heartbeat — a token counter, a "still working, 0:47 elapsed," anything that proves the process is alive. A loop gets caught and surfaced: "retried search 3x, no new results — want me to try a different approach?" A dead end gets an honest stop, not a spinner that runs forever. Each of these is a design decision you're currently making by default.
  • Stream partial results. Don't make the user stare at nothing for four minutes and then dump everything at once. Render the work as it lands. A half-finished answer that's visibly growing beats a perfect answer behind a wall of spin.
  • Make failure legible, fast. If the agent communicates uncertainty and stops cleanly, users recalibrate and try again. If it hides the failure behind motion, they churn and don't tell you why. Transparency isn't a nicety here — it's the cheapest trust-preservation mechanism you have.

The reframe: in an agent product, the loading state isn't a placeholder you tolerate until the real UI loads. It is the UI for most of the interaction, because most of the interaction is waiting. Treat it like a placeholder and your product feels broken even when the model is perfect. Treat it like the product and a mediocre model feels controllable — which, to a user, is most of what "good" means.

Try this on your own product. Give your agent a task that takes at least three steps and a couple of minutes. Watch the screen the whole time and answer three questions: (1) At any given second, can I tell whether it's thinking, retrying, or stuck? (2) If it hit a loop or a dead end right now, would the screen look any different than if it were working fine? (3) If I walked away for two minutes, could I rejoin and instantly know where it is? If the answer to any of these is "it's just spinning," you don't have a loading state. You have a lie with nice easing.