Designing the Wait: Async AI Generation UX for Products That Take Minutes
A photo goes in. A video comes out. In between, roughly ten minutes of nothing — and that gap is where AI products tend to lose people. I build Burning Bridges Studio , a single-purpose tool that turns one photo into a short party video. Generation takes about ten minutes, so waiting had to become

A photo goes in. A video comes out. In between, roughly ten minutes of nothing — and that gap is where AI products tend to lose people.
I build Burning Bridges Studio, a single-purpose tool that turns one photo into a short party video. Generation takes about ten minutes, so waiting had to become a designed part of the product instead of a spinner. This post is the pattern I settled on. It applies to any slow AI job — image upscaling, voice cloning, long-form writing — not just video.
An HTTP request that stays open for ten minutes is fragile. Proxies time out, phones sleep, tabs close, and the user has no idea whether the work survived.
Store the work as a record that outlives the request:
type GenerationTask = { id: string; status: " queued " | " running " | " ready " | " failed "; createdAt: string; updatedAt: string; outputUrl?: string; // only when ready errorCode?: string; // only when failed }; Enter fullscreen mode Exit fullscreen mode Two consequences follow immediately:
If your product charges per generation, attach the task id to the charge. That single foreign key makes refunds, support and duplicate-detection possible later.
Never show a fake percentage. A progress bar that moves without meaning is worse than a plain statement, because users plan around it.
The honest version sets a low expectation that is usually beaten. The optimistic version sets a high expectation that is usually missed.
Most of the ten minutes happen off-screen. That is the normal case, not an edge case.
A task list also gives you a natural place to explain failures. "Failed" with no reason is a support ticket; "Failed — credits returned" is a resolved moment.
Slow jobs fail more often than fast ones, because there are more moving parts and longer windows in which something can break.
Decide the failure contract before launch:
The third point matters for auditing. If a failed task can be edited into a successful one, you lose the record of what actually happened.
For anything with a visible output, let people take it with them. In my case the result is a 15-second, 16:9 MP4 the user previews and then downloads. Preview before download matters: it lets someone decide whether the result is worth keeping without leaving the product.
If results are only viewable inside your app, you are competing with a screenshot. A downloadable file is a small feature that changes how the product spreads.
There is a nasty case between "task created" and "response received". If the network drops after the server accepted the work, the client cannot tell whether it should retry.
Two rules keep this from turning into double charges:
This is a small amount of state to track, and it prevents the most expensive support category: "I paid twice and got one video."
Before shipping a slow AI feature, check that:
None of these are novel. They are just easy to skip when the model demo works on the first try, and the demo never waits ten minutes.
I build Burning Bridges Studio, so I am describing my own product rather than reviewing it independently. The site publishes its terms, privacy, refund and support pages if you want to check how the pieces above are handled in practice. The ten-minute and 15-second figures are the product's stated estimates, not measured benchmarks.