7/16/2026

Building a Studio: Designing the UX of AI-Assisted CAD

Before the model was ready, we built the interface it would eventually live behind — and shipped a working demo anyway.

Studio is the page where a prompt turns into a part — the primary reason anyone signs up for SolidMake at all. Designing it raised a question that has nothing to do with 3D viewers or CadQuery: what does the interface look like for a task that doesn't happen instantly, and where the underlying model isn't even trained yet? Both of those constraints shaped the same set of decisions.

Generation is a process, not a request

A real generation isn't a single round-trip. Per the pipeline design, a job moves through queued → generating → executing → validating → succeeded (with an optional repairing step folded in), and the executing and validating stages alone can take most of a minute once real geometry and validator checks are involved. A UI built around a single blocking request — spinner in, result out — hides all of that and just reads as slow. So Studio is built around a job-status model instead: the frontend gets a jobId back immediately and watches it progress through named phases, streamed live via server-sent events rather than polled.

Concretely, that means Studio's job panel shows phase pills — queued, generating, executing, validating, succeeded — with the active one highlighted, plus a live log stream underneath showing what's actually happening at each step: tokens being emitted, the sandbox subprocess spawning, topology counts coming back from the validator. None of this changes what the pipeline is doing. It changes whether the user experiences a minute of silence or a minute of legible progress.

The repair loop has to be visible, not hidden

When a generation fails its first attempt and the pipeline retries with the traceback fed back to the model, that's information worth showing, not smoothing over. A user who watches a generation take 40 seconds because it stumbled once and self-corrected has a completely different experience than a user who just waits 40 seconds for a black box. The job lifecycle includes an explicit repairing status for exactly this reason — it's a state the UI can render, with its own log lines and its own pill, rather than a detail the backend swallows silently. Reliability that's invisible doesn't build trust; a visible recovery does.

Shipping the interface before the model existed

The hardest sequencing decision wasn't a UX detail at all — it was deciding not to wait. The fine-tuned model, the executor, and the validator were all still ahead of us when Studio needed to exist for a demo. Rather than block the product experience on ML training timelines that measure in weeks and Colab session limits, we shipped a Studio mock first: a fully real interface — real prompt input, a real phase-by-phase status stream, a real Three.js viewer, real downloadable files — wired to a small canned catalog of pre-generated parts instead of a live backend. A handful of hand-authored CadQuery bundles (an L-bracket, a flange, a sheet-metal cover, a bearing housing) stand in for what the real pipeline will eventually produce, matched to whatever the user types through simple keyword scoring.

This let the actual product experience — the pacing of the status stream, the layout of the viewer, what a download bar should look like — get designed, tested, and refined on its own timeline, completely decoupled from whether a Colab training run finished this week or not. It also means there's always a working demo path: even after the real backend lands, a cached-demo-mode toggle keeps the canned bundles available as a fallback if the GPU worker is down during a defense or a walkthrough.

The general lesson generalizes past this one project: the hardest technical dependency in a system doesn't have to be the one that blocks everything else. If the API contract between a frontend and a not-yet-finished backend can be pinned down early — a jobId, a status stream, a set of named phases — the UI work and the ML work can run in parallel instead of in sequence, and swapping the mock for the real thing later becomes a backend change nobody outside the team even notices.