DesignTree
Case study·AI Design Generation & Systems Architecture

From a sentence to a canvas of shippable screens.

An AI design app on an infinite canvas. Describe a screen, get several distinct directions side by side, then refine one by pointing at it.

The DesignTree canvas with several generated design directions side by side
ProductDesign Tree
CategoryAI design tool
What I didDesign, Build, AI
StackNext.js 16 · Database
ModelsClaude · Gemini

Design Tree is an AI design product that turns a single sentence into production-grade screens on an infinite canvas, where every model call is rate-limited, paid for, and sanitized before it reaches the canvas.

Scope at a glance
6AI agents, each pinned to the most appropriate model for the job
1–4parallel variants per prompt, one credit each
12aesthetic seeds so no two designs converge
1,000sof users, with over 10,000 designs generated

What I built

A selected frame on the canvas with the edit prompt open

Point at a frame to change it

Select a frame to restyle the whole document, or pick one element inside the live preview. Only that element is regenerated and spliced back in by CSS selector. Saved design systems and screenshot references keep every edit on brand.

Four edit modes: create, full, element, variant
1–4 variants per prompt, desktop, tablet or mobile
Brand tokens and screenshot style lock
A design card showing its loading animation on the canvas

Generate in parallel, see finished designs

Each variant lands in its own free slot on the canvas and holds a loading animation until the design is complete. Short prompts trigger a quick round of tailored questions first, and "Plan first" runs a guided interview that remembers your product.

Finished designs only, never half-built cards
Clarify and Plan modes for thin prompts
Live collaboration, bookmarks, archive with undo

Design together, in real time

Teammates join by invite link and work on the same canvas at once. Everyone sees each other's cursors and selections, and every new frame, edit and move syncs instantly through reactive queries, with no refresh and no merge step.

Live cursors and selections
Single-use, time-limited invite links
Instant sync with no refresh

How it works

Three tiers, one reactive loop

The client never polls. Every card subscribes to a database query, so a finished design is pushed to the browser the moment it's written. Transactional queries and mutations own all state. Node actions are the only code that talks to model providers, and they write back through internal mutations.

Architecture: the Next.js browser app talks to the database cloud (HTTP router, queries and mutations, rate limiter, document DB, file storage, scheduler, actions), which calls OpenRouter, OpenAI and Resend

The most efficient call is the one that fixes a bad prompt

Before any credit is spent, the prompt is triaged on the client. Thin prompts are enriched by Gemini Flash at a fraction of a Sonnet call, and bloated prompts are offered compression. Everything converges on one batch runner, which picks the edit mode from what is selected on the canvas.

Prompt routing flow: thin prompts go to Clarify or Plan, long prompts to the optimizer, all converging on the batch runner
<100characters on a fresh design opens Clarify Mode, asking 2–4 questions at once
3,000characters or more offers compression to a brief of at most 1,600 characters
5questions at most in Plan Mode before it is forced to finalize

Reserve in a transaction, stream out of band, settle once

Each variant is created by its own mutation. In one atomic step it takes a rate-limit token, checks the balance, places the card, debits a credit and schedules an internal generate action. A model call can only exist if a credit paid for it.

The action retrieves the closest knowledge examples, then collects Claude's streamed output under a streaming status, while the card holds its loading animation. One finalize step sanitizes and stores the result, or refunds the credit on failure.

Sequence diagram of a generation: rate limit, balance check, pending card and debit, scheduled action, vector search, streamed output with in-flight checks, then finalize or refund

How the system prompt is layered

The last layer wins. Recency carries priority with the model, so the strongest constraint is appended at the end.

01Agent instructionsDesign philosophy, content and responsive rules
02Edit-mode contractCreate, full, element or variant output rules
03Aesthetic seedOne of 12 directions, random per variant
04Retrieved examplesClosest curated components from the vector index
05Design system tokensColors, type scale, spacing and radius
06Screenshot style lockPalette, type and density from a reference image

A double-entry ledger with a balance cache in front

The first paid action grants 10 credits. Every later change writes the cached balance and an append-only ledger row in the same transaction, linked to the generation it paid for. Refunds only fire on the first transition into error, so a retried failure can never mint free credits.

Credits flow: grant, debit per variant, refund on first failure, with each change writing the balance cache and a ledger row
+10initial grant, written once per user
−1deduction per variant, before any model call
+1refund on the first failure only
+npurchase type, ready for paid top-ups

Token buckets cap the rate. Credits cap the spend.

Two independent limits sit in front of every model call. A per-user token bucket throttles bursts on every agent, including the free Gemini Flash helpers. Credits bound total spend, with the check and debit in one serializable transaction so parallel tabs can't overdraw.

IdentityWho can call

Password and email OTP with lockout after repeated failures. Invite tokens stored only as SHA-256 hashes with a 7-day TTL. Protected routes redirect without a session.

SpendHow often and how much

Per-user token buckets on generate, clarify, plan, optimize and support. One credit per Sonnet generation, debited in the transaction that schedules it.

ModelHow far a call can run

30k max output tokens. Zod schemas cap every structured field, retries are bounded with fallbacks, and user text is wrapped in untrusted tags.

OutputWhat the HTML can do

Sanitized on write and on serve. Strict CSP with nonce-only script and no network. Iframe messages accepted only from the card's own frame.

Spend the expensive model only where taste matters

Six agents, each pinned to the most appropriate model for its job. Only the generator runs on Sonnet and costs a credit.

GeneratorClaude Sonnet 4.6Writes each design's HTML, up to 30k tokens
OptimizerGemini 2.5 FlashCompresses long prompts to a 1,600-char brief
ClarifierGemini 2.5 Flash2–4 tailored questions, then a final prompt
PlannerGemini 2.5 FlashGuided interview with product memory
SupportGemini 2.5 FlashScheduled replies over the last 20 messages
KnowledgeGPT-4o miniTags and embeds components for retrieval

Untrusted HTML, served like a static asset

Finished cards load from a database HTTP route as a fresh document with a per-request nonce, cached immutably and versioned by a runtime parameter. A small injected helper reports height, blocks navigation, forwards zoom to the canvas, and turns clicks into stable CSS selectors for element editing.

Metadata and markup live apart

Every HTML-bearing entity is split into a light metadata document and a heavy code document. Reactive canvas queries only subscribe to the light side. Deleting moves rows into archive tables, so undo is a restore rather than a rebuild.

Data model: each HTML-bearing table split into a metadata document and a code document, with archive tables for undo

Four decisions that hold it together

01Paid for and scheduled in one transaction

No public endpoint can start a model call, so spend and work never drift apart.

02Every agent sits behind a token bucket

Credits bound total spend. Buckets bound burst rate, so one tab or script can't flood the providers.

03Designs appear only when they're finished

A loading animation holds the card until the sanitized result has painted, so nobody sees half-built markup.

04Retrieval grounds the taste

The closest curated components are passed in as few-shot examples before every create.