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.

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.
What I built

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.

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

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

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.
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.
Per-user token buckets on generate, clarify, plan, optimize and support. One credit per Sonnet generation, debited in the transaction that schedules it.
30k max output tokens. Zod schemas cap every structured field, retries are bounded with fallbacks, and user text is wrapped in untrusted tags.
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.
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.

Four decisions that hold it together
No public endpoint can start a model call, so spend and work never drift apart.
Credits bound total spend. Buckets bound burst rate, so one tab or script can't flood the providers.
A loading animation holds the card until the sanitized result has painted, so nobody sees half-built markup.
The closest curated components are passed in as few-shot examples before every create.