# Brilliant: complete documentation > Brilliant is a professional 2D vector design tool where humans and AI agents > work on the same canvas. It's a full Figma-class editor with auto layout, > components, design tokens, pen tool, boolean ops, and shaders, that also > runs as an MCP server so LLMs can read and write designs directly. > The built-in AI chat runs on Brilliant AI by default (Brilliant-provided > inference, included with every plan, usage shown as a percent of a > monthly allowance) or on your own AI (your keys and coding agents, > unlimited on every plan, never routed through Brilliant). This file concatenates every doc and blog post for one-shot consumption by LLMs. Individual pages are also available at the source URLs listed inline. Files use MDX, which is markdown plus a few JSX components — agents can ignore the JSX tags safely. --- Source: https://brilliant.design/manifesto.mdx # Manifesto *The contract.* Something broke this year. Software stopped being written by hand. The best builders no longer type code; they align intent with a capable model. The number of people who can create software just went from millions to everyone. But watch those builders work and you'll see something strange. Their agents write the code, run the tests, ship the deploy. Then the product needs to look like something, behave like something, feel like something: and suddenly it's screenshots pasted into chats, "a bit more to the left," hex codes in prompts. The most automated workflow in history, bottlenecked by describing rectangles in English. *Code is becoming a build artifact.* Nobody has said this out loud yet: when agents write the code, the code stops being the source of truth. You don't edit compiled output. You edit the source, and the toolchain does the rest. So what is the source now? The contract: what the product looks like, and how it behaves. The design and the definition. Blueprint and markdown. Change the contract and the change propagates: through agents, through CI, into the product, the way source becomes binaries today. "The app doesn't behave right" stops meaning "file a ticket" and starts meaning "edit the definition." That world needs a home. We built it. *A real canvas that agents can read.* Brilliant is a professional 2D vector design tool. Real pen tool, real booleans, components with variants, auto layout, design tokens, and a GPU engine we wrote from scratch that runs on macOS, Windows, and in your browser. Everything you expect from a decade of professional tools, faster. Underneath it, one radical decision: every design is plain text. A language with a published spec that round-trips losslessly: the file on disk, the realtime wire, and the thing your agent reads and writes are the same text. Git blame a hero color. Review a redesign in a PR. Point any agent at your designs the way you point it at your codebase: Claude Code, Codex, Cursor, any harness, any model, your keys. Zero markup on AI, forever. *On brand, at scale, under control.* When an agent touches a Brilliant canvas, its change is treated like source code: parsed, validated, normalized against your design system's tokens, linted against the real layout, and reviewed by the agent itself against a screenshot, before you ever see it. A rogue hex can't ship. An off-brand asset can't sneak through. That is what teams actually want from AI design: not a slot machine of pretty pictures, but a system where the thousandth asset is as on-brand as the first, and where every element stays fully editable by hand. Direction stays yours. Taste stays yours. *The different bet.* Count the field honestly. Everyone building "AI design" made the same bet: the canvas is HTML and CSS. It demos beautifully, and it caps them forever: no real geometry, no engine-grade materials, no output beyond the web, a browser's ceiling on everything. And "no handoff" is only true when your product is a website they host. Everyone else's agent reimplements the design in the real codebase anyway. We bet higher. The design is not the code, and not a picture of the code. It is the contract above the code. Nobody else is building there. The incumbent, meanwhile, is retrofitting agents onto a tool built for hands, sold by the seat. *Free, the way it should be.* VSCode is free; developers don't pay for the privilege of writing code. The Brilliant editor is free: the full professional tool, unlimited public work, unlimited agents, your own AI keys. Not a trial. Free. We make money the way GitHub does: private work, teams, and the workflows that connect your contract to everything else. Simple, honest, aligned. *The music is starting.* Models learned to build software from a decade of open source. The products of the next decade will be defined somewhere. We're building that somewhere. Care to shape it? --- Source: https://brilliant.design/pricing.mdx # Pricing Generated from `src/data/pricingTiers.js`, the same file the React pricing pages read. Edit there, redeploy, and this mirror updates. Monthly prices only. Start free. Add Brilliant AI as you need it. ## Individual ### Free · $0 forever WHAT YOU GET: - Brilliant AI to try - Every design feature - Public projects and drops, 2 GB - Your own agents over MCP, unlimited - Your own AI, 10 messages a day ### Personal · $20 / month EVERYTHING IN FREE, PLUS: - Brilliant AI's base allowance - Private projects and drops - 50 GB storage - Short handle and blue badge - Your own AI, unlimited ### Pro · $50 / month EVERYTHING IN PERSONAL, PLUS: - 5x the Brilliant AI allowance - Refills every month on your billing date - Replaces your Personal plan, one subscription ### Max · $100 / month EVERYTHING IN PERSONAL, PLUS: - 10x the Brilliant AI allowance - Refills every month on your billing date - Replaces your Personal plan, one subscription ### Max 20x · $200 / month EVERYTHING IN PERSONAL, PLUS: - 20x the Brilliant AI allowance - Refills every month on your billing date - Replaces your Personal plan, one subscription ## Team ### Team · From $25 / seat / month RUN IT LIKE A STUDIO: - Brilliant AI on every seat - Raise any seat's AI allowance individually (5x, 10x, 20x) - Team-owned projects that stay when people leave - Real-time multiplayer on one canvas - Roles and invites, one invoice for everyone - 1 TB pooled storage ## Your own AI: Your keys, your provider, no markup. Connect your own provider account and you pay it directly. Keys stay on your machine. We never meter it, on any plan. ### Free · $0 forever WHAT YOU GET: - Your own AI, 10 messages a day - Brilliant AI to try - Every design feature - Public projects and drops, 2 GB - Your own agents over MCP, unlimited ### Personal · $20 / month EVERYTHING IN FREE, PLUS: - Your own AI, unlimited - Brilliant AI's base allowance - Private projects and drops - 50 GB storage - Short handle and blue badge All plans: Brilliant AI comes with every plan. Need more? Pro and Max. Need a team? Seats from $25. Compare plans: https://brilliant.design/pricing --- Source: https://brilliant.design/blog.mdx # Blog > The Brilliant blog. Posts are listed below in reverse chronological order. Each link points at the raw `.mdx` body for one-shot consumption; drop the `.mdx` extension to view the rendered post. ## [The math behind Brilliant's renderer: a budget of nanoseconds per pixel](https://brilliant.design/blog/renderer-math.mdx) *July 14, 2026 · by Amichai Mantinband · 7 min read* One frame divided by one viewport leaves a few nanoseconds per pixel for everything. The cost laws behind that budget, why some caches are provably a bad idea, the measured result that replaced one of them, and where the arithmetic honestly stops closing. ## [Brilliant's rendering contract: seven things a frame never trades](https://brilliant.design/blog/rendering-contract.mdx) *July 14, 2026 · by Amichai Mantinband · 7 min read* Perf work fails by winning benchmarks and trading away what you can feel. We wrote our rendering values down, gated them with tests, and let them delete work we were proud of. All seven, with the moments each one earned its keep. ## [One renderer: moving Brilliant's canvas to Rust and wgpu](https://brilliant.design/blog/rust-engine.mdx) *July 14, 2026 · by Amichai Mantinband · 8 min read* Why we deleted the Flutter painter instead of caching around it, how Dart stays in charge of everything but the pixels, and the optimization we were sure we needed until we measured it. ## [How Brilliant hunts jank: a profiler that writes the verdict](https://brilliant.design/blog/jank-hunting.mdx) *July 14, 2026 · by Amichai Mantinband · 10 min read* A late frame leaves no stack trace, so we built a recorder that captures a real session and writes down what was slow and why. Plus the measurement culture that would rather say 'I don't know' than lie. ## [How Brilliant's canvas stays fast at 13,000 elements (and why selection draws two rectangles)](https://brilliant.design/blog/per-parent-selection.mdx) *July 14, 2026 · by Amichai Mantinband · 8 min read* Brilliant treats the canvas as a hierarchy of coordinate spaces, not a flat pile of shapes. A tour from the spatial index that keeps 13,000 elements fast down to the per-parent selection model that keeps operations correct. ## [In Brilliant, a design system is a short program](https://brilliant.design/blog/design-tokens-with-modes.mdx) *July 14, 2026 · by Amichai Mantinband · 8 min read* In Brilliant, tokens come from a compiler: a few seed lines expand into every stop, role, and per-mode variant through OKLCH math. The part we're proudest of is modes as transforms, not duplicated palettes. ## [Your key, your traffic: why Brilliant's AI is BYOK](https://brilliant.design/blog/byok.mdx) *July 14, 2026 · by Amichai Mantinband · 7 min read* Written when the built-in chat was your-own-key only: why bringing your own key is a trust decision, how the multi-provider plumbing works, and the consent rules for anything leaving your machine. Brilliant AI (the default since September 2026) routes through Brilliant to Google; your own AI still never does. ## [Brilliant's Blueprint: teaching agents to speak design](https://brilliant.design/blog/blueprint.mdx) *April 24, 2026 · by Amichai Mantinband · 14 min read* How a line-oriented DSL built for agents became Brilliant's only format: the file on disk, the realtime wire, and the language every agent speaks. Told as it happened, wrong turn included. # Section: Getting started --- Source: https://brilliant.design/docs/getting-started/coming-from-figma.mdx # Coming from Figma If you already think in frames, components, and auto layout, you know most of Brilliant. This page maps Figma's concepts onto Brilliant's, calls out the few places the two genuinely diverge, and gives you the full keyboard-shortcut translation so your muscle memory keeps working. ## The concept map Almost everything you build in Figma has a direct home in Brilliant. The names line up more often than not. | Figma | Brilliant | Notes | |-------|-----------|-------| | Page | Canvas | A canvas is a single `.bl` file. Importing a Figma file makes one canvas per page. | | File (multiple pages) | Folder of canvases | A multi-page import lands as a folder named after the file, one canvas inside per page. | | Project or team | Workspace | A workspace is just a folder on disk. Open one with . | | Frame | Frame | A real container: it clips its contents, runs auto layout, and accepts drops. | | Group | Group | Hugs its contents, structural only, children can't be dragged out on the canvas. | | Auto layout | Auto layout | Same direction, spacing, padding, wrap, and alignment controls. | | Constraints and resizing | Hug / fill / fixed sizing | Each element sizes per axis; pull a child out of the flow with its ignore-auto-layout toggle. | | Component (main) | Master component | A normal frame marked with a purple label and a filled diamond in the layers list. | | Instance | Instance | A linked copy that syncs from its master except where you override. | | Component set with variants | Component set | Named properties become axes; each value combination is one variant. | | Styles and variables | Design system tokens | Brilliant's [design system](/docs/design-system/overview) is the token layer; bind colors, type, and spacing to it. | Frames and groups are covered in [Frames and auto layout](/docs/editor/frames-and-auto-layout), components in [Components](/docs/editor/components), and canvases in [Canvases and files](/docs/workspace/canvases-and-files). ## What genuinely differs Most of Brilliant will feel like home. These are the spots where it doesn't, and where a Figma habit will trip you up until you know the difference. ### Selection is grouped per frame This is the big one. When your selection spans more than one frame, Brilliant draws a separate selection rectangle for each frame, each with its own handles. Select two elements in Frame A and three in Frame B, and you see two rectangles, not one box around all five. Flip the toggle below between Brilliant's per-parent model and the flat one a Figma habit expects, on the same selection across two frames. The blue outlines are drawn to stand in for the real selection chrome. It's deliberate, and it makes multi-frame edits behave sanely. Every operation runs once per frame, in that frame's own coordinate space: - **Align Left** lines up the elements in Frame A among themselves, and the elements in Frame B among themselves, independently. Nothing aligns across a frame boundary. - **Resize, distribute, and rotate** each work against their own frame's box. - **Snapping** stays inside the frame you're working in, so an element never yanks toward something in a different frame. When everything you've selected shares one parent, this is invisible: you get a single rectangle that behaves exactly as you'd expect. Full detail lives in [Selection and editing](/docs/editor/selection-and-editing). ### Select a frame to restyle all its text Select a frame and any typography change (font, size, weight, bold, alignment) applies to every text element inside it at once. You don't have to drill in and multi-select the text nodes. Structural and geometric edits still target only what you explicitly selected. ### Your files are folders and files on disk Brilliant is local-first. A workspace is a folder, each canvas is a plain-text Blueprint `.bl` file, and images live in `Assets/` directories beside them. Nothing is uploaded until you choose to publish a project to brilliant.design. Because they're plain text, your designs drop straight into Git: branch them, review them in pull requests, and `git blame` a stray color change like code. There's also no save button. Brilliant autosaves continuously, and there's no for normal work. ### Undo is per canvas Each canvas keeps its own undo history. undoes the active canvas's last action, and switching canvases never clears it. Undo is session-only. For persistent history you use Git, and a project published to brilliant.design also keeps named [checkpoints](/docs/platform/projects) and a full version history. ### Components live on the canvas, not in a panel There's no Components panel or dedicated Components page. Masters are regular frames on whatever canvas you put them on; a common convention is to keep them together on one canvas. A contextual **Component** section appears in the right toolbar when a component, set, variant, or instance is selected. There's also no swap-instance command. To repoint an instance at a different master, detach it and make a fresh instance. Within a set, you don't swap: you flip the property dropdowns to pick a variant. See [Components](/docs/editor/components) for the full model. ## Shortcut translation Your Figma muscle memory mostly carries over. These are the shortcuts a migrant reaches for most, with the ones that differ called out. Shortcuts shown are the macOS defaults; on Windows, Cmd maps to Ctrl. ### Same as Figma | Action | Shortcut | |--------|----------| | Move tool | | | Frame tool | | | Rectangle | | | Ellipse | | | Text | | | Line | | | Pen | | | Group / ungroup | / | | Add auto layout | | | Duplicate | | | Select all | | | Rename layer | | | Hide / show selection | | | Bring to front / send to back | / | | Align left / right / top / bottom | / / / | | Undo / redo | / | | Drill into frame / step out | / | | Cycle siblings | / | ### Different in Brilliant | Action | Figma | Brilliant | |--------|-------|-----------| | Frame the selection | | | | Export selection to PNG | | | | Outline text | | | | Use as mask | (see note) | | | Color picker | none by default | | Brilliant binds masks to because macOS reserves Cmd+Option+M for "Minimize All". Component shortcuts match Figma: Create Component is and Detach Instance is . The other component actions (Create Instance, Create Component Set, and so on) have no default key and live in the command palette or the right-click menu. ### Only in Brilliant | Action | Shortcut | |--------|----------| | Global search (commands, canvases, layers, fonts, chats) | | | Command palette | | | AI chat input | | | Scale mode (scales strokes, radii, and text) | | | Combos (macro system) | | | Overlay mode | | On Windows a few of these change or drop out, because their chords collide with essential Ctrl shortcuts: the color picker moves to , and Outline Text and Overlay Mode are unbound by default (reach them from the command palette). Every shortcut is fully remappable in the [Shortcuts panel](/docs/workspace/shortcuts) with , so you can rebuild any layout you miss. ## Bringing your Figma files over You don't have to rebuild anything by hand. Brilliant imports Figma files directly in three ways: paste a file link into the web New menu, paste a URL into the desktop editor's Import section, or push a selection from the Brilliant plugin inside Figma. Figma components come across as real, editable components and sets. The full walkthrough, fidelity notes, and rate-limit tips are in [From Figma](/docs/import/from-figma). ## Next - **Learn the editor:** [Quick Tour](/docs/getting-started/first-steps). - **Import your designs:** [From Figma](/docs/import/from-figma). - **See how the tools compare:** [Brilliant vs Figma](/docs/compare/figma). --- Source: https://brilliant.design/docs/getting-started/connect-provider.mdx # Connect an AI Provider A fresh install runs on Brilliant AI: your first message just works, with no key and no setup, on the Brilliant AI usage Free includes every month, part of it usable before you sign in. This page is for the other lane, your own AI. You do not need a provider to send prompts. Connect one when you want to run the chat on your own AI instead (Free allows 10 messages a day on your own AI; any paid plan removes that cap). You have two paths: - **Bring your own API key** from Anthropic, OpenAI, Google, or OpenRouter. Pay-per-use, billed by the provider. - **Route through your Claude Code subscription** if you already have one. No separate API key required; Brilliant uses the Claude Code CLI to talk to Anthropic on your behalf. Both are managed from the same place: **Settings → Your own AI**. The moment you connect one, it appears in the chat's model selector. If you'd rather drive Brilliant from an external editor like [Cursor](/docs/agents/cursor) or [Claude Code](/docs/agents/claude-code) instead of the integrated chat, you don't need to connect a provider here. Head to [Connect an Agent](/docs/agents) instead. ## 1. Open Settings → Your own AI Two ways in: - **Click the connection indicator** at the left of the AI chat input in the bottom toolbar. It tracks your own AI: a checkmark means one of your own providers is connected, an X means none is (Brilliant AI needs none of them and works either way). Clicking it jumps straight to the Your own AI pane. - **Press ** to open Settings, then pick **Your own AI** in the sidebar. ![The Settings window with Your own AI selected, showing Claude Code and five API-key providers, each with a status dot and an action on the right|80%x](/docs/images/settings-ai-providers.webp) Hovering the indicator also pops out a small status list, but it's display-only. Every action lives in the Settings pane. ## 2. Connect a provider ### Option A: Bring your own API key For **Anthropic**, **OpenAI**, **Google**, or **OpenRouter**, click **Add API key** on the row, paste the key, and confirm. The status dot turns green and the row swaps to a connected state. - **Anthropic:** direct API access to Claude models. - **OpenAI:** direct API access to GPT models. - **Google:** direct API access to Gemini models. - **OpenRouter:** a meta-provider with a single key for dozens of models across vendors. Useful if you want to try different models without juggling keys. You can connect more than one. Switch between them later from the model picker in the chat itself.
Privacy: Your key is encrypted in the system keychain the moment you submit it. Brilliant reads it back only when it needs to make a request on your behalf, and it never leaves your machine. No API key is ever sent to Brilliant's servers.
### Option B: Use Claude Code If you already have [Claude Code](https://claude.com/code) installed and signed in on a Pro, Max, Team, or Enterprise plan, the **Claude Code** row will show as connected the moment you open the pane. Nothing else to do; Brilliant routes through the CLI and bills against your existing Claude subscription. (On a free tier the row reads as not connected, since the CLI can't drive the integrated chat there.) If you don't have it yet, install it and sign in from your terminal first: 1. Follow the [Claude Code install guide](https://docs.claude.com/en/docs/claude-code/quickstart) for your OS. 2. Run `claude` once and complete the browser sign-in flow. 3. Reopen Settings → Your own AI in Brilliant. The Claude Code row picks up the existing login. ## Once connected Send a prompt from the AI chat input. Brilliant uses whichever provider you selected, and you can swap providers any time by reopening the pane. ## About Playground mode Playground replays bundled demo conversations so you can scrub a real session with no network call. It is not a fallback: it is a toggle at the bottom of the Your own AI pane, the onboarding step plays one recorded session, and the `/playground` page on the website is the guided demo for visitors. ## Note on Quiver The same pane also has a **Quiver** row. Quiver isn't a chat model. It's the API behind Brilliant's AI vector generation and image-to-SVG conversion. Connecting it unlocks those features but has nothing to do with the chat. ## Next - **Design with the built-in agent:** [Your First AI Chat](/docs/getting-started/first-ai-chat) walks through prompting, element references, and iterating. - **Drive Brilliant from your editor:** [Connect an Agent](/docs/agents) sets up [Claude Code](/docs/agents/claude-code), [Cursor](/docs/agents/cursor), [Codex](/docs/agents/codex), and others. --- Source: https://brilliant.design/docs/getting-started/first-ai-chat.mdx # Your First AI Chat Brilliant has an AI chat built directly into the canvas. It's not a generic LLM you paste screenshots into and copy answers out of. It's an agent that reads your current file, sees what you have selected, and runs real design commands on your behalf: creating elements, modifying properties, running layout commands, searching across canvases, and exporting frames. You describe the outcome in plain English, and the agent instantiates it on the canvas while you watch. The chat is designed for long conversations. It keeps a running transcript, carries context across turns, can run dozens of tool calls back-to-back to build something up, lets you pause and course-correct mid-stream, and supports multiple parallel chats so you can experiment in one while keeping another intact. You can feed it element references (copy-paste, or `@`-mention), inline images, and picked colors, and it will cite its work with progressive on-canvas previews as each step lands. If you'd rather drive Brilliant from an external editor like [Cursor](/docs/agents/cursor) or [Claude Code](/docs/agents/claude-code), head to [Connect an Agent](/docs/agents) instead.
Heads up: The integrated chat works on your first message with no setup: it enrolls in Brilliant AI (the default) and sends on the Brilliant AI usage Free includes every month. To use your own provider key instead, see Connect an AI Provider.
## 1. Open the AI chat The AI chat lives in the bottom toolbar, collapsed until you call it. There are three ways to open it, all of which land you in the same focused input: **Click the AI input** at the bottom of the canvas. Fastest if your cursor is already near the bottom of the screen. ![The AI input inside the bottom toolbar at the bottom of the canvas|50%x](/docs/images/chat-open-bottom-toolbar.webp) **Press ** anywhere on the canvas. The fastest way if you're mid-design and don't want to move the mouse. ![The slash shortcut that opens the AI chat from anywhere on the canvas|50%x](/docs/images/chat-open-slash.webp) **Press ** to open the global search and type `focus ai chat`. Useful if you've already remapped your shortcuts and can't remember the new binding. ![Opening the AI chat from the global search palette with the ⌘K shortcut|50%x](/docs/images/chat-open-cmdk.webp) Once you're in, the chat panel slides out from the side. You can dismiss it with `Esc` or by clicking anywhere on the canvas; your draft message is preserved, so reopening picks up exactly where you left off. ## 2. Write a prompt The chat accepts plain-English descriptions of what you want. Don't bother breaking the task into tool calls or step-by-step instructions. Describe the outcome and let the agent pick the tools. A few examples of prompts that work well: - `Create a pricing page with three tiers.` - `Add a hero section above the current design with a headline and a download button.` - `Apply the color palette from @Dashboard to @Card.` - `Make @Card into a dark-mode variant.` - `Export @Card as SVG.` - `Create three variations of @Card in a new canvas.` The simplest way to point the agent at something on the canvas is to **copy and paste** it. Select any element, hit , click into the prompt input, and hit . Brilliant drops the element in as a reference chip inline with your text. Works with a single element, a multi-selection, or a whole frame: whatever you had selected is what the agent will see. If you'd rather type than click, `@` references do the same thing from the keyboard. Type `@` and Brilliant autocompletes with the names of elements you currently have in the file. `@` references are scoped to elements only, not canvas pages or files. You can `@`-reference multiple elements in one prompt to compose them, like `Apply the typography from @DesignSystem to every text layer inside @Marketing`. ![The prompt input with example prompts and the model selector](/docs/images/chat-step2.webp) ### Attach images and files Use the **attachment button** inside the prompt input to attach images, screenshots, or files directly to a message. Handy when you want to hand the agent a reference, a mockup, an exported screenshot, or a logo to work from. ### Pick a color Next to the attachment button is a **color pick button** that drops you into an eyedropper: click anywhere on screen and the picked hex gets inlined into your prompt. Press for the same eyedropper from the keyboard without moving off the canvas. If you'd rather pick from a full palette, press to open the color selector. Hit to send. If you want a line break inside the prompt instead of sending, use . On the control row below the input (the same row as the attach and send buttons) you'll see the model picker (Brilliant AI is selected by default; on your own AI you pick the model) and a thinking-level selector. The default thinking level is fine for almost everything; bumping it up gives the agent more room to reason through longer planning tasks, and you can ignore it until you need it. Your first message just works on Brilliant AI (the default), which Free includes every month, and part of it can be used before you sign in. On Free, using your own provider key instead is capped at 10 messages a day. When the month's Brilliant AI usage runs out, the next send opens the upgrade page, a page on the command-palette shell (the same overlay as sign in), which names when your usage resets and offers the next paid rung or setting up your own AI; when you hit the daily key cap you can upgrade to Personal or keep going through an external agent over MCP, which is never capped. The message you sent is held and continues by itself once the wall clears (you sign in, upgrade, or connect your own AI); "Not now" returns it to the composer as a draft. See [Plans and billing](/docs/platform/plans-and-billing). ## 3. Iterate Very few designs are right on the first prompt, and that's fine. The agent keeps a running transcript of the conversation, so follow-up messages are cheap: - **"Make it darker."** - **"Swap the primary color for #FFDD00."** - **"Tighten the spacing on the second card."** - **"Actually, use the same layout as @Hero instead."** Each follow-up sees the full context of the canvas and the previous responses, so you can speak in deltas instead of re-explaining the whole task. If you want to try a different direction without losing the current one, open a **New Chat** from the chat list at the top of the panel. With the AI chat focused you can also press to spin one up from the keyboard. Old chats stay live until you archive them, so you can jump between parallel experiments the same way you'd keep two drafts open in a text editor.
Tip: Every chat has its own undo/redo stack, separate from the canvas's main history. That means you can keep designing by hand on the canvas while an agent is running. Your edits and the agent's edits don't collide. If you want to roll back a specific agent's changes without touching your own, use the undo/redo buttons inside that chat instead of the global canvas undo.
## Next - **Use an external editor instead:** skip the in-app chat and drive Brilliant from [Claude Code](/docs/agents/claude-code), [Cursor](/docs/agents/cursor), [Codex](/docs/agents/codex), or anything else that speaks MCP. See [Connect an Agent](/docs/agents) for the full list. - **Learn the canvas by hand:** if you'd rather get fluent with vectors, frames, and auto layout the old-fashioned way, read [Quick Tour](/docs/getting-started/first-steps). --- Source: https://brilliant.design/docs/getting-started/first-steps.mdx # Quick Tour A whirlwind tour of the Brilliant editor: the regions of the screen, the one shortcut that unlocks everything else, and the handful of keybindings worth burning into muscle memory on day one. If you'd rather skip straight to designing with an agent, jump to [Your First AI Chat](/docs/getting-started/first-ai-chat). ## The editor at a glance Brilliant's editor is laid out around four toolbars, one on each edge of the canvas, plus the canvas itself in the middle. Once you know what each region is for, you always know where to look. ![Annotated overview of the Brilliant editor with the four toolbars labeled](/docs/images/quick-tour-editor-overview.webp) - **Top toolbar:** one island across the top. The home button sits on the left, and a tab for each open project sits beside it, so you can jump between projects and back to home. In the web editor and the overlay, where there is no tab strip, it shows the breadcrumb for the active canvas instead, in the form workspace / folder / canvas. - **Left toolbar:** your project tree and layer list. The top half is the file explorer, showing every canvas in the document; the bottom half is the layers explorer, showing every element in the active canvas, nested the way the canvas nests. - **Right toolbar:** properties for whatever you currently have selected. The contents change based on the selection (typography, fill, stroke, effects, layout, frame properties, and so on); with nothing selected, it shows document-level settings. - **Bottom toolbar:** the AI chat input, always one click (or ) away. - **Canvas:** everything else. This is where you design. ## Everything is a ⌘K away The single shortcut worth learning before any other is . It opens a global search palette that finds and runs anything in the editor: commands, canvases, layers, fonts, and chats. If you ever forget where a feature lives or what it's called, you don't have to hunt through menus. Open ⌘K, type a few letters of what you want, and hit . ![The ⌘K global search palette open with a sample query|50%x](/docs/images/quick-tour-cmd-k.webp) Treat ⌘K as your escape hatch. Every shortcut on this page is worth memorizing, but if you forget any of them, ⌘K will still get you there. If you know the category of what you're after, there are narrower, faster searches for each one: - searches layers in the active canvas. - searches canvases in the document. - searches your AI chats. All four palettes share the same keyboard-first behavior: type to filter, arrow keys to move, to commit, to dismiss. ## The AI chat The bottom toolbar is home to the integrated AI chat, an agent that reads your canvas, sees your selection, and runs real design commands on your behalf. Tap the input or press to open it. ![The Brilliant AI chat with a prompt, element references, and the agent's response|50%x](/docs/images/quick-tour-ai-chat.webp) The chat gets its own walkthrough in [Your First AI Chat](/docs/getting-started/first-ai-chat), including how to reference elements with `@`, attach images, pick colors, and run parallel chats. For this tour it's enough to know where it lives. ## Moving around the canvas Canvas navigation in Brilliant follows the conventions you already know from other design tools: - **Pan:** hold and drag, or two-finger drag on a trackpad. - **Zoom:** / , or pinch on a trackpad, or . - **Fit the whole canvas:** . - **Fit the current selection:** . - **Toggle 100% zoom:** flips between 100% and whatever zoom you were at before. If you ever get lost, is the fastest way to bring the whole design back into view. ## Selecting and moving things - **Click** to select a single element. to add or remove from the selection. - **Drag on empty canvas** to marquee-select everything inside the rectangle. - **Drill into a frame:** with the frame selected, press to narrow the selection to whatever is inside it. walks back up to the parent. - **Cycle siblings:** / moves the selection to the previous or next element at the same level. - **Move** with the mouse, or nudge with the arrow keys. Hold for a larger step. - **Duplicate** with (drag a copy out) or (drop a copy in place). ## Undo freely Brilliant keeps a full undo history for the canvas. If you don't like a change, whether yours or the AI's, walk it back: - to undo. - to redo. There's no penalty for experimenting. Break something, then roll it back. ## Your files save themselves There's no save button. Every change, whether yours or the AI's, is saved to the `.bl` file on disk shortly after you stop editing (a brief debounce, so a burst of edits writes once), so you can close the app or lose power without losing work. The document name in the top toolbar is always a live reflection of what's on disk. A fresh launch drops you straight into a **Scratch** project, so you can start designing before you've opened a folder of your own. `.bl` files are plain-text Blueprint, so they drop cleanly into git (or any other version control system). Every commit renders as a human-readable diff, so you can branch your designs, review them in pull requests, and `git blame` a stray color change the same way you would on code: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ```
Tip: For quick one-off snapshots, duplicating the `.bl` file in Finder works too. Autosave covers "I closed the laptop"; a duplicate or a commit covers "I want a version I can go back to." And once you publish a project to brilliant.design, you also get named [checkpoints](/docs/platform/projects) and a full history for it.
## Next - **Use your own AI (optional):** the chat works out of the box on Brilliant AI; connect a provider when you want to run it on your own key or coding agent. See [Connect an AI Provider](/docs/getting-started/connect-provider). - **Design with the built-in agent:** [Your First AI Chat](/docs/getting-started/first-ai-chat) walks through prompting, element references, and iterating. - **Drive Brilliant from your editor:** skip the in-app chat and use [Claude Code](/docs/agents/claude-code), [Cursor](/docs/agents/cursor), or any other MCP-speaking agent instead. --- Source: https://brilliant.design/docs/getting-started/installation.mdx # Download & Install Everything you need to get Brilliant running on **macOS** or **Windows**. Grab the installer, run it, and sign in. You'll be on the canvas in a couple of minutes, and a short guided tour runs the first time to show you around. ## 1. Get the app Click . It grabs the macOS installer (the .dmg). Need Windows instead? Pick it from the platform chooser to grab the .exe setup. ## 2. Install it ### macOS Open the .dmg and drag **Brilliant** into the **Applications** folder. ![The Brilliant .dmg installer window with an arrow pointing to the Applications folder|50%x](/docs/images/install-dmg.webp) macOS will confirm once. Click **Open** and you're in. ![macOS Gatekeeper dialog asking to confirm opening Brilliant|450x](/docs/images/install-gatekeeper.webp) ### Windows Run the downloaded Brilliant-…-Setup.exe. It installs just for you (no admin prompt) and launches when it finishes.
SmartScreen: if Windows shows a "Windows protected your PC" notice, click More info → Run anyway. Brilliant keeps itself up to date from inside the app, so you only do this once.
## 3. Sign in The first time you open Brilliant, it asks you to sign in. There are no passwords: enter your email and Brilliant sends you a 6-digit code, or continue with Google. The same account signs you in on the desktop app and on brilliant.design. Brilliant AI on desktop needs version 0.1.0-beta.71 or later; on earlier builds the chat runs on your own AI only, so update the app (it updates itself, or grab the latest installer above). ## 4. Take the tour Once you're signed in you land on the canvas, with the AI chat open. A short guided tour runs on this first launch and spotlights the parts of the editor you'll reach for first: watching an agent design, jumping to any element, and running chats side by side. It plays once and steps you through each part in turn. ![Brilliant on first launch, with the AI chat open and the guided tour running](/docs/images/install-welcome.webp)
Tip: Press (on Windows, Alt+A) to fit the whole canvas in view whenever you want to get your bearings.
--- Source: https://brilliant.design/docs/getting-started/why-pay.mdx # Why you should pay for the integrated AI chat Brilliant's integrated AI chat is the best interface in the world for designing with an AI agent. Are we objective? No. Is it the truth? Absolutely. If you design in Brilliant without the integrated chat, you are paying more, not less. On your own AI you pay your provider in tokens and you pay in time; Brilliant AI is included with your plan instead. This doc explains why. The chat runs on Brilliant AI out of the box: Free includes it every month, then a monthly plan raises your usage as a share of the base (Personal $20, Pro $50 at 5x, Max $100 at 10x, Max 20x $200 at 20x). Or bring your own AI, where you pay your provider in tokens and Brilliant takes no markup. ## Context management is incredible We save you hundreds of thousands of tokens per chat. That is not an exaggeration. Here is just a peek at what the orchestrator does on every turn, without you lifting a finger. The end result? You can have longer conversations, and the cost per chat is lower. ### Old images get dropped A screenshot attached several turns ago can be 50,000 tokens. It is not helping the agent decide what to do now. Brilliant drops stale images automatically, leaving a short `[Image cleared]` stub in their place. The recent ones stay, because those are the ones that matter for the next move. Zero user effort, zero config. ### Long tool results get collapsed When the agent calls `lookup` and gets back a 20K-token canvas dump, it stays in context while you keep iterating. Once it goes stale, it collapses into `[Tool result cleared]`, and only when collapsing actually saves enough to matter. Nothing is trimmed aggressively to manufacture an upgrade prompt. The savings are real. ### Streaming catches bad output early When the agent writes a broken Blueprint line mid-stream, Brilliant stops applying lines at the break point and surfaces the error, so the next turn course-corrects instead of piling another 300 lines of invalid tokens on top. ### Much more There is a lot more to add here around token efficiency and how it affects the quality of the agent's output, but let's just say: we take the AI chat experience and your token spend very seriously. ## The UX is why you stop using everything else Design work is contextual. Context includes locations, colors, elements, ordering and more. Brilliant's AI chat is optimized exactly for this. No describing which element you're talking about. No trying to describe general locations on the canvas. Want to reference an element? Just copy paste it to the chat. The agent sees the element, the structure and the hierarchy. No back and forth to fetch context, and no time wasted on describing. Want to point at a place on the canvas? Drop a pin with the position picker. Want to use an existing color? Toggle color picker and pick a color or open the color selector and try different colors until you get it right. That's just a taste but there is a lot more. ### Selection-aware inputs - **Copy-paste any element** as a reference chip, inline in the prompt. - **`@mention` any layer** with autocomplete from live layer names. - **Eyedropper** anywhere on screen with the color pick button or . - **Position pick** for "this spot on the canvas." - **Paste images** into the input with . - **Drag files** onto the input. ### Model switching mid-conversation One dropdown. Brilliant AI, Anthropic, OpenAI, Google, OpenRouter, Claude Code CLI. Brilliant AI runs at its lightest thinking level by default; pick medium or high for deeper reasoning, and your own models add an off level plus xhigh on Codex. The conversation carries through because the event stream is normalized behind the dropdown. Hit the hard turn with Opus, drop to Haiku for cleanup, back to Opus. No re-briefing. ### Parallel chats with real keyboards opens a new chat. through jump between them. Each has its own undo stack, its own canvas context, its own model. Experiment in one while the other stays intact. searches across all chats. Up and down arrows walk your prompt history in the input; reverse-searches it. ### Per-chat undo stack We treat each agent like a user. Each agent has its own undo/redo you control. You can roll back to a specific part of the chat, or step back and forth change after change. Separate from the canvas undo. The agent runs a 40-operation batch, and you can walk back just the agent's edits without touching the pen-tool work you did during the wait. This is not possible in any external editor. ### The chat is a first-class panel, not a modal Snap full-screen, bottom, left, right. Resize. Queue the follow-up while the agent is still working; it sends on completion. Edit a user message and resend. Copy the entire transcript as markdown with YAML front matter. ### You can see what's happening - **Progressive creation** lands elements on canvas as the agent types them. - **Thinking blocks** are collapsible with a live elapsed timer. - **Tool calls** are expandable cards with clickable URLs and copy-JSON. - **Skill badges** show exactly what knowledge the agent loaded. - **Element and canvas chips** show exactly what element was updated and with what values. Same for Canvases. The agent can literally point you at the right element or canvas. Every one of these is the difference between seeing what the agent does, live, and waiting 3 minutes just to see it didn't understand you properly. Each element updates immediately as the agent streams the intent. You can stop the agent immediately if something seems off and point it at the right direction. ## The output is actually better External MCP agents get access to the same canvas tools, the same composition checks, and the same on-demand knowledge files that teach Brilliant-specific patterns (effects, shaders, tokens, typography scales). What they don't get is the streaming flow on top: composition warnings surfaced live as the agent builds (bad auto layout, wrong sizing modes, collapsed or overflowing elements), so the agent course-corrects mid-build instead of after. Same model, better prompt, better output. ## For teams Team extends Personal with the admin layer you need to run a team. - **One invoice for every seat.** Each seat carries its own Brilliant AI usage, measured per seat on the team's cycle and never pooled. Your own keys and their API bills stay per-designer, and Brilliant never marks up your own AI, for teams or anyone else. - **Admin dashboard.** Per-seat usage and each seat's tier. An admin assigns a tier per member (Personal seat $25, Pro seat $55, Max seat $105, Max 20x seat $205 per month, two-seat minimum) and can raise a seat when a member asks; per-seat prices are admin-only and members see their own tier read-only. - **Usage and counts only, never content.** Admins see per-seat usage, message counts, and design counts. They do not see the content of any conversation or any design. We take that seriously because we run on our own tool. - **User management.** Invite, remove, role changes. The trade is one invoice instead of five, plus an admin who can see what the team is doing without seeing what the team is designing. Tidier, defensible, auditable. ## Why this model We want anyone and everyone to be able to use Brilliant. Our goal is clear: creating the single best design tool to have ever existed. So how do we make money? We offer an incredible, optional experience. An experience so worthwhile that users actively choose to pay for it. That's the reason Free includes Brilliant AI every month and 10 built-in chat messages a day on your own AI. External agents over MCP stay unlimited on every plan. Try it out. Fall in love. Never look back. ## Next - **Try it:** [Your First AI Chat](/docs/getting-started/first-ai-chat) walks through the basics. - **Compare the plans:** [Plans and billing](/docs/platform/plans-and-billing) lays out Free, Personal, Pro, Max, and Team. - **Set up an external agent anyway:** [Connect an Agent](/docs/agents) covers Claude Code, Cursor, Codex, and the rest. Both paths live side by side. # Section: The editor --- Source: https://brilliant.design/docs/editor/canvas-navigation.mdx # Canvas navigation Getting around the canvas: zoom, pan, guides, and the handful of view modes that change how the canvas looks while you work. None of this touches your design; it is all about how you see it. ## Zooming - **In and out:** and , or pinch on a trackpad, or hold and scroll to zoom around your cursor. - **Toggle 100%:** flips between 100% and the zoom you were just at. - **Jump to a level:** through set 100% through 900%. Press the same digit twice to snap back to 100%. - **Fit everything:** zooms out until the whole canvas is in view. This is the fastest way to get un-lost. - **Fit the selection:** zooms to whatever is selected. centers on it without changing zoom. ## Panning - **Space-drag:** hold and drag to pan with any tool active. - **Trackpad:** two-finger drag to pan; plus scroll pans horizontally. - **Middle-mouse drag** pans one-to-one. ## Rulers and guides Toggle the rulers along the top and left edges with . With rulers on, drag from a ruler onto the canvas to drop a guide. Guides are snap targets and never export, so use them freely to line work up. ## Pixel grid Zoom in past 400% and the pixel grid appears; toggle it with . Separately, pixel-grid snapping keeps everything on whole pixels as you draw and move; toggle that with . ## Canvas backdrops The canvas backdrop is yours to set: - **Whiteboard** () puts a solid white surface behind your work. - **Blackboard** () does the same in black. - **Toggle background** () flips between an opaque backdrop and the transparent default. ## Hiding the interface - **Presentation mode** () hides the editing chrome to show your design cleanly. - **Toggle all UI** () hides every toolbar for a distraction-free canvas. - **Toggle sections** () collapses the toolbar panels rather than hiding them, so headers stay reachable. - Hide a single edge with , , or for the left, right, and bottom toolbars. ## Overlay mode Brilliant can float above your other apps as a transparent overlay, so you can trace, annotate, or design on top of anything on screen. Summon and dismiss it with , a global hotkey that works even when Brilliant is not focused. Overlay mode is a macOS feature. See [Overlay mode](/docs/workspace/overlay-mode) for the full guide. ## Next - **Structure your work:** [Frames and auto layout](/docs/editor/frames-and-auto-layout). - **Back to the map:** [The Editor](/docs/editor/overview). --- Source: https://brilliant.design/docs/editor/color-and-fills.mdx # Color and fills Color in Brilliant runs through one control: the color picker. Learn it once and you can paint a solid, build a gradient, drop in an image, or reach for a shader, all from the same place. This page is a guided tour of the picker, top to bottom, followed by a tour of every fill type. ## Where color lives With something selected, the right toolbar shows up to three color sections: - **Fills** paint the inside of an element. Add one with , remove one with . - **Strokes** paint its outline. See [Strokes and effects](/docs/editor/strokes-and-effects). - **Selection colors** appears when you select several elements: it gathers every color they use so you can recolor them all at once. Each row in these sections starts with a small color rectangle on its left. That rectangle is the button that opens the picker. ![The Fills section of the right toolbar, with the color rectangle on the left of the row|360x](/docs/images/bank/right-toolbar/fills/general.webp) ## Opening the picker Click the color rectangle on any fill or stroke row to open the picker. (You can also toggle it from the keyboard with .) The picker opens as a panel anchored to the right toolbar, and everything about that color is now one place. ![The color picker open|420x](/docs/images/bank/color-picker/general.webp) We will walk it top to bottom. ## Step 1: The saturation and brightness square The big square at the top is where you pick the shade. Drag the crosshair: left and right changes saturation, up and down changes brightness. It always shows the current hue, so this is where you dial a color from washed-out to vivid, from dark to light. ![The color picker with the saturation and brightness square highlighted|420x](/docs/images/bank/color-picker/highlight-sb-square.webp) ## Step 2: The hue slider The rainbow slider below the square sets the base hue. Slide it and the square repaints to that hue, so the usual rhythm is: pick a hue here, then refine the shade in the square above. ![The color picker with the hue slider highlighted|420x](/docs/images/bank/color-picker/highlight-hue.webp) ## Step 3: The eyedropper The eyedropper sits at the left end of the hue row. Click it, then click anywhere on the canvas to sample that exact color into the current fill. It is the fastest way to match a color you can already see. The shortcut is . ![The color picker with the eyedropper button highlighted|420x](/docs/images/bank/color-picker/highlight-eyedropper.webp) ## Step 4: The opacity slider The next slider sets the paint's opacity, from fully transparent to fully solid. This fades just this fill or stroke, separately from the element's overall opacity. ![The color picker with the opacity slider highlighted|420x](/docs/images/bank/color-picker/highlight-opacity.webp) ## Step 5: The format field Below the sliders is the value field with a format dropdown. Switch between **Hex**, **RGB**, **HSB**, and **CSS**, then type a value directly, or copy the current one out with the copy button. If you have a color code from a spec or a brand guide, this is where you paste it. ![The color picker with the format dropdown highlighted|420x](/docs/images/bank/color-picker/highlight-format.webp) ## Step 6: Design tokens If your document has a design system with color tokens, they appear as a strip of swatches here. Click one to bind this fill to that token, so it updates everywhere when the token changes. This strip is only present when the document actually defines color tokens. More in the [design system](/docs/design-system/overview) docs. ![The color picker with the design tokens strip highlighted|420x](/docs/images/bank/color-picker/highlight-tokens.webp) ## Step 7: Canvas and recent colors At the bottom, two sets of swatches keep colors close at hand. **Canvas** gathers the colors already used on the current canvas, so your design stays consistent. **Recent** holds up to the last two dozen colors you picked. Click any swatch to apply it. ![The color picker with the Canvas and Recent color swatches highlighted|420x](/docs/images/bank/color-picker/highlight-canvas-colors.webp) ## Fill types A fill does not have to be a flat color. Use the type dropdown at the top of a fill row to change what kind of paint it is, and the picker reflows to match. ![The Fills section of the right toolbar|360x](/docs/images/bank/right-toolbar/fills/general.webp) - **Solid:** one flat color. - **Gradients:** a blend between color stops. Brilliant offers **linear**, **radial**, and **angular** gradients. Add and drag stops right in the picker, and reposition the gradient with its on-canvas handles. - **Image:** drop in a photo or graphic as the fill, with controls to scale, fit, or tile it. - **Shaders:** animated and interactive procedural fills, including Metaballs, Liquid Metal, Iridescent, Liquid Stainless Steel, Dithering, and a Reactive Grid. - **Filters:** effects that transform what is behind or beneath the fill, including Color Adjust, Noise, Halftone, Pixelate, Duotone, Posterize, and Dither. - **Glass and static effects:** Liquid Glass for a refractive surface, plus inner shadow, inner glow, and background blur, which are added as fills so they can sit anywhere in the fill stack. See [Strokes and effects](/docs/editor/strokes-and-effects). Because these are all fills, you can stack several on one element and reorder them: a solid base, a gradient overlay, and a grain filter on top, for example. ## Handy shortcuts - Swap an element's fill and stroke: . - Add a stroke: . Remove one: . ## Next - **Strokes, shadows, and glows:** [Strokes and effects](/docs/editor/strokes-and-effects). - **Bind colors to tokens:** [Design system](/docs/design-system/overview). --- Source: https://brilliant.design/docs/editor/components.mdx # Components A component is a design element you build once and reuse everywhere. You edit the **master**, and every **instance** of it updates to match, except for the bits you deliberately override per instance. This page covers making components, how syncing and overrides behave, component sets with variants, and the handful of Figma habits that do not carry over. If you are coming from Figma, most of this will feel familiar. Skip to [What Brilliant does not have](#what-brilliant-does-not-have) for the differences. ## Masters and instances - A **master** is the source of truth. It is a normal frame on the canvas, marked with a purple label and a filled diamond icon in the layers panel. - An **instance** is a linked copy of a master. It inherits everything from the master until you override a property on it. There is no separate Components panel or Components page. Masters live as regular frames on whatever canvas you put them on. A common convention is to keep them together on one dedicated canvas. ### Create a master 1. Select one or more elements on the canvas. 2. Press `Cmd+Alt+K`, or right-click and choose **Component then Create Component**, or run **Create Component** from the command palette. 3. The selection becomes a master. Its frame label and selection chrome turn purple, and a filled diamond marks it in the layers panel. What gets wrapped depends on your selection: - **A single plain frame** converts in place, with no extra wrapper. - **A single non-frame element, or several elements,** get wrapped in a new frame sized to their combined bounds, and that frame becomes the master. - **A selection spanning multiple parents** produces one component per parent group. You cannot turn a loose child of someone else's instance into a component. Select elements outside any instance, or select the instance root itself (that one is allowed). ### Create an instance 1. Select a master. 2. Run **Create Instance** from the command palette, or right-click and choose **Component then Create Instance**. 3. A linked copy appears, offset slightly down and to the right, and is selected for you. Duplicating or copy-pasting a master also makes an instance, not a second master. So `Cmd+D` on a master gives you an instance linked back to it. Duplicating an instance gives you another instance of the same master. In the layers panel, a filled diamond is a master and a diamond outline is an instance. ![The layers panel showing two component masters with filled purple diamonds and two instances with outline diamonds, beside a regular frame|280x](/docs/images/bank/left-toolbar/components.webp) ## Overrides and syncing Change any property on an instance (its text, a fill color, its size) and Brilliant records that change as an **override**. From then on, edits to the master stop touching that one property on that one instance. Your override wins and survives every future sync. Nothing to mark or confirm, it just happens. Everything else keeps flowing from the master. When you edit a master, every instance updates except where you have overridden: - Non-overridden properties are copied down to each instance. - Children you add to the master appear in every instance. Children you remove disappear from every instance. Reordering carries over too. - Overridden properties are left alone. Almost everything participates in sync: fills, strokes, text content and styling, rotation, flips, geometry, corner radii, layout and frame properties, sizing mode, effects, opacity, and element-level token bindings. Edit the master below (its accent, corner radius, or button label) and watch all three cards move together. This shows the master-edit half of the story; per-instance overrides, where one instance keeps its own value while the master changes, live in the editor. A few things stay independent per instance and never sync from the master: - **Blend mode.** Set it per instance. Changing it on an instance is not even treated as an override. - **Crop, shadow token reference, and the element-level opacity token.** Managed per element. - **The instance's own name.** Renaming a master frame does not rename its instances. Renaming a master's child does propagate to the matching child in each instance. ### Instances keep the master's structure The shape of an instance belongs to its master, so Brilliant blocks structural drift: - You cannot drag a new element into an instance. The drop is rejected. - You cannot drag an element out of an instance to reparent it. - Adding, removing, or reordering children happens on the master and propagates down. The one exception is a **slot**: a child whose subtree the instance fully owns, so sync skips it and you can drop freely inside it. Slots are designated through blueprint authoring; there is no by-hand control to mark an element as a slot. If you need an instance to fully break away, [detach](#detach-reset-and-push) it first. ## Component sets, variants, and properties A **component set** is Brilliant's take on Figma variants. Instead of one master, a set groups several **variants** of the same component (a button's default, hover, and pressed looks, say) along named **properties**. Each property is an axis with a list of values, and every combination of values maps to one variant. An instance of a set carries a **configuration**, one value per property, and shows the matching variant. To change an instance's look you flip its property dropdowns rather than swapping in a different element. ### Create a set 1. Select two or more frames, each of which becomes one variant. 2. Right-click and choose **Create Component Set**, or run it from the command palette. 3. The frames combine into a set, one variant each, inside a dashed boundary. You can also grow a single component into a set later with **Add Variant**. ### The Component inspector section Select a set, a variant, or an instance and a **Component** section appears in the right toolbar. It has three modes: - **Set selected:** an **Add Property** button (name the axis; Size, State, Type, and Variant are suggested) and an **Add Variant** button. Each property shows its name and a button to remove it. - **Variant selected:** one dropdown per property sets that variant's value, its coordinate in the set. If two variants end up with the same combination, a warning appears so you can disambiguate them. - **Instance selected:** one dropdown per property picks the configuration, flipping the instance to a different variant. It re-renders immediately. Nested instances surface their own dropdowns, indented beneath the parent's. ### Use a set 1. Create an instance of the set (Create Instance, or duplicate an existing instance). 2. Select the instance. 3. Flip the property dropdowns in the Component section to choose its variant. That is the whole workflow: one instance, reconfigured by picking values, no detaching or swapping. ## Detach, reset, and push - **Detach** breaks the link to the master. Select an instance and press `Cmd+Alt+B`, or use **Detach Instance** from the command palette or right-click menu. The frame becomes a plain frame, content preserved, and future master edits no longer reach it. - **Reset overrides** restores an instance to match its master. Run **Reset Component Instance Overrides**. Every override clears and the instance re-syncs fresh from the master, children included. - **Push overrides to master** takes the overrides you made on an instance and applies them back to the master, so they become the new default and every other instance syncs to them. Run **Push Overrides to Master**. This only works when the master is on the same canvas as the instance. Ungrouping cleans up links first: ungrouping a master detaches all its instances, then ungroups; ungrouping an instance detaches it, then ungroups. ## Go to the master With an instance selected, run **Go to Master Component** (command palette or right-click). The selection jumps to the master. This works when the master is on the canvas you are currently viewing. ## Cross-canvas instances A master can live on a different canvas than its instances. Keep your masters on one canvas and use instances throughout the rest of the project. - An instance remembers which canvas its master lives on. When you open the instance's canvas, Brilliant finds the master and syncs to its latest values. - Editing a master updates instances on every canvas you currently have open. Instances on canvases you have not opened pick up the changes the next time you open them. - **Push Overrides to Master** and **Go to Master Component** only work when the master is on the canvas you are viewing. Switch to the master's canvas first. - Pasting a master into a different canvas creates an instance pointing back to the original. The master is not duplicated. Two things worth knowing: deleting a master does not immediately detach instances on canvases you do not have open (those clean up next time you open them), and if a canvas full of cross-canvas instances is the first thing you open in a session, its instances may not find their master yet. To be safe, open the master's canvas first. ## Shortcuts | Action | Shortcut | |--------|----------| | Create Component | `Cmd+Alt+K` | | Detach Instance | `Cmd+Alt+B` | | Create Instance | Command palette or right-click | | Create Component Set | Command palette or right-click | | Reset Component Instance Overrides | Command palette or right-click | | Go to Master Component | Command palette or right-click | | Push Overrides to Master | Command palette or right-click | Only Create Component and Detach Instance have default keybindings. The rest are reachable from the command palette or the Component right-click submenu, and you can bind them yourself in the [Shortcuts](/docs/workspace/shortcuts) panel. ## What Brilliant does not have Brilliant components are a master/instance system with overrides, slots, component sets, and cross-canvas references. If you are used to Figma, these concepts are not here: - **Swap instance.** There is no swap-instance command. To point an instance at a different master, detach it and make a fresh instance of the other master. Within a set, you do not swap, you flip the property dropdowns to pick a variant. - **A Components panel or page.** No left-toolbar Components panel, no dedicated Components canvas type. Masters are regular frames; keep them on a canvas by convention. A contextual Component section does appear in the right toolbar when a component, set, variant, or instance is selected. - **Per-property override badges.** Nothing flags which individual property you overrode on an instance. The visible component chrome is the purple frame label and the diamond icon. - **Component descriptions or metadata,** publishing or library export, a master-versus-instance diff view, and exposing nested overrides as instance-level properties. ## Next - **Reuse across canvases:** [Canvases and files](/docs/workspace/canvases-and-files). - **Bind component colors to tokens:** [Design system](/docs/design-system/overview). - **Bring components from Figma:** [Import from Figma](/docs/import/from-figma). --- Source: https://brilliant.design/docs/editor/frames-and-auto-layout.mdx # Frames and auto layout Frames are how you structure a design in Brilliant: they hold elements, clip them, and drive auto layout. This page covers frames versus groups, the auto layout controls, the three sizing modes, layout grids, and masks. ## Frame versus group Both a frame and a group contain other elements, but they behave differently. - A **frame** is a real container. It can be any size, it clips its contents when you ask it to, it can run auto layout, and you can drag elements in and out of it on the canvas. - A **group** is a lightweight bundle. It always hugs its contents, you cannot drag elements out of it on the canvas (reach them through the layers list instead), and it has no layout or clipping of its own. It exists to move and transform a set of elements as one. Groups and frames are not a one-way door. If you give a group a fixed or fill size on either axis, Brilliant promotes it to a frame automatically, since only frames can be sized independently of their contents. Create them from a selection: - **Frame the selection:** . - **Group:** . - **Ungroup:** . Or draw an empty frame with the Frame tool (). ![The layout section of the right toolbar for a frame|60%x](/docs/images/bank/right-toolbar/layout/general.webp) ## Auto layout Auto layout turns a frame into a self-arranging stack: add, remove, or resize a child and the frame reflows. Add it to a selection with , then tune it in the right toolbar. - **Direction:** stack children horizontally or vertically. - **Spacing:** the gap between children (defaults to 10). Set it to a number for a fixed gap, or switch to auto spacing to push children to the edges (equivalent to space-between). - **Padding:** the inset between the frame edge and its children, per side (defaults to 10). - **Alignment:** where children sit along the stack (start, center, end) and across it (start, center, end). - **Wrap:** let children flow onto a new line when they run out of room along the main axis. The controls below drive a real frame on the actual engine: flip its direction, gap, padding, and alignment, and the tiles reflow in place, right here in the page. ## Sizing: hug, fill, fixed Every element in an auto layout frame has a sizing mode for each axis, and the frame itself does too. - **Hug:** shrink to fit the contents. - **Fill:** grow to fill the available space in the parent. - **Fixed:** stay at an explicit width or height. ![Three auto layout frames comparing sizing modes: a hug frame shrinking to fit its children, a fill child stretching into leftover space, and a fixed frame keeping its explicit size](/docs/images/diagrams/auto-layout-sizing.webp) When several children are set to fill along the same axis, they share the leftover space. Give one a higher grow factor and it takes a proportionally larger share. Need one child to sit outside the flow, pinned wherever you drag it? Turn on its ignore-auto-layout option (labeled **Toggle Ignore Auto Layout**), and the frame stops counting it for spacing and sizing while keeping it as a child. ## Clip content By default a frame lets its children spill past its edges. Toggle **clip content** (the scissors control next to the frame type) to crop anything that extends beyond the frame bounds. This is what turns a frame into a window onto a larger design. ## Layout grids Layout grids are on-canvas guides for lining work up: columns, rows, or a uniform square grid. Add one from the layout guides section of the right toolbar, then set its count, size, gutter, and margin. Grids are guides only, so they never export or affect your elements. Show or hide every grid on the canvas with . ## Masks A mask clips a set of elements to the shape of one of them. Select two or more elements and press (or right-click and choose **Use as Mask**). The topmost element becomes the clip shape, and everything below it shows only where the two overlap. Masks are live: edit the mask shape or the masked content any time, and the result updates. ![Before and after applying a mask: a circle sits over gradient artwork with both visible, then the same circle clips the artwork to its outline](/docs/images/diagrams/mask-before-after.webp) ## Next - **Fill and stroke your frames:** [Color and fills](/docs/editor/color-and-fills). - **Turn a frame into a reusable piece:** see components in the [design system](/docs/design-system/overview) docs. --- Source: https://brilliant.design/docs/editor/images-and-crop.mdx # Images and crop An image in Brilliant is just another kind of fill, so it lives on the same rows, and opens through the same picker, as any color. This page covers getting an image onto the canvas, the four ways it can scale inside an element, and crop mode for positioning it by hand. ## Get an image onto the canvas There are three ways to bring an image in: - **Paste** it with . Screenshots, images copied from a browser, and files copied in your file manager all work. - **Drag** an image file out of your file manager and drop it onto the canvas. - **Import** it with , which also handles SVG and `.bl` files. However it arrives, the image lands as a rectangle with an image fill, ready to move, resize, and restyle like any other element. Brilliant reads PNG, JPG, GIF, BMP, and WebP on every platform. On macOS it also reads TIFF, HEIC, HEIF, and AVIF. ## Swap an image with the picker Because an image is a fill, you replace it through the color picker. Click the color rectangle on an image fill row in the right toolbar to open the picker in image mode. New to the picker? Start with [Color and fills](/docs/editor/color-and-fills). In image mode the picker shows a preview of the current image in place of the usual color controls. From there you can: - **Select** a new file from disk by clicking the preview. - **Drop** an image file onto the preview. - **Paste** an image or an image URL with . The expanded image fill row in the right toolbar also has a replace button that opens the same image mode. ## Scale modes Every image fill has a scale mode that decides how the picture sits inside its element. Set it from the **Scale Mode** dropdown in the image fill's expanded row in the right toolbar. - **Fill** (default): the image covers the whole element, cropping whatever overflows. Aspect ratio is preserved. - **Fit**: the image sits entirely inside the element, letterboxed if the two shapes don't match. Aspect ratio is preserved. - **Crop**: you position the image by hand with the crop editor (below). - **Repeat**: the image tiles at its natural pixel size across the element. Switch the mode below and watch one element window hold the same picture four ways. The blue box is the element; the picture is a shape-built stand-in for a photo, because live docs can't load a real raster, but the window really clips it, so fill crops the sides, fit letterboxes the top and bottom, crop reframes a slice, and repeat tiles. Switching from Crop to any other mode discards the crop you set. ## Crop an image in place Crop mode turns the element into a window onto the image: the element stays put while you pan, scale, and rotate the picture behind it. It works on image fills and image strokes alike. ### Enter and exit Crop mode needs the Move tool, so press first, then enter it any of these ways: - **Double-click** an element that has an image fill. - Select the element and press . - Choose **Crop** from the Scale Mode dropdown. Text and vector elements are the exception: a double-click or takes them into text or vector editing instead, so use the Scale Mode dropdown to crop those. A frame with contents drills into its children, but an empty frame with an image fill crops like any shape, which makes it a handy clipped image container. To leave crop mode, press or , double-click again, or click once on empty canvas. ### Move, scale, and rotate the image Inside crop mode you work with two things: the image and the element window around it. - **The image.** Drag anywhere on the picture to pan it. Drag its corners or edges to scale it, holding to keep its aspect ratio. Drag just outside a corner to rotate it. - **The element.** The blue brackets at the element's corners and edges resize the window itself. As you resize, the image stays pinned in place instead of stretching with the window. Behind the window, the full image is shown faded so you can see everything you have to work with. Every pan, scale, and rotate is its own undo step, so walks the crop back one change at a time. ### Keep an image fixed when resizing outside crop mode Normally, resizing an element stretches its image fill along with it. To resize the element but keep the image fixed in the world, hold on a resize handle. On Windows and Linux, hold Ctrl instead. This applies to image fills; the image is converted to Crop mode automatically so its framing is preserved. ## Next - **The picker in full:** [Color and fills](/docs/editor/color-and-fills). - **Strokes, shadows, and glows:** [Strokes and effects](/docs/editor/strokes-and-effects). --- Source: https://brilliant.design/docs/editor/overview.mdx # The Editor This is the heart of Brilliant: the canvas where you draw, and the four toolbars that surround it. This page is the map. It names every region, explains the two things that surprise people coming from other tools (per-canvas undo and no save button), and links out to a page for each part of the editor. If you have never opened Brilliant before, start with the [Quick Tour](/docs/getting-started/first-steps) instead. This page assumes you have the app open and want to know where things are. ![The Brilliant editor with the four toolbars around the canvas](/docs/images/full-ui-overview.webp) ## The four toolbars Brilliant frames the canvas with one toolbar on each edge. Learn what each is for and you always know where to look. - **Top toolbar:** one island spanning the top edge. On the left is the home button; to its right sits a tab for each open project, so you can jump between projects and back to home. In the web editor and the overlay, where there is no tab strip, the island instead shows the breadcrumb for the active canvas, as workspace / folder / canvas. - **Left toolbar:** your project tree and layer list. The top half lists every canvas in the document; the bottom half lists every element in the active canvas, nested the way the canvas nests. On the home screen this panel swaps to home content instead: your avatar, a search field, the **+ New** button, and the Recent / Local / Synced / Cloud switcher. - **Right toolbar:** the property inspector. Its contents follow your selection (fill, stroke, effects, typography, layout, frame properties, and so on). With nothing selected, it shows document-level settings. - **Bottom toolbar:** the tool switcher on the left, and the AI chat input, one away, on the right. - **Canvas:** everything else. This is where you design. You can hide any edge to reclaim space: for the left toolbar, for the right, for the bottom. hides all UI at once for a clean look at your work. ## Everything is a search away The single shortcut worth learning first is . It opens a global palette that finds and runs anything: commands, canvases, layers, fonts, and chats. Forget where a feature lives? Open the palette, type a few letters, press . Every shortcut in this section is also reachable this way, so you never have to memorize more than you want to. ## Where everything lives Each part of the editor gets its own page: - **[Tools](/docs/editor/tools):** every drawing and editing tool, its shortcut, and its modifiers. - **[Selection and editing](/docs/editor/selection-and-editing):** selecting, moving, resizing, rotating, aligning, and Brilliant's per-frame selection model. - **[Frames and auto layout](/docs/editor/frames-and-auto-layout):** frames versus groups, auto layout, sizing modes, layout grids, and masks. - **[Color and fills](/docs/editor/color-and-fills):** the color picker, top to bottom, plus every fill type from solids to gradients to shaders. - **[Strokes and effects](/docs/editor/strokes-and-effects):** stroke options, shadows, glows, blurs, blend modes, and opacity. - **[Text](/docs/editor/text):** creating text, sizing modes, typography, and per-range styling. - **[Vectors](/docs/editor/vectors):** the pen and pencil, vector edit mode, nodes and handles, and boolean operations. - **[Components](/docs/editor/components):** masters and instances, component sets and variants, overrides, and sync. - **[Shaders and filters](/docs/editor/shaders-and-filters):** animated GPU shader fills and filter fills. - **[Images and crop](/docs/editor/images-and-crop):** getting images onto the canvas, the four scale modes, and crop mode. - **[Canvas navigation](/docs/editor/canvas-navigation):** zoom, pan, rulers, guides, and the canvas backdrops. ## Undo is per canvas Every canvas keeps its own undo history. Switch to another canvas, make changes, come back, and your first canvas still has its full history waiting: nothing is shared or clobbered across canvases. Switching canvases is itself never an undoable step. - undoes. - redoes. Undo covers your edits and the AI's alike, so experiment freely and roll back what you do not like. ## Your files save themselves There is no save button, and you never need one. Every change is written to the `.bl` file on disk shortly after you stop editing (a brief debounce, so a burst of edits writes once), so closing the app or losing power costs you nothing. The document name in the top toolbar always reflects what is on disk. Because `.bl` files are plain-text Blueprint, they drop cleanly into git. You can branch designs, review them in pull requests, and `git blame` a stray color change the same way you would with code.
Tip: For a snapshot you can return to, duplicate the `.bl` file in your file manager or commit it. Autosave covers "I closed the laptop"; a copy or a commit covers "I want a version to go back to."
## Next - **Meet the tools:** [Tools](/docs/editor/tools). - **Learn the selection model:** [Selection and editing](/docs/editor/selection-and-editing). - **Design with AI:** [Built-in AI](/docs/ai/overview). --- Source: https://brilliant.design/docs/editor/selection-and-editing.mdx # Selection and editing Selecting things in Brilliant works the way you expect from any design tool, with one twist that trips up people coming from Figma: selection is grouped by frame. This page covers the basics, that twist, and every way to move, resize, rotate, align, and measure what you have selected. ## Selecting - **Click** an element to select it. - **Shift-click** to add or remove elements from the selection. - **Drag on empty canvas** to marquee-select everything the rectangle touches. - **Double-click** to drill into a frame or group and select the element under your cursor directly. - **Select all** in the current context with . Once something is selected, drill and step through the hierarchy with the keyboard: - drills into the selected frame and selects its contents. walks back up to the parent. - and cycle the selection through sibling elements at the same level. ![The canvas with elements selected|70%x](/docs/images/selection-per-parent.webp) ## The per-frame selection model Here is the twist. When your selection spans more than one frame, Brilliant draws a separate selection rectangle for each frame, each with its own handles. Select two elements in Frame A and three in Frame B, and you see two rectangles, not one box around all five. This is deliberate, and it makes multi-frame edits behave sanely. Every operation runs once per frame, in that frame's own coordinate space: - **Align Left** aligns the elements in Frame A to each other, and the elements in Frame B to each other, independently. - **Resize** drags each frame's selection by its own handles. When everything you have selected lives in the same parent, this is invisible: you get one rectangle and it behaves exactly as you would expect. The per-frame behavior only shows up when a single selection reaches across frames. ## Moving - **Drag** with the Move tool. - **Nudge** with the arrow keys, one pixel at a time. Hold with an arrow key to jump ten pixels. - **Duplicate in place** with , or hold and drag to pull out a copy. ## Resizing and rotating - **Resize** by dragging any handle. Hold to keep the proportions, or to resize from the center. - **Rotate** by moving your cursor just outside a corner handle until it turns into a rotate cursor, then drag. Hold to snap to 15-degree steps. - **Scale everything** (including stroke weight, corner radius, and text) instead of stretching the box by switching to the Scale tool with . ## Aligning and distributing Alignment runs per frame, so it does the right thing even across a multi-frame selection. The align buttons live in the right toolbar, and each has a shortcut: - **Align edges:** left , right , top , bottom . - **Align centers** on a shared horizontal axis with , or a shared vertical axis with . - **Center within the parent:** horizontally with , vertically with . - **Distribute** three or more elements into even gaps: horizontally with , vertically with . ## Measuring with Alt Hold and hover over another element while you have a selection. Brilliant overlays the exact distance between them, with a solid measurement line and a pixel label. Hover a frame that contains your selection, and it shows the padding on all four sides instead. Nothing changes on the canvas: measurements are read-only, and they update live as you nudge. ## Snapping As you drag, resize, or create, Brilliant snaps to the things around you: edges and centers of nearby elements, matching gaps between siblings, and equal sizes. Snapping stays within the frame you are working in, so it never yanks an element toward something in a different frame. A pixel-grid snap keeps everything on whole pixels; toggle it with . ## Z-order, hiding, and deleting - **Bring to front** or **send to back** . Step one layer with and . - **Hide or show** the selection with . - **Flip** horizontally with or vertically with . - **Delete** with . Standard , , and copy, cut, and paste. ## Next - **Group and contain elements:** [Frames and auto layout](/docs/editor/frames-and-auto-layout). - **Move around the canvas:** [Canvas navigation](/docs/editor/canvas-navigation). --- Source: https://brilliant.design/docs/editor/shaders-and-filters.mdx # Shaders and filters A fill doesn't have to be a flat color. Brilliant ships two families of procedural fills: shaders, which paint animated patterns like liquid metal and merging blobs, and filters, which reprocess whatever sits beneath them into grain, halftone dots, or a duotone. Both live in the same place as your solid colors and gradients: the fill type dropdown. ## Where they live Shaders and filters are fill types, so you reach them the same way you reach a gradient or an image: open the [color picker](/docs/editor/color-and-fills) on a fill or stroke row, then use the type dropdown at the top. The menu is grouped: - **Colors:** Solid, Linear, Radial, Angular. - **Static:** Image, Inner Shadow, Inner Glow, Background Blur, Liquid Glass. - **Animated** and **Interactive:** the six shaders. - **Filters:** the seven filter fills. Pick a type and the picker reflows to show that fill's own controls. Switch types as often as you like without deleting the row. Because these are fills, they work on fills and on strokes, and on whole text elements (the pattern renders through the glyphs). Individual character runs inside a text block stay solid-color only. ![The Fills section of the right toolbar, where the type dropdown lives|360x](/docs/images/bank/right-toolbar/fills/general.webp) ## Shader fills Shaders are animated, procedural patterns. There are six, split into two groups in the picker. ### The six shaders - **Metaballs** (Animated): organic blobs that merge and split. Takes up to five colors. - **Liquid Metal** (Animated): metallic reflections with chromatic shift and flowing stripes. Two colors. - **Iridescent** (Animated): folded, oil-slick iridescence with a metallic finish. Three colors. - **Liquid Stainless Steel** (Animated): flowing chrome with specular highlights. Two colors. - **Dithering** (Animated): procedural noise rendered as a retro two-tone dither. Two colors. - **Reactive Grid** (Interactive): an animated grid of nodes and connecting lines. Up to five colors. A still can't show what a shader actually does. Pick a shader below and set its speed: Slow, Fast, or Frozen. Frozen is literally a speed of 0, the freeze you reach for when a shader should hold still. The fill animates live on the real engine. ### The controls you get Click a shader fill row to expand it. Every shader has: - A **Colors** section of swatches. Click a swatch to edit it in the picker, and use the plus and minus controls to add or remove colors up to that shader's maximum. Any shader color can be bound to a design token, so it follows your brand and mode. - A **Speed** slider. Higher is faster; set it to 0 to freeze the pattern in place. - A **Transform** section that scales, offsets, and rotates the pattern inside the element without moving the element itself. Then each shader adds its own sliders. The ones you'll actually reach for: - **Metaballs:** Count, Size. - **Liquid Metal:** Shape, Softness, Repetition, Shift Red, Shift Blue, Distortion, Contour, Angle. - **Iridescent:** Shape, Intensity, Folds, Angle, Complexity, Metallic. - **Liquid Stainless Steel:** Shape, Flow, Roughness, Distortion, Depth, Angle. - **Dithering:** Shape (the noise pattern: Simplex, Warp, Dots, Wave, Ripple, Swirl, Sphere), Dither (Random, 2x2, 4x4, 8x8), Size. - **Reactive Grid:** Density, Distortion, Radius. Liquid Metal, Iridescent, and Liquid Stainless Steel add a **Shape** dropdown with three options: **Element** makes the pattern follow the element's outline, **Metaballs** masks it with an internal blob shape (which adds Ball Count and Ball Size sliders), and **None** fills the bounds flat. ### Presets Most shaders open with a **Presets** dropdown of curated color-and-parameter combinations: Metaballs has Lava Lamp, Deep Ocean, Galaxy, and more; Liquid Metal has Mercury, Molten Gold, Copper; Reactive Grid has Neon Circuit, Blueprint, Laser Grid. Pick one as a starting point, then tune it. The reset button next to the dropdown restores the shader's defaults. ### Animation Shaders animate by default. Each shader fill has a play/pause toggle in its expanded view, so you can freeze one fill while others keep moving. To pause or resume every shader on the canvas at once, run **Toggle Shader Animations** from the command palette. Neither toggle has a default keyboard shortcut. ## Filter fills A filter fill doesn't paint its own color: it reprocesses the fills stacked beneath it. Drop a grain filter over a photo and the photo gets grain; drop it over a gradient and the gradient gets grain. There are seven. ### The seven filters - **Color Adjust:** photo-style grading (exposure, contrast, saturation, temperature, vignette, and more). Non-destructive, like an adjustment layer. - **Noise / Grain:** film-like grain, from subtle texture to heavy static, in color or monochrome. - **Halftone:** print-style dot patterns, with a CMYK mode that mimics four-color offset printing. - **Pixelate:** mosaic pixelation with square, hexagonal, diamond, circle, or triangle cells. - **Duotone:** map the tones beneath it to two colors (add a third for a tritone). - **Posterize:** collapse smooth gradients into flat bands of color. - **Dither:** ordered dithering with Bayer and noise patterns for a retro, low-bit look. ![The seven filter fills applied to one reference image by the engine: color adjust, noise and grain, halftone, pixelate, duotone, posterize, and dither, next to the unfiltered original](/docs/images/diagrams/filter-fills-grid.webp) ### Ordering filters in the fill list A filter restyles the fills beneath it in the fill list, so position matters: to affect the whole element, drag the filter row to the top of the Fills section. Stack two filters and each one only restyles the fills in its own run, from the filter below it up to itself, so they don't double-process the bottom of the stack. Anything sitting above the top filter paints on untouched. Filters expose their own parameters when expanded, plus a **Presets** dropdown and a blend mode that sets how the filtered result composites with the content below. Halftone, Duotone, and Dither also carry color swatches, where each swatch's opacity controls how strongly that color tints the result. Hover a preset to preview it live before you commit. ### Filters and photos Filters work on any fill, but they shine on images. Add an image fill, then stack a Duotone or Halftone above it to restyle the photo without touching the original. See [Images and crop](/docs/editor/images-and-crop) for working with image fills. ## Practical notes - **Stable across zoom:** grain, dots, and cell sizes are measured in logical pixels, so a pattern looks the same as you zoom in and out. - **Exports match the canvas.** PNG, JPEG, and WebP capture exactly what you see, with animated shaders rendered at the frame you export. Video export (MP4, MOV) captures the animation across the clip. - **Vector export flattens them.** SVG and PDF can't express these procedural fills, so any element using a shader or filter is embedded as a raster snapshot. Export to PNG if you need the exact look. ## Next - **The picker and every fill type:** [Color and fills](/docs/editor/color-and-fills). - **Strokes, shadows, and glows:** [Strokes and effects](/docs/editor/strokes-and-effects). - **Image fills and cropping:** [Images and crop](/docs/editor/images-and-crop). --- Source: https://brilliant.design/docs/editor/strokes-and-effects.mdx # Strokes and effects Strokes outline your shapes; effects add depth with shadows, glows, and blurs. This page covers both, plus the one distinction worth understanding up front: some "effects" are really fills, and that changes where they sit in the stack. ## Strokes Add a stroke to the selection with , or from the Strokes section of the right toolbar. Remove one with . An element can carry several strokes at once, stacked in the panel. Each stroke has: - **Weight:** the thickness in pixels. - **Position:** whether the stroke sits **inside** the shape edge, on **center** (the default, straddling the edge), or **outside** it. - **Caps:** how open path ends are drawn (flat, round, or square), plus arrow and dot end-markers for lines. - **Dashes:** a dash pattern for dashed and dotted lines. Leave it empty for a solid stroke. Strokes take the same fills a shape does, so a stroke can be a solid color, a gradient, or an image. See [Color and fills](/docs/editor/color-and-fills). Swap an element's fill and stroke with . ## Effects versus fill-based effects Brilliant splits what other tools lump together as "effects" into two groups, and they behave differently. **Effects** live in the Effects section of the right toolbar and sit at a fixed place in the stack: shadows and glows always render behind the element, and a blur always wraps the whole element. ![The effects section of the right toolbar|360x](/docs/images/bank/right-toolbar/effects/general.webp) | Effect | What it does | Key defaults | | --- | --- | --- | | Drop shadow | Casts a shadow behind the element | Offset 0, 4; blur 8; 25% black | | Outer glow | Radiates light outward from the edge | Blur 8; 60% white; Screen blend | | Element blur | Blurs the whole element | Radius 4 | Drive all three on one card below: deepen the drop shadow, light the outer glow, and blur the floating orb. Each control recompiles the card in place on the real engine. **Fill-based effects** are added as fills, not effects, which means they interleave with your other fills in the fill stack. An inner shadow can sit between two solid fills, for instance. | Fill effect | What it does | Key defaults | | --- | --- | --- | | Inner shadow | Casts a shadow inside the edge | Offset 0, 2; blur 4; 50% black | | Inner glow | Glows inward from the edge | Blur 4; 60% white; Screen blend | | Background blur | Frosts whatever is behind the element | Radius 8 | | Liquid Glass | A refractive glass surface | See [Color and fills](/docs/editor/color-and-fills) | Offsets and blur run to 200 pixels; spread runs from -100 to 100. Background blur has no color because it samples the canvas behind the element rather than painting its own. ## Blend modes Every fill, stroke, effect, and element itself can take a blend mode, which controls how it mixes with what is underneath. Normal is the default. The full set of 16 runs from Multiply, Screen, and Overlay through Color Dodge, Difference, and the hue/saturation/color/luminosity modes. Set a blend mode from the small mode control next to the item it applies to. ![All 16 blend modes in Brilliant: the same magenta circle composited over the same gradient backdrop, once per mode, from Normal through Luminosity](/docs/images/diagrams/blend-modes-grid.webp) ## Opacity Two opacities are in play, and they compound: - **Element opacity** fades the whole element, contents and all. It lives at the top of the right toolbar. - **Fill and stroke opacity** fades a single paint, set from that paint's color picker (see [Color and fills](/docs/editor/color-and-fills)). Nudge the selection's opacity from the keyboard: raises it and lowers it, and through jump to preset levels. ## Next - **Fill types in depth:** [Color and fills](/docs/editor/color-and-fills). - **Style your text:** [Text](/docs/editor/text). --- Source: https://brilliant.design/docs/editor/text.mdx # Text Text in Brilliant is fully editable type with real typographic controls and per-character styling. This page covers creating it, the sizing modes, the typography panel, and how to restyle just part of a string. ## Creating text Grab the Text tool with , then click anywhere on the canvas to drop a cursor and start typing. New text starts at 24 pixels in the system font and grows to fit as you type. Press or click away when you are done. To edit existing text, double-click it. ## Sizing modes How a text box resizes as its content changes is set by its sizing mode, in the right toolbar: - **Auto size:** the box hugs the text on both axes. The text never wraps; the box just grows. - **Auto height:** the width is fixed, so text wraps, and the box grows taller to fit. - **Auto width:** the height is fixed and the box grows wider as you type, without wrapping. - **Fixed size:** both dimensions are locked, and text is clipped if it overflows. Dragging a side handle on the canvas locks that axis, so resizing a hugging box by its side switches it to auto height. Sizing is really about how the box reacts as the content changes, which a fixed picture can't show. Pick a mode below and flip the content between short and long: the same box hugs, wraps, grows wide, or clips, live on the real engine. ![The typography section of the right toolbar|60%x](/docs/images/bank/right-toolbar/typography/general.webp) ## The typography panel With a text element selected, the right toolbar shows the full typography section: - **Font family** and **weight.** The weight dropdown lists only the weights the chosen font actually ships. - **Size,** **line height,** and **letter spacing.** - **Bold, italic, underline,** and **strikethrough.** - **Alignment:** left, center, or right. - **Text case,** **max lines** (truncate with an ellipsis past a line count), and **OpenType features** for fonts that offer them. Common styling has keyboard shortcuts: bold , italic , underline . Align with , , and for left, center, and right. Bump the size with and . ## Styling part of a string You do not have to style a whole text element the same way. While editing, select a range of characters and change the font, weight, size, color, italic, underline, strikethrough, or letter spacing on just that range. Each override sticks to those characters, so one text element can hold a bold word, a colored link, and a larger first letter, all at once. ## Fonts Open the font picker with . It searches a large curated library of web fonts plus the fonts installed on your machine. Start typing to filter, and the preview updates as you go. ## Outlining text to vectors When you need the letterforms as editable shapes (to distort them, cut them out, or guarantee they render identically anywhere), convert text to vectors: - **Outline Text** () turns the selection into a group of vector paths, one per glyph. - **Flatten** () merges those glyphs into a single path. This is a one-way trip: outlined text is no longer editable as type, so keep an original if you might revise the wording. Outlining is available on macOS and Windows. ## Next - **Bend the outlines:** [Vectors](/docs/editor/vectors). - **Color your type:** [Color and fills](/docs/editor/color-and-fills). --- Source: https://brilliant.design/docs/editor/tools.mdx # Tools Every tool Brilliant gives you, what it does, and the one-key shortcut that gets you there. The tool switcher lives at the left end of the bottom toolbar. Click a tool, or press its shortcut. After you draw, Brilliant drops you back to the Move tool automatically, so you rarely need to reach for it. Press any time to return to Move, or to bail out of whatever you are doing. ![The bottom toolbar with the tool switcher on the left and the AI input beside it|560x](/docs/images/bank/bottom-toolbar/general.webp) ## Selecting and moving - **Move** (): the default tool. Click to select, drag to move, drag a handle to resize. - **Scale** (): a toggle layered on top of Move. In Scale mode, dragging a handle scales the whole element, including stroke weight, corner radius, and text size, instead of just stretching its box. Press again to switch back. - **Hand** (): pan the canvas by dragging. You usually will not need it, since holding pans with any tool active. ## Drawing shapes - **Rectangle** (): draw a filled rectangle. Press for an outline-only version (a stroke, no fill). - **Circle** (): draw a filled ellipse. gives you the outline-only version. - **Line** (): draw a straight line. - **Arrow** (): a line with an arrowhead. - **Frame** (): draw a frame, Brilliant's container element. Frames hold and clip other elements and power auto layout. See [Frames and auto layout](/docs/editor/frames-and-auto-layout). - **Text** (): click to place a text cursor and start typing. See [Text](/docs/editor/text). ## Drawing paths - **Pen** (): click to drop precise nodes, or click and drag to pull out curve handles. The pen is how you draw exact vector paths. - **Pencil** (): draw a freehand path that Brilliant smooths into a vector as you go. Both feed into vector editing, covered in [Vectors](/docs/editor/vectors). ## Capturing the screen - **Snip** (): drag a rectangle anywhere on screen to grab that region as an image element on the canvas. Snip is macOS only. ## Modifiers while you draw These hold-keys work across the drawing tools: - **Constrain** (): hold Shift while drawing. Rectangle and Frame snap to a square, Circle to a perfect circle, and Line, Arrow, and Pencil snap to 45-degree angles. On the pen, Shift constrains node placement and handles to 15-degree steps. - **Reposition mid-draw** (): hold Space while dragging out a shape to slide the whole in-progress shape to a new spot, then release to keep sizing. - **Independent pen handles** (): hold Option while dragging a pen handle to break it away from its mirror, so the two sides of a node curve independently. ## Combining and converting shapes These live in the right-click context menu (and have shortcuts once you know them). Select the elements first. - **Boolean operations:** select two or more elements, right-click, and open the **Boolean** submenu for Union, Subtract, Intersect, and Exclude. The shortcuts are (Union), (Subtract), (Intersect), and (Exclude). Booleans stay live, so you can still edit the shapes inside them. - **Mask:** select two or more elements and choose **Use as Mask** (). The topmost element becomes the clip shape for the rest. More in [Frames and auto layout](/docs/editor/frames-and-auto-layout). - **Flatten** (): merge the selection into a single vector path. - **Outline Text** (): convert selected text into editable vector outlines, one path per glyph. See [Text](/docs/editor/text).
Tip: Forgot a shortcut? Open global search with and search the tool or action by name (the command palette is ). It runs the same command and reminds you of the key.
## Platform notes Shortcuts here show the macOS chords. On Windows and Linux, becomes Ctrl. Snip (screen capture) is macOS only. Outline Text works everywhere, but on Windows its default chord is unbound to avoid a conflict, so reach it from the context menu or the command palette. --- Source: https://brilliant.design/docs/editor/vectors.mdx # Vectors Under every shape in Brilliant is an editable vector path. This page covers drawing paths from scratch, editing their nodes and handles, and how fills and strokes attach to the parts of a path. ## Drawing paths - **Pen** (): click to drop a node, or click and drag to pull out a pair of curve handles as you place it. Keep clicking to extend the path, and click the first node again to close it. - **Pencil** (): draw freehand, and Brilliant smooths your stroke into a clean vector when you release. Hold while drawing to constrain angles. Press to finish the current path. ## Vector edit mode Any shape, not just pen drawings, can be edited as a vector. Select it and press , or double-click it, to enter vector edit mode. Its nodes and edges become directly editable. (Text is the exception: outline it to vectors first, covered in [Text](/docs/editor/text).) To leave, press . The first press clears any selected nodes; a second press exits vector edit mode and returns you to the Move tool. Double-clicking or dragging outside the shape also exits. ## Nodes and handles Inside vector edit mode you work with nodes (the points) and handles (the levers that curve the path through a node). Drag a node to move it, and drag its handles to reshape the curve on either side. Each node has a type that decides how its two handles relate: - **Straight:** a sharp corner with no handles. - **Mirrored:** a smooth node whose handles mirror each other in both direction and length. - **Asymmetric:** a smooth node whose handles stay in line but can differ in length. - **Disconnected:** a node whose two handles move fully independently, for a sharp change of direction that is still curved. Flip between the three curved types below and watch the same curve reshape through its center node: mirrored gives a smooth symmetric arc, asymmetric a smooth but lopsided one, and disconnected a broken corner. The two hollow rings are that node's control handles, drawn just as the editor shows them. Hold while dragging a handle to move just that one, breaking it away from its partner. ## Adding and removing nodes - **Add a node:** hover over an edge until a plus marker appears, then click to insert a node there. The curve keeps its shape. - **Remove a node:** select it and press . A node with an edge on each side dissolves and its neighbors reconnect, so the path stays whole. Delete enough that fewer than two nodes remain and the whole element goes. ## Region fills and edge strokes Vectors in Brilliant fill and stroke by part, which is more flexible than one fill for the whole shape. Any enclosed region of a path can take its own fill, and every edge can take its own stroke. So a single path can have several filled regions and separately styled outlines. In practice: a closed path can carry both a fill and a stroke, while an open path shows only its stroke. Click a region or an edge (rather than a node) to select just that part, then set its color from the right toolbar. See [Color and fills](/docs/editor/color-and-fills). ## Combining paths Boolean operations build complex shapes from simple ones. Select two or more elements and combine them (the operations also live in the right-click Boolean submenu): - **Union** (): merge into one shape. - **Subtract** (): cut the top shapes out of the bottom one. - **Intersect** (): keep only the overlap. - **Exclude** (): keep everything except the overlap. ![Boolean operations in Brilliant: a source circle and rounded square, then the four results, Union, Subtract, Intersect, and Exclude, each a boolean parent with both shapes still editable inside](/docs/images/diagrams/boolean-ops.webp) Booleans stay editable: you can still move and reshape the elements inside them. When you want to bake the result into a single flat path, use **Flatten** (). ## Next - **Fill your regions and strokes:** [Color and fills](/docs/editor/color-and-fills). - **Back to the tools:** [Tools](/docs/editor/tools). # Section: Built-in AI --- Source: https://brilliant.design/docs/ai/design-tutorials.mdx # Design tutorials Two end-to-end walkthroughs you can follow prompt by prompt. The first builds a landing hero and refines it by talking to the agent. The second builds a dashboard card wired to your design system, so a single brand switch re-skins it. Both use the built-in chat. It runs on Brilliant AI by default, so you can follow along without connecting anything; to use your own AI instead, see [Connect an AI Provider](/docs/getting-started/connect-provider). Everything the agent makes lands as native, editable elements, so you can keep nudging it by hand at any point. ## Tutorial 1: A landing hero, prompt by prompt The goal: a hero section with a headline, supporting copy, two buttons, and space for a product shot. You'll prompt it into existence, then shape it with follow-ups. ### 1. Open the chat and describe the outcome Press anywhere on the canvas to focus the AI input, or click it in the bottom toolbar. Then describe what you want in plain language. Don't spell out steps or tools, just the result. ![The slash shortcut focusing the AI input at the bottom of the canvas|50%x](/docs/images/chat-open-slash.webp) Send this with : > Create a landing page hero for a habit-tracking app called Momentum. A big headline, one line of supporting copy, a primary "Get Started" button and a secondary "Watch Demo" button, and room for a product screenshot on the right. The agent builds the hero as it goes, streaming each piece onto the canvas so you can watch the layout come together. When it finishes you have a real frame with real text and button elements, not a flattened picture. ![The hero the agent built, live and editable on the canvas|75%x](/docs/images/tutorial-hero-result.webp) ### 2. Refine the copy Follow-ups see the whole conversation and the current canvas, so you can speak in deltas instead of re-describing the hero: > Rewrite the headline to be punchier and under six words. > Make the supporting line more concrete: mention daily streaks. Each message edits the same elements in place. Nothing gets recreated from scratch. ### 3. Adjust spacing and layout Same idea, now for structure: > Add more breathing room between the headline and the buttons. > Center the whole hero and give it a bit more top padding. ### 4. Select an element, then talk about "this" The agent reads your current selection, so you rarely have to name things. Click the primary button on the canvas to select it, focus the chat, and say: > Make this button a warmer orange and give it a subtle shadow. Because the button is selected, "this" is unambiguous. The change applies to exactly what you picked. This works for a single element, a multi-selection, or a whole frame. ### 5. Reference other elements by name When you want to point at something you don't have selected, type `@` and pick it from the list. Mentions attach the element's structure and a render to your message, so the agent can match against it: > Match the secondary button's corner radius and height to @Get Started. You can mention more than one element in a prompt to compose them, like pulling one element's styling onto another. Mentions cover elements, not whole canvases or files. ### 6. Branch off to try a variation Happy with the hero but want to explore a bolder direction without losing it? With the chat focused, press to start a fresh chat, then prompt the alternate take there. Old chats stay live, so you can jump between parallel experiments. Each chat also keeps its own undo, and any turn you don't like has a **Revert** control on its card that rolls back just that turn's changes. More on running several chats at once in [Sessions and chat](/docs/ai/sessions-and-chat).
Tip: Stuck on wording for a follow-up? Describe the problem, not the fix: "the buttons feel cramped" works as well as an exact spacing value. The agent picks the tools.
## Tutorial 2: A dashboard card that re-skins with your brand This one leans on your design system. Every fresh project ships with a fully populated set of tokens (colors, spacing, radius, type), so the payoff here is real: build the card once bound to those tokens, and switching theme or brand restyles it everywhere at once. If tokens are new to you, skim the [design system overview](/docs/design-system/overview) first. ### 1. Ask for the card, bound to tokens Focus the chat and describe the card, and explicitly ask it to use your design system: > Build a dashboard stat card: a small label, a big metric number, a trend indicator, and a sparkline area below. Use my design system tokens for the colors, spacing, corner radius, and type so it stays on-brand. The agent has your token catalog in context, so it paints surfaces with roles like `color.surface` and text with `color.text.primary`, sizes gaps with `spacing` tokens, and rounds corners with `radius` tokens instead of hard-coded values. The result looks the same as hand-picked colors, but each property is bound to a named decision. The full catalog of what's available is in [Tokens](/docs/design-system/tokens). ![The chat panel with the stat card taking shape on the canvas|420x](/docs/images/bank/ai-chat-panel/general.webp) ### 2. Multiply it, still on tokens Ask for a row and the spacing stays token-driven: > Add two more of these cards in a row with equal spacing between them. Because the gaps reference the same `spacing` token, they all move together the day you rescale your spacing. ### 3. Switch theme and watch it re-skin Here's the point of binding to tokens. With nothing selected, open the **Design system** section at the top of the right toolbar and switch the theme dropdown from light to dark. Every card follows instantly: surfaces darken, text lightens, and the accent tones flip, because each property resolves through a token rather than a fixed hex. ![The Design system section in the right toolbar, where you switch theme and brand|360x](/docs/images/bank/right-toolbar/general.webp) You didn't touch the cards. The tokens did the work. ![The same stat card under two brands, re-skinned from one switch|75%x](/docs/images/tutorial-brand-reskin.webp) ### 4. Switch brands, or spin up a new one Brands work the same way. A brand is a sparse overlay: a different seed color and a few overrides, with everything else inherited. If your project already defines brands, pick another from the brand dropdown in that same Design system section and the whole row re-skins. Don't have a second brand yet? Ask the agent to make one: > Add an alternate brand called "sunset" with a warm orange primary, and switch this canvas to it. It writes a brand file into your project's `Styles/` folder and flips the canvas over to it. Swap back to `default` any time from the brand dropdown. The mechanics of overlays, modes, and how the cascade resolves are in [Modes & Brands](/docs/design-system/modes-and-brands).
Tip: If a color didn't re-skin with the rest, it was probably set to a raw hex instead of a token. Select that element and tell the agent to "bind this fill to the closest design system token," then switch modes again.
## Where to go next - **The full range of what the agent can do:** [What the AI can do](/docs/ai/what-the-ai-can-do). - **Run several chats in parallel and revert turns:** [Sessions and chat](/docs/ai/sessions-and-chat). - **Go deeper on tokens, modes, and brands:** [Tokens](/docs/design-system/tokens) and [Modes & Brands](/docs/design-system/modes-and-brands). --- Source: https://brilliant.design/docs/ai/overview.mdx # Built-in AI Brilliant has an AI agent built into the canvas. It reads your file, sees your selection, and runs real design commands on your behalf: creating elements, editing properties, running layout commands, searching across canvases, and exporting. You describe the outcome, and it builds it while you watch, in native elements you can keep editing by hand. This page is the map of what the built-in AI is and how it fits together. If you just want to send your first prompt, jump to [Your First AI Chat](/docs/getting-started/first-ai-chat). ## Brilliant AI or Your own AI The chat has two power sources. **Brilliant AI** is the default: Brilliant-provided models, included with every plan (usage shows as a percent used and a reset date), that work on your first message with no setup. The request routes through Brilliant's servers to Google. **Your own AI** (your own keys and coding agents) is the alternative: bring an Anthropic, OpenAI, Google, or OpenRouter key, or your local Claude Code login, and that request goes straight from the app to your provider. A key you bring never routes through Brilliant's servers, and you pay your provider directly. The details of exactly what is sent, where it goes, and what never leaves are in [Privacy and consent](/docs/ai/privacy-and-consent). On the Free plan you get Brilliant AI every month (the default, no setup), plus 10 built-in chat messages a day on your own key. Paid plans raise your Brilliant AI usage: Personal $20/month (the base), Pro $50/month (5x), Max $100/month (10x), Max 20x $200/month (20x), monthly only, each a standalone price that includes everything in Personal, and make the built-in chat unlimited on your own AI. When the month's usage runs out, the next send opens the upgrade page, a page on the command-palette shell (the same overlay as sign in), which names when your usage resets and offers the next paid rung or setting up your own AI, and external agents over MCP are never capped on any plan. Details are in [Plans and billing](/docs/platform/plans-and-billing). To bring your own AI instead of Brilliant AI, connect a provider in a couple of clicks, or route through a Claude Code subscription you already have. See [Providers and models](/docs/ai/providers-and-models), which deepens the quick-start in [Connect an AI Provider](/docs/getting-started/connect-provider). ## What it can do on the canvas Once a session is running, the AI can: - **Create designs** as native, editable elements: UIs, pages, dashboards, illustrations, diagrams, wireframes. - **Edit existing work**: change properties, reparent, rename, delete, reorder. - **Run app commands** (align, distribute, auto layout, components, and more), each with full undo. - **Read your selection and search** the canvas by name, text, type, color, or component. - **Export** to PNG, JPEG, WebP, SVG, PDF, or HTML/React. - **Generate images and vectors** and place them on the canvas. The full tour with before-and-after examples is in [What the AI can do](/docs/ai/what-the-ai-can-do). ## How it differs from an external agent Brilliant works two ways, and both are valid: - **The built-in chat** (this section) is docked in the app. You get selection-aware inputs, per-chat undo, color and position pickers, and progressive previews that stream onto the canvas as each step lands. It runs on Brilliant AI by default, or on your own AI. - **An external agent over MCP** ([Use with AI agents](/docs/agents/overview)) drives the same canvas from your own editor, like Claude Code or Cursor. Same canvas, same undo stack. Many designers keep both around. ## Where it lives The AI input sits in the bottom toolbar. Press `/` on the canvas, or click the field, to focus it and open the chat panel. Sessions dock as tabs, run in parallel, and persist between launches. The full walkthrough of panels, tabs, and shortcuts is in [Sessions and chat](/docs/ai/sessions-and-chat). ## Try it in Playground Brilliant AI is the default, so your first message just works with no provider connected. If you would rather watch first, **Playground mode** replays bundled demo conversations end to end, so you can see how the agent behaves before spending Brilliant AI usage or connecting a key. It is a toggle in Settings, and the `/playground` page is the guided demo for visitors. More on setup in [Connect an AI Provider](/docs/getting-started/connect-provider). ## Next - **Send your first prompt:** [Your First AI Chat](/docs/getting-started/first-ai-chat). - **Connect a model:** [Providers and models](/docs/ai/providers-and-models). - **See the full capability set:** [What the AI can do](/docs/ai/what-the-ai-can-do). --- Source: https://brilliant.design/docs/ai/privacy-and-consent.mdx # Privacy and consent The built-in chat is designed so you always know what leaves your machine. This page is precise about it: where traffic goes, what travels with each message, what never does, and how your keys are handled. ## Where AI traffic goes The chat has two lanes. On **Brilliant AI** (the default), the request routes through Brilliant's proxy to Google's Vertex AI under Brilliant's own account. What Brilliant records is the usage rows that meter your plan: per-request metadata like the model, the token counts per class, the computed cost, your plan and cycle, and timestamps, never your prompt text or your design content. On **Your own AI** (your own keys and coding agents), every request goes straight from the app to the provider you chose, using your own API key or your local Claude Code CLI; a key you bring never routes through Brilliant's servers, you pay your provider directly, and your prompts reach only that provider. ## Where your keys live Keys are stored locally in your OS credential store (macOS Keychain, Windows Credential Manager) and are sent only to that provider's own API endpoint. Brilliant also reads provider environment variables as a fallback (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY`). A key you paste never appears in a chat transcript, in the conversation history, or in your prompt drafts. When you set one up from inside the chat, the pasted key is stashed straight into the credential store and is not echoed into the conversation. ## Explicit consent, by default Outbound chat traffic is **explicit-consent only**. Brilliant does not quietly attach a screenshot of your screen, your system info, your app version, your recent files, telemetry, or any other ambient context. Beyond your prompt and the canvas context described below, the default is to send nothing extra. ## What travels with a message There are exactly three things that can go out with a message, and you control all three. ### 1. Canvas context (text) So the model can see the work in progress, Brilliant includes a **text** outline of the canvas: a depth-limited structural outline of the elements in Brilliant's Blueprint (not the full tree, and a large canvas is condensed into a compact summary), the design-system tokens, the component catalog, and the element count. This is text, not an image. No screenshot of the canvas or your screen is captured for it. How often it goes depends on the provider. With Brilliant AI or an API-key provider (Anthropic, OpenAI, Google, OpenRouter), a fresh outline travels with every message. With an in-app CLI provider (Claude Code, Codex), it goes with your first message only; later messages carry nothing extra beyond a short note if you switch to a different canvas. Your current selection is not part of this. The model sees a specific element only when you attach it yourself (an `@` mention or paste) or when it calls a tool to read the selection. To send a prompt with no canvas context at all, start the chat in an empty workspace. ### 2. Attachments you add (opt-in) Anything you attach is something you chose to attach, and each shows as a chip above the input with an X to remove it before sending: - An **element** attachment (via `@` mention or paste) sends that element's Blueprint plus a PNG render of just that element. - An **image or file** attachment sends exactly what you added. ![Attachment chips above the chat input, each removable before sending|60%x](/docs/images/ai-attachment-chips.webp) ### 3. Automatic self-review screenshot (after edits) After the agent applies a block of changes, Brilliant renders a screenshot of **those changed elements** and sends it back to the model so it can check its own work (spacing, contrast, alignment, clipping). This is a render of the design content only, never your screen or other apps. Text-only models never receive it, and it is suppressed for the rest of a session once an endpoint rejects image input. ## What reaches Brilliant Where your prompt and canvas context go depends on the lane. On **Your own AI**, the three things above go to your provider, never to Brilliant; what reaches Brilliant is contentless (a running count of the messages you send, a bare number with no prompt, canvas, or response attached, which enforces the Free plan's daily cap), plus the plan check and the usage analytics described in [Security and your data](/docs/workspace/security-and-data), which carry things like a message's character count, never its text. On **Brilliant AI**, the request itself routes through Brilliant's proxy to Google, and Brilliant records the usage rows that meter it: the model, the token counts, the computed cost, your plan, and timestamps, never the prompt text or the design content. The part of Brilliant AI you can use before signing in (the anonymous allowance) is granted once per installation: Brilliant keeps an install identifier for it, and uses your IP address only for anti-abuse limits on that anonymous allowance (a cap per address per day and an overall daily limit on anonymous usage). The daily cap and the usage meter are covered in [Plans and billing](/docs/platform/plans-and-billing). ## What is never sent Brilliant never attaches, on its own: - A screenshot of your screen, desktop, or other applications. - System information, hardware details, or the app version. - A list of your recent files or your file system. - Telemetry or usage analytics riding along with your prompt. - Your API keys, in any transcript, history, or draft. ## Playground mode sends nothing Playground mode (the onboarding demo, or the toggle in Settings) replays bundled demo conversations locally. There is no network call and no model request while a demo is the active path. ## Sending feedback The feedback card (covered in [What the AI can do](/docs/ai/what-the-ai-can-do)) follows the same explicit-consent rule as the chat. Opening the card sends nothing. Your message leaves only when you press **Send**, and the optional extras (your app version, a recent error log, the chat transcript, a canvas screenshot) attach only per item, when you tick them: an unticked screenshot is never even captured. Exactly what a submission carries over the network, and the periodic prompt check that rides beside it, are listed in [Security and your data](/docs/workspace/security-and-data). ## A note on Quiver AI vector generation and image vectorization use a separate **Quiver** key, set up the same bring-your-own-key way. It follows the same rule: traffic goes to Quiver's own endpoint using your key, and nothing routes through Brilliant. ## Next - **See what the agent does with all this:** [What the AI can do](/docs/ai/what-the-ai-can-do). - **Manage your keys and providers:** [Providers and models](/docs/ai/providers-and-models). --- Source: https://brilliant.design/docs/ai/providers-and-models.mdx # Providers and models Brilliant's chat runs on Brilliant AI by default (the served model, Gemini 3.8 Flash for chat, with a Gemini image model for image generation; nothing to set up). This page covers the other lane, your own AI, where you choose the model and bring your own key or local CLI: every provider you can connect, how to switch models and thinking levels, and where your keys are stored. For the two-minute version, see [Connect an AI Provider](/docs/getting-started/connect-provider). ## Supported providers ### Brilliant AI **Brilliant AI** leads the model selector and is selected by default. It needs no key and no setup: your first message just works on Brilliant's own served model. It runs at its lightest thinking level unless you pick a higher one. How its usage works is in [Plans and billing](/docs/platform/plans-and-billing). ### Your own AI All of these are managed in **Settings** (`Cmd+,`) **→ Your own AI**. You can connect more than one and switch between them per chat. - **Claude Code** leads the list and needs no key. Install the `claude` CLI and Brilliant detects it on launch; if it is not signed in, run `/login` in the chat input. This routes through your existing Claude subscription. - **Codex** also needs no key. Install the `codex` CLI and sign in with your ChatGPT subscription, and Brilliant picks it up as its own provider once its models are discovered. It is a peer of Claude Code, not part of the OpenAI API-key row. - **Anthropic**, **OpenAI**, **Google (Gemini)**, and **OpenRouter** each take an API key. Paste it into the provider's row and it is validated before it saves. - **OpenAI** covers both its standard chat models and its reasoning line (including the Pro tiers, which run over OpenAI's Responses API). You do not pick the API; Brilliant routes each model correctly. - **OpenRouter** is a single key that reaches many models across vendors, handy for trying different models without juggling keys. A separate **Quiver** row powers AI vector generation and image vectorization. It is set up the same way, but it is not a chat provider. ## Custom and self-hosted endpoints Any OpenAI-compatible endpoint can be added under **Settings → Your own AI → Custom Providers**. Pick a preset to pre-fill the base URL, or choose the generic option for anything else: | Preset | Kind | Key | |--------|------|-----| | LM Studio, Ollama, vLLM | Local (localhost) | None needed | | GLM (Z.ai), Moonshot (Kimi), DeepSeek, Xiaomi MiMo | Hosted | Required | | Custom OpenAI-compatible | Anything else | Optional | On save, Brilliant tests the connection, then lists the models: curated defaults for the preset plus whatever the endpoint reports. Local runtimes ship no curated list, so theirs is whatever you have loaded; use the row's refresh button after pulling a new model. Leave the key blank for local runtimes. ## Choosing and switching models The model selector sits in the chat input bar. Only models whose provider currently has a valid key (or, for Claude Code, a detected install) appear in it. Each shows a short quality-and-speed subtitle like "Best", "Excellent", or "Good + Fast". - Click the **model selector** to change the model for the current chat. Your choice becomes the default for new chats. - Type **`/model`** for an interactive provider-then-model picker.
Tip: The exact model lineup shifts between releases. Read the live list in the selector rather than memorizing specific model names.
### Enabling extra models The selector normally shows a curated set per provider. To use a model the app didn't ship (an older, newer, or niche one your key can reach): 1. Open **Settings → Your own AI**. 2. On a connected provider row, click **Choose models** (the sliders icon). 3. Brilliant fetches that provider's full model list live. Shipped models sit at the top, locked on; every other model is a toggle, with a search field to filter. 4. Toggle the ones you want. They appear in the chat model selector immediately, no restart. Capabilities for a manually enabled model (context window, thinking support) are inferred from its id, so a niche model may show a conservative context window or no thinking selector. ## Thinking levels When a model supports reasoning, a thinking-level selector appears next to the model selector: - **Claude** models support **off, low, medium, high**. On your own AI, Claude is the only family you can turn off. Brilliant AI runs at its lightest thinking level by default; pick medium or high for deeper reasoning. - **OpenAI** and **Gemini** reasoning models always reason, so they offer **low, medium, high** with no off. Some OpenAI reasoning models add an **xhigh** level. - Text-only models that do not reason show no thinking selector at all. The default level is right for almost everything; raise it when you want more room to plan through a longer task. ## Where your keys live Keys are stored locally in your OS credential store (macOS Keychain, Windows Credential Manager) and are sent only to that provider's own API endpoint. Brilliant also reads provider environment variables as a fallback: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, and `OPENROUTER_API_KEY`. Keys never appear in a chat transcript. For the full data story, see [Privacy and consent](/docs/ai/privacy-and-consent). ## Playground mode Playground replays bundled demo conversations so you can watch the agent work. It is not the no-provider fallback: with nothing connected your first message runs on Brilliant AI. Turn Playground on from the toggle at the bottom of the Your own AI pane; the onboarding step plays one recorded session too. ## Next - **Send a prompt:** [Your First AI Chat](/docs/getting-started/first-ai-chat). - **Learn the panels and shortcuts:** [Sessions and chat](/docs/ai/sessions-and-chat). --- Source: https://brilliant.design/docs/ai/sessions-and-chat.mdx # Sessions and chat The built-in chat is built for long, parallel conversations. This page covers sessions and tabs, the follow-up composer, the chat explorer, the full slash-command list, and how to stop or revert a turn. To send your first prompt, start with [Your First AI Chat](/docs/getting-started/first-ai-chat). ## Sessions and tabs Each chat is a **session** with its own provider, model, thinking level, history, and context. Sessions run in parallel and persist between launches. Start a new one three ways: - Submit a prompt from the bottom-toolbar input. - Click **+** in the chat panel header. - With the AI chat focused, press . Active sessions show as tabs to the right of the AI input: - **Minimized:** topic label, a processing spinner while it works, and a context-usage percentage. Click to expand, double-click to rename. - **Expanded:** the full chat panel with messages, attachments, and a follow-up input. Drag tabs to reorder them; the toolbar scrolls when they overflow. ## Shortcuts | Action | Shortcut | |--------|----------| | Focus the AI input | | | Focus chat session 1 through 9 | to | | Focus chat session 10 | | | Focus next session | | | Focus previous session | | | New chat (AI chat focused) | | | Close focused session | | | Toggle the chat explorer | | | Search your chats | | | Dismiss the chat panel | | Focusing a session by number opens the panel if it is hidden. Toggling the AI chat panel has no default shortcut, but you can assign one in the shortcuts view. ## The follow-up composer Very few designs land on the first prompt, and that is the point. Follow-ups see the full context of the canvas and the conversation, so you can speak in deltas. Send with ; add a line break without sending with . The composer has four typing helpers: - **`@` mentions** attach a canvas element by name. Type `@`, pick an element, and its structure and a render travel with your message. - **Attachments** ride along too: paste an image with , drag a file in, or click the paperclip. Each shows as a chip above the input with an X to remove before sending. - **`#` hashtags** open a dropdown of style and context modifiers (dark, mobile, minimal, and so on) that drop into your prompt. - **`/` slash menu** opens the slash commands plus recent sessions for a quick resume. **Prompt history:** step through past prompts with and (or and ). Reverse-search with : type to filter, press it again to cycle matches. History is deduplicated and persists between launches. ## Panel controls - **Open or close:** click the connection indicator in the bottom toolbar, or run **Toggle AI Chat**. - **Rename:** double-click the topic in the header, or use `/rename`. - **Resize:** drag the panel edges, or the divider between sessions. - **Queue a follow-up:** send while the model is still working and the message queues, then fires automatically when the current response finishes. - **Edit and resend:** click the edit icon on one of your messages to revise and resend it. - **Copy chat:** the header copy button exports the whole conversation as Markdown, with metadata (model, date, project, canvas, tokens, turns). When your Brilliant AI usage runs out, the turn in flight finishes and only the next send is stopped: it opens the upgrade wall, a page over the editor (the same overlay as sign in) that offers the paid rungs and a bring-your-own-AI door. The message you sent is held and continues on its own once the wall clears, so nothing you typed is lost. ## The chat explorer Toggle the explorer with to browse, search, and manage every session in a sidebar. Drag its divider to resize or collapse it. ![The chat explorer sidebar next to an active session|420x](/docs/images/bank/ai-chat-panel/general.webp) ## Slash commands Type these in the chat input. Some are provider-specific and only show when relevant. | Command | Does | |---------|------| | `/stop` | Stop the current response | | `/continue` | Nudge the model to continue | | `/context` | Show context-window usage | | `/usage` | Show your usage: on Brilliant AI a percent used and a reset date, plus your own-AI count on Free | | `/cost` | Show cost and usage for this session (Claude Code and Anthropic only) | | `/compact` | Compact (summarize) conversation history | | `/feedback` | Classify the feedback (bug, feature request, question, or praise) on a quick card, then the agent routes it and takes it from there (uses one message) | | `/archive` | Archive the current chat | | `/new` (`/clear`) | Start a new chat | | `/model` | Change the model (provider-then-model picker) | | `/rename` | Rename the current chat, for example `/rename My Chat` | | `/copy` | Copy the last assistant message | | `/help` | Show available commands | | `/login` | Sign in to Claude (Claude Code only; typed directly, it does not appear in the autocomplete list) | ## Stop, revert, redo - Click the stop button in the chat input bar, or type `/stop`. The active request aborts immediately and no further tools run. - Anything the AI already created before you stopped stays on the canvas. - The stopped turn's card has a **Revert** control that undoes everything that turn changed in one step, plus a **Redo** to bring it back. You can also undo normally. For the in-app CLI providers (Claude Code and Codex), Brilliant keeps a single warm, long-lived process running across your turns rather than relaunching it per message, so follow-ups start faster and the session's history stays in place. Stopping a turn sends a control-request interrupt instead of killing the process, so it aborts cleanly and immediately, on Windows as well as macOS.
Tip: Each chat has its own undo stack, separate from the canvas's main history. You can keep editing by hand while an agent runs, and roll back a specific chat's changes with its own Revert without touching your own edits.
## Next - **See the full capability set:** [What the AI can do](/docs/ai/what-the-ai-can-do). - **Understand what gets sent:** [Privacy and consent](/docs/ai/privacy-and-consent). --- Source: https://brilliant.design/docs/ai/what-the-ai-can-do.mdx # What the AI can do The built-in agent is not a chatbot that hands you code to paste. It acts on your canvas directly, through the same operations you run by hand, and everything it makes stays fully editable. This page is the full range. To try it, head to [Your First AI Chat](/docs/getting-started/first-ai-chat). ## Create designs Describe an outcome and the agent builds it as native, editable elements: full UIs, pages, dashboards, illustrations, diagrams, and wireframes. There is no import step and nothing is flattened to an image; every layer, frame, and text run is yours to nudge afterward. > **You:** "Create a pricing page with three tiers." > > **Result:** three aligned cards with headings, feature lists, and call-to-action buttons, laid out on the canvas and ready to restyle. Flip between the prompt and its result below. The result is real, editable elements compiled live on the same engine that draws your canvas, not a flattened screenshot: the frame, every text run, the checkmarks (real vector paths), and the button are all yours to nudge, recolor, and restyle. ## Edit existing work The agent reads your current design and changes it in place: adjusting properties, reparenting, renaming, deleting, and reordering. Point it at something with your selection or an `@` mention and describe the change. > "Make @Card into a dark-mode variant." > > "Tighten the spacing on the second column and match its heading to @Hero." > > "Rename these layers to something sensible." ## Read your selection and search The agent can read what you have selected and look elements up across canvases by name, text, type, color, or component. That lets it act on the right thing without you spelling out ids. > "Apply the color palette from @Dashboard to @Card." > > "Find every text layer using the old brand blue and switch it to the new one." ## Run canvas commands Beyond drawing, the agent can invoke Brilliant's own commands, over a hundred of them: align, distribute, flip, corner radius, opacity, blend mode, tool changes, and frame and component operations. Each runs through the exact path a button or shortcut does, with full undo. > "Align these to the left and distribute them evenly." > > "Group the header and turn it into a component." ## Export The agent can render selected elements to **PNG, JPEG, WebP, SVG, PDF**, or **HTML/React** markup. > "Export @Card as SVG." > > "Give me the hero section as React." ## Generate images The agent can generate raster images with Google's image model and apply them as image fills on target elements. Generated images are saved to the project's `Assets/` folder. - **On Brilliant AI, image generation needs no setup.** On your own AI it requires a connected Google provider (API key or sign-in), whichever chat model is active. - **Sizes** run roughly from 512px up to 4K; larger is slower and more detailed. - **Reference images:** the agent can pass existing canvas elements as visual references to steer style, or to "edit" a prior generation one change at a time. - **Use for** photos, realistic scenes, and complex textures. ## Generate and trace vectors The agent can generate editable vector graphics and place them as native vector elements, so you can move, resize, recolor, and reshape them like anything you drew by hand. It can also **vectorize** an existing raster element into editable paths, leaving the original intact. - **Requires a connected Quiver provider**, whichever chat model is active. - **Use for** icons, logos, illustrations, and diagrams: anything that should stay crisp at any zoom and stay editable.
Image or vector? Reach for image generation when you want a photo or a rich texture, and vector generation when you want something crisp and editable like an icon or logo. Have a raster you want to edit as paths? Vectorize it.
## Spawn sub-agents For large tasks, the main session can launch parallel helper agents that each work on a slice and return a summary, shown as collapsible cards in the transcript. You do not manage them directly; you just get a faster path through big jobs. ## Send feedback to the Brilliant team When you hit a wall, want a feature, or spot a bug, you can send feedback to the Brilliant team without leaving the app, and the agent can help you get it to the right place. The **feedback card** is a notification in the rail at the top of the window: one text field, a **Send** button, and **Cancel**. Four things can open it: - The palette-only **Send Feedback** command (no default shortcut). - The agent's **send_feedback** tool. Despite its name, this tool never transmits anything: it opens the card in front of you (with an optional draft pre-filled) and returns. Nothing is sent until you press Send yourself. - A CTA that appears after an error. - A prompt the Brilliant team raises remotely to ask you one targeted question. The app checks for these periodically, and when one matches it surfaces as the same card. Typing **/feedback** in the chat is different: it does not open the card. It first asks you to classify the feedback (bug, feature request, question, or praise) on a quick card, then hands routing to the agent with that class as context. The agent figures out the best destination (the card, a GitHub issue, email, or Discord) and asks you a single "where should this go?" question only when the destination is still not obvious. It uses one message. The rule underneath all of it: nothing leaves your machine until you press **Send**. Your words move by your own act, and any context you attach (app version, error log, chat transcript, screenshot) rides along only per item, when you tick it. [Privacy and consent](/docs/ai/privacy-and-consent) is exact about what a submission carries. ## Next - **Understand exactly what gets sent:** [Privacy and consent](/docs/ai/privacy-and-consent). - **All of this works on your first message with Brilliant AI.** To run it on your own AI instead, see [Providers and models](/docs/ai/providers-and-models). # Section: Agents and MCP --- Source: https://brilliant.design/docs/agents/antigravity.mdx # Connect Antigravity Connect Google's Antigravity so its agent can design directly on your Brilliant canvas. ## Prerequisites - Antigravity installed. - Brilliant open. The MCP server only runs while the app is running. ## Setup 1. Open **Settings** (`Cmd+,`) in Brilliant and pick **MCP Connections**. 2. Flip the **Antigravity** row on. ![The MCP Connections list with the Antigravity row toggled on|420x](/docs/images/bank/settings/mcp/highlight-antigravity.webp) Brilliant writes a `brilliant` server into `~/.gemini/antigravity/mcp_config.json`. **Restart Antigravity** so it loads the server. ### By hand Antigravity uses a `serverUrl` key: ```json { "mcpServers": { "brilliant": { "serverUrl": "http://127.0.0.1:3333/mcp" } } } ``` Use `3334` or `3335` if Brilliant reported a fallback port. ## Try it Ask Antigravity's agent to design, with Brilliant open: - `In Brilliant, build a product page with a gallery and an add-to-cart panel.` - `Read my selection in Brilliant and generate a matching empty-state illustration.` - `Lay out a four-step wizard in Brilliant, each step in its own frame.` ![Antigravity's agent building a design in Brilliant|75%x](/docs/images/agent-antigravity-result.webp) ## Troubleshooting - **Antigravity doesn't see the `brilliant` tools.** Restart it; MCP servers load at launch. - **Calls fail.** Brilliant must be running. Launch it and retry. - **Port mismatch.** If Brilliant is on 3334 or 3335, re-toggle the Antigravity row in Settings to rewrite the address, then restart Antigravity. - **The row shows unchecked but the config has a `brilliant` entry.** Its URL points elsewhere. Toggle on to rewrite it to the correct local address. --- Source: https://brilliant.design/docs/agents/claude-code.mdx # Connect Claude Code Drive Brilliant from the Claude Code CLI: ask it to design in plain English and watch native, editable elements land on your canvas. This is the smoothest of all the integrations, because Brilliant can register itself with Claude Code for you. ## Prerequisites - [Claude Code](https://claude.com/code) installed and signed in. - Brilliant open. The MCP server only runs while the app is running. ## Setup You have two paths. The first needs nothing at all. ### Automatic (recommended) Open the [built-in AI chat](/docs/ai/overview) in Brilliant once (press `/` on the canvas). Brilliant registers its endpoint with `claude` for you, including the per-project trust it needs, so the `brilliant` tools are available the next time you start Claude Code from anywhere. ### Manual toggle If you would rather not open the chat, add the connection from settings: 1. Open **Settings** (`Cmd+,`) and pick **MCP Connections**. 2. Flip the **Claude Code** row on. ![The MCP Connections list with the Claude Code row toggled on|420x](/docs/images/bank/settings/mcp/highlight-claude-code.webp) Either way, Brilliant writes a `brilliant` server entry into `~/.claude.json`. **Restart Claude Code** (or start a fresh session) so it picks the server up. ### By hand If you manage `~/.claude.json` yourself, add Brilliant under `mcpServers`: ```json { "mcpServers": { "brilliant": { "type": "http", "url": "http://127.0.0.1:3333/mcp" } } } ``` Use `3334` or `3335` if Brilliant reported a fallback port on startup. ### On the web Everything above is for the Brilliant desktop app. To drive a brilliant.design tab from Claude Code instead, install the local helper once: ``` claude mcp add brilliant -- npx -y @brilliant-hq/mcp ``` Then open brilliant.design in Chrome, Edge, or Firefox, start a Claude Code session, and it connects to your live tab automatically. Everything stays on your machine: the helper bridges Claude Code and the tab over `127.0.0.1`, and nothing routes through Brilliant. Chrome and Edge ask for a one-time "local network" permission on the first connect, Firefox does not, and Safari is not supported yet. The in-app walkthrough and the full browser story are in [Use with AI agents](/docs/agents/overview). ## Try it In a Claude Code session with Brilliant open, describe the outcome: - `Design a three-tier pricing section in Brilliant.` - `Read the Dashboard canvas in Brilliant and add a stats row that matches its style.` - `Turn the button in my selection into a reusable component with hover and disabled variants.` Claude Code shines on multi-step work, so lean into it: point it at a real design brief or an existing canvas and let it build, inspect its own result, and refine. ![Claude Code building a layout in Brilliant, tool calls in the terminal and elements on the canvas|75%x](/docs/images/agent-claude-code-result.webp) ## Troubleshooting - **Claude Code doesn't see the `brilliant` tools.** Restart the session; MCP servers load at startup. Run `/mcp` inside Claude Code to confirm `brilliant` is listed and connected. - **"Connection refused" or nothing happens.** Brilliant has to be open for the endpoint to answer. Launch it, then retry. - **Port mismatch.** If 3333 was busy, Brilliant is on 3334 or 3335. Re-toggle the Claude Code row in Settings to rewrite the current address, then restart. - **The Settings row shows unchecked even though `~/.claude.json` has an entry.** The entry points somewhere other than the local Brilliant address (usually a hand edit). Toggle it on to rewrite it to the correct URL. --- Source: https://brilliant.design/docs/agents/claude-desktop.mdx # Connect Claude Desktop Claude Desktop can drive Brilliant, but it takes one extra piece. Brilliant serves its tools over a local web address, and Claude Desktop's config only knows how to launch local command-line servers. A tiny bridge closes that gap.
Heads up: Claude Desktop appears in Brilliant's Settings → MCP Connections list, but as a non-toggleable row with a note: its config supports only local stdio servers, not a remote URL. There is no one-click toggle here. Use the manual setup below.
## Prerequisites - Claude Desktop installed. - **Node.js** installed (the bridge runs through `npx`). - Brilliant open. The MCP server only runs while the app is running. ## Setup The bridge is `mcp-remote`, a small adapter that lets a stdio client talk to an HTTP MCP server. You point Claude Desktop at `mcp-remote`, and `mcp-remote` at Brilliant. 1. In Claude Desktop, open **Settings → Developer** and click **Edit Config**. That opens `claude_desktop_config.json` (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows). 2. Add a `brilliant` server that runs the bridge: ```json { "mcpServers": { "brilliant": { "command": "npx", "args": ["-y", "mcp-remote", "http://127.0.0.1:3333/mcp"] } } } ``` 3. Save, then fully quit and reopen Claude Desktop. If Brilliant reported a fallback port at startup, use `3334` or `3335` in the URL instead of `3333`. ## Try it With Brilliant open, ask Claude Desktop to design: - `In Brilliant, create a simple landing page with a headline and a signup form.` - `Read the current canvas in Brilliant and add a footer that matches it.` - `Export the selected frame in Brilliant as PNG.` ![Claude Desktop calling Brilliant's tools through the bridge|75%x](/docs/images/agent-claude-desktop-result.webp) ## Troubleshooting - **The `brilliant` tools never appear.** Confirm Node.js is installed (`node --version` in a terminal). `npx` needs it to fetch and run the bridge. Then fully restart Claude Desktop. - **The bridge starts but calls fail.** Brilliant must be open for the endpoint to answer. Launch it, then restart Claude Desktop. - **Port mismatch.** If Brilliant is on 3334 or 3335 because 3333 was busy, update the URL in the config to match, then restart. - **First launch is slow.** The very first run downloads the bridge through `npx`; later launches reuse it and start quickly. --- Source: https://brilliant.design/docs/agents/codex.mdx # Connect Codex Drive Brilliant from OpenAI's Codex CLI. Like Claude Code, this one is nearly automatic: if Codex is installed, Brilliant registers itself on launch. ## Prerequisites - The Codex CLI installed. - Brilliant open. The MCP server only runs while the app is running. ## Setup ### Automatic If Brilliant finds a `~/.codex/` directory when it starts, it adds a `brilliant` entry to `~/.codex/config.toml` for you and keeps the address in sync with whichever port it bound to. Nothing to click. Just **restart your Codex session** so it reads the updated config. ### Manual toggle To add or re-add the connection on demand: 1. Open **Settings** (`Cmd+,`) and pick **MCP Connections**. 2. Flip the **Codex CLI** row on. ![The MCP Connections list with the Codex CLI row toggled on|420x](/docs/images/bank/settings/mcp/highlight-codex.webp) Then restart your Codex session. ### By hand The entry in `~/.codex/config.toml` is a TOML section: ```toml [mcp_servers.brilliant] url = "http://127.0.0.1:3333/mcp" ``` Use `3334` or `3335` if Brilliant reported a fallback port. ## Try it With Brilliant open, ask Codex to design: - `In Brilliant, lay out a dashboard with a KPI row and a chart placeholder.` - `Read the Landing canvas in Brilliant and add a testimonials section that fits.` - `Export the selected frame in Brilliant as SVG.` ![Codex driving Brilliant from the terminal, elements appearing on the canvas|75%x](/docs/images/agent-codex-result.webp) ## Troubleshooting - **You restarted and nothing happened.** That is expected. Codex spawns the `brilliant` MCP server only when a session starts, not before, so restarting Brilliant alone shows no change. Start a Codex session and send a message. The `brilliant` tools connect on that first call. - **Codex doesn't see the `brilliant` tools.** Restart the Codex session; it reads `config.toml` on startup. - **No entry appeared automatically.** Brilliant only writes to `~/.codex/config.toml` when that directory already exists (so it never litters the home folder of non-Codex users). Create the directory or run Codex once, then reopen Brilliant, or add the connection from Settings. - **Calls fail.** Brilliant must be running. Launch it and retry. - **Port mismatch.** Brilliant rewrites the `url` to the current port every launch, so restarting Brilliant then your Codex session resolves a stale address. You can also re-toggle the row in Settings. --- Source: https://brilliant.design/docs/agents/copilot.mdx # Connect GitHub Copilot (VS Code) Let GitHub Copilot's agent mode in VS Code design on your Brilliant canvas. Brilliant registers into VS Code's own MCP config, so Copilot picks the `brilliant` tools up automatically. ## Prerequisites - VS Code with GitHub Copilot and its agent mode available. - Brilliant open. The MCP server only runs while the app is running. ## Setup 1. Open **Settings** (`Cmd+,`) in Brilliant and pick **MCP Connections**. 2. Flip the **VS Code** row on. ![The MCP Connections list with the VS Code row toggled on|420x](/docs/images/bank/settings/mcp/highlight-vscode.webp) Brilliant writes a `brilliant` server into VS Code's user MCP config (`.../Code/User/mcp.json`), preserving any comments already in the file. **Reload VS Code** so it loads the server. ### By hand The file lives at `~/Library/Application Support/Code/User/mcp.json` on macOS, `%APPDATA%\Code\User\mcp.json` on Windows, and `~/.config/Code/User/mcp.json` on Linux. Add Brilliant under `servers`: ```json { "servers": { "brilliant": { "type": "http", "url": "http://127.0.0.1:3333/mcp" } } } ``` Use `3334` or `3335` if Brilliant fell back to those ports. ## Try it Open Copilot Chat in agent mode, with Brilliant open, and describe what you want: - `In Brilliant, build a documentation landing page with a hero and a feature grid.` - `Read my selection in Brilliant and produce a dark-mode version of it.` - `Create a component set for buttons in Brilliant: primary, secondary, and ghost.` ![Copilot's agent mode building a design in Brilliant|75%x](/docs/images/agent-vscode-result.webp) ## Troubleshooting - **Copilot doesn't show the `brilliant` tools.** Reload the VS Code window. Make sure you are in Copilot's agent mode, which is what can call MCP tools. - **Calls fail or time out.** Brilliant must be open. Launch it and retry. - **Port mismatch.** If Brilliant is on 3334 or 3335, re-toggle the VS Code row in Settings to rewrite the current address, then reload VS Code. - **The row shows unchecked but `mcp.json` has a `brilliant` entry.** The URL points elsewhere. Toggle on to rewrite it to the correct local address. --- Source: https://brilliant.design/docs/agents/cursor.mdx # Connect Cursor Wire Cursor's agent into Brilliant so it can build and edit designs on your canvas while you stay in the editor you already live in. ## Prerequisites - Cursor installed. - Brilliant open. The MCP server only runs while the app is running. ## Setup 1. Open **Settings** (`Cmd+,`) in Brilliant and pick **MCP Connections**. 2. Flip the **Cursor** row on. ![The MCP Connections list with the Cursor row toggled on|420x](/docs/images/bank/settings/mcp/highlight-cursor.webp) Brilliant writes a `brilliant` server entry into `~/.cursor/mcp.json`, leaving any other servers untouched. **Restart Cursor** so it loads the new server. ### By hand If you prefer to edit `~/.cursor/mcp.json` yourself: ```json { "mcpServers": { "brilliant": { "url": "http://127.0.0.1:3333/mcp" } } } ``` Use `3334` or `3335` if Brilliant fell back to those ports on startup. ## Try it Ask Cursor's agent to design, with Brilliant open: - `In Brilliant, design a settings page with a sidebar and a form.` - `Read my current selection in Brilliant and generate three color variations of it on a new canvas.` - `Build a mobile onboarding flow in Brilliant, one frame per screen.` Because you are already in a code editor, Cursor is handy for turning a spec or a component in your repo into a matching design in Brilliant. ![Cursor's agent creating a design in Brilliant|75%x](/docs/images/agent-cursor-result.webp) ## Troubleshooting - **You restarted Cursor and nothing happened.** That is expected. Cursor spawns the `brilliant` MCP server only when a chat starts, not on launch, so a restart alone shows no change. Open Cursor's AI chat (`Cmd+I`) and send any message. The `brilliant` tools connect on that first call. - **Cursor doesn't list the `brilliant` tools.** Restart Cursor; it reads MCP servers at launch. Check Cursor's MCP settings panel to confirm `brilliant` shows as connected. - **Calls fail or hang.** Brilliant must be open for the endpoint to respond. Launch it and retry. - **Port mismatch.** If Brilliant is on 3334 or 3335 (because 3333 was taken), re-toggle the Cursor row in Settings to rewrite the current address, then restart Cursor. - **The row shows unchecked but the config has a `brilliant` entry.** The entry points somewhere else. Toggle on to rewrite it to the correct local URL. --- Source: https://brilliant.design/docs/agents/overview.mdx # Use with AI agents Brilliant runs a local MCP server the whole time the app is open, so any coding agent that speaks MCP can design on your canvas: create elements, edit them, run commands, and export, all as native editable objects you can keep working by hand. This page explains the two ways to connect an agent and points you at the exact setup for your tool. ## Two ways to work with an agent There are two integration styles, and most designers end up using both: - **The built-in chat.** Brilliant ships with an AI chat docked in the bottom toolbar. It reads your selection, streams edits onto the canvas, and gives you per-chat undo, color and position pickers, and progressive previews. It runs on Brilliant AI out of the box, or on your own AI if you bring a key or your Claude Code login. Start with [Built-in AI](/docs/ai/overview). - **Your own external agent, over MCP.** Keep working in Claude Code, Cursor, Codex, or another editor and let it drive Brilliant from there. You point the tool at Brilliant's local endpoint once, then ask it to design. This is the rest of this section. Same canvas, same undo stack, either way. You can draw by hand while an agent runs and nothing collides. Driving Brilliant from an external agent over MCP is unlimited on every plan, Free included. The built-in chat's daily message cap does not apply to it. More in [Plans and billing](/docs/platform/plans-and-billing). ## MCP in one paragraph MCP (Model Context Protocol) is a shared language that lets an AI tool call another app's features. Brilliant exposes its canvas as a small set of MCP tools and serves them over a local web address on your own machine. Any MCP-aware editor you connect can then create and edit designs in Brilliant by calling those tools. Nothing leaves your computer to make this work, and Brilliant has to be open for a connected tool to reach it. ## The endpoint ``` http://127.0.0.1:3333/mcp ``` Localhost only, no authentication, running right now if Brilliant is open. If port 3333 is busy, Brilliant falls back to 3334, then 3335, so a second instance still gets its own address. Whenever you set a tool up through Brilliant's own settings, it writes the correct current address for you. ## On the web (brilliant.design) You do not need the desktop app to drive Brilliant from an agent. A live brilliant.design tab works too, through a tiny local helper you install once: ``` claude mcp add brilliant -- npx -y @brilliant-hq/mcp ``` That one command is universal: it is the same `@brilliant-hq/mcp` package for every MCP client, and it covers both the web tab and the desktop app. The helper runs on your own machine and bridges your agent to the tab over loopback (`127.0.0.1`). No MCP or design data ever touches Brilliant's servers: the whole exchange is agent to helper to tab, all on localhost. Brilliant walks you through it in the app. Open the AI chat and pick **Drive Brilliant via MCP**, or go to **Settings → MCP Connections**: either one hands you the exact command to copy, then waits and connects the moment your agent starts. Once it is connected, ask it to design and the edits land on the canvas you are looking at. A couple of browser notes: - **Chrome and Edge** show a one-time "local network" permission the first time an agent connects (Chromium 147 and later). Grant it once and it sticks for brilliant.design. **Firefox** needs no prompt. - **Safari is not supported yet.** Its rules block the local connection with no way to opt in, so use Chrome, Edge, or Firefox for the web app, or the desktop app instead. ## What an agent can do Everything the built-in chat can do: - **Create designs** as native editable elements: full UIs, pages, dashboards, illustrations, diagrams, and wireframes, via plain HTML/CSS or Brilliant's Blueprint format. - **Inspect and edit** any canvas: read structure, change properties, reparent, rename, delete, and reorder. - **Search** across canvases by name, text, type, color, or component. - **Run canvas commands** the same way your keyboard does (align, group, auto layout, corner radius, create components, and more), each with full undo. - **Export** selected elements to PNG, JPEG, WebP, SVG, PDF, HTML (page, document, or flex layout), React, MP4, MOV, or Replay. - **Generate images** (raster) and **vectors**, and apply them to the canvas. - **Load design knowledge** on demand: tokens, typography, effects, composition patterns. ## Pick your tool Setup is a one-time step per tool. If the tool is in Brilliant's built-in list, it is a single toggle; otherwise you paste one line of config. Each page below has the exact steps, prompts to try, and troubleshooting. | Tool | Setup | Page | |------|-------|------| | Claude Code | Automatic once you open the chat, or one toggle | [Claude Code](/docs/agents/claude-code) | | Claude Desktop | Manual (a small bridge command) | [Claude Desktop](/docs/agents/claude-desktop) | | Cursor | One toggle | [Cursor](/docs/agents/cursor) | | Codex CLI | Automatic on launch, or one toggle | [Codex](/docs/agents/codex) | | GitHub Copilot (VS Code) | One toggle | [Copilot](/docs/agents/copilot) | | Windsurf | One toggle | [Windsurf](/docs/agents/windsurf) | | Antigravity | One toggle | [Antigravity](/docs/agents/antigravity) | The in-app list under **Settings → MCP Connections** is the live source of truth for what Brilliant can connect to, and it grows over time. It also covers tools without a dedicated page here (Gemini CLI, Zed, OpenCode); the step is identical, just flip that tool's toggle. ![Settings → MCP Connections, a flat list of tools each with a connect checkmark|420x](/docs/images/bank/settings/mcp/general.webp) ## The shortest path If you use Claude Code, the fastest setup is no setup: open the built-in chat in Brilliant once and it registers the endpoint with `claude` for you. From then on, tell your agent to work in Brilliant. > "Design a pricing section in Brilliant." > > "Add a hero to the dashboard canvas." > > "Update the card component to match the new brand colors." Brilliant hands the agent a short overview on its first call and serves deeper knowledge on demand, so you do not have to prompt-engineer it. ## Next - **Set up your editor:** pick your tool from the table above. - **Prefer the in-app chat?** [Built-in AI](/docs/ai/overview) covers the docked chat, providers, and everything it can do. --- Source: https://brilliant.design/docs/agents/windsurf.mdx # Connect Windsurf Connect Windsurf so its Cascade agent can create and edit designs on your Brilliant canvas. ## Prerequisites - Windsurf installed. - Brilliant open. The MCP server only runs while the app is running. ## Setup 1. Open **Settings** (`Cmd+,`) in Brilliant and pick **MCP Connections**. 2. Flip the **Windsurf** row on. ![The MCP Connections list with the Windsurf row toggled on|420x](/docs/images/bank/settings/mcp/highlight-windsurf.webp) Brilliant writes a `brilliant` server into `~/.codeium/windsurf/mcp_config.json`. **Restart Windsurf** so it loads the server. ### By hand Windsurf uses a `serverUrl` key: ```json { "mcpServers": { "brilliant": { "serverUrl": "http://127.0.0.1:3333/mcp" } } } ``` Use `3334` or `3335` if Brilliant reported a fallback port. ## Try it Ask Cascade to work in Brilliant: - `In Brilliant, design a checkout screen with an order summary and a payment form.` - `Read the Marketing canvas in Brilliant and add a FAQ accordion in the same style.` - `Generate an icon set for navigation in Brilliant and lay them out in a row.` ![Cascade building a design in Brilliant|75%x](/docs/images/agent-windsurf-result.webp) ## Troubleshooting - **Windsurf doesn't list the `brilliant` tools.** Restart Windsurf; it reads MCP servers at launch. Check its MCP panel to confirm `brilliant` connected. - **Calls fail.** Brilliant must be open for the endpoint to answer. Launch it and retry. - **Port mismatch.** If Brilliant is on 3334 or 3335, re-toggle the Windsurf row in Settings to rewrite the address, then restart Windsurf. - **The row shows unchecked but the config has a `brilliant` entry.** Its URL points elsewhere. Toggle on to rewrite it to the correct local address. # Section: Files and workspace --- Source: https://brilliant.design/docs/workspace/canvases-and-files.mdx # Canvases and files A Brilliant workspace is just a folder on disk. Your canvases are files inside it, your folders are real directories, and there is no hidden project database keeping score. This page covers how the file explorer works, how autosave keeps your work safe, and how undo behaves across canvases. ## Workspaces are folders Open a folder with `Cmd+O` and Brilliant treats it as a workspace: every `.bl` file inside is a canvas, subfolders are folders, and images live in `Assets/` directories. The directory tree is the project tree. Nothing else to manage. If you have not opened a folder, Brilliant works in **scratch mode** and autosaves to a private scratch directory, so you never lose an untitled sketch. Open a folder any time to switch into that workspace. Brilliant remembers your last workspace and reopens it on launch, and it tracks your recent workspaces so you can jump back. Because a canvas is a plain file, its identity is its path within the workspace. Rename or move a canvas and its identity moves with it, and references update to follow. ## The file explorer The file explorer lives in the top half of the left toolbar. Focus it with `Cmd+Shift+E`. Below it sits the layers explorer for the active canvas; focus that with `Cmd+Shift+R`. Toggle the whole left toolbar with `Cmd+Shift+Left`. ![The file explorer in the left toolbar, showing canvases and folders|280x](/docs/images/bank/left-toolbar/general.webp) The explorer shows everything in the workspace as a tree: canvases, folders, image assets, and any text or code files. Click an item to select it and, if it is a canvas or a previewable file, switch to it. `Cmd+click` adds or removes from the selection, `Shift+click` selects a range, and a selection can span canvases, folders, and assets at once for a batch move or bulk delete. **Type to filter.** The Files header has a search toggle so you can filter the tree by name as you type. ### Create, rename, reorder, delete | Action | Shortcut | |--------|----------| | New canvas | `Cmd+N` | | New folder | `Cmd+Shift+N` | | Duplicate current canvas | `Cmd+Ctrl+N` | | Rename active canvas | `Alt+Enter` | | Delete active canvas | `Cmd+Shift+Delete` | | Toggle expand/collapse all folders | `Cmd+Shift+C` | `Cmd+N` and `Cmd+Shift+N` create inside the focused folder when the explorer is focused, otherwise at the workspace root. Rename inline by double-clicking a name, or press `Alt+Enter` on a focused canvas. Drag a canvas or folder onto another folder to move it, or multi-select and drag to move several at once as a single undo step. Right-click a folder for the full menu: Rename, Delete, Duplicate, Cut, Copy, Paste, New Canvas, New Folder, expand/collapse options, Reveal in Finder, and Copy Relative or Absolute Path. **Color tags.** Append a color suffix to a folder or canvas name to tint its icon: `.red`, `.green`, `.yellow`, `.orange`, `.purple`, `.pink`, or `.gray`. Naming a folder `Components.purple` gives it a purple icon, and the suffix stays hidden from the displayed name. ### Switch between files | Action | Shortcut | |--------|----------| | Next / previous file | `Alt+Right` / `Alt+Left` | | Previously active canvas | `Ctrl+Alt+Left` | | Reveal active canvas in explorer | `Cmd+Shift+K` | | Search and switch canvas | `Cmd+P` | | Global search (canvases, images, text) | `Cmd+K` | | Search layers in the active canvas | `Cmd+L` | Switching canvases is instant and is not an undoable action. ## The top toolbar shows where you are Centered at the top, the breadcrumb shows the active file's location as `Workspace / Folder / Canvas`. Double-click the canvas-name segment to rename it inline, and hover the breadcrumb to reveal a button that copies the file's absolute path. The canvas name also doubles as your save indicator: it dims while there are unsaved changes and brightens once saved. There is no separate save chip or spinner. ## Assets Images live in `Assets/` folders next to your canvases, one per folder level. Importing an image drops it into the appropriate `Assets/` directory, and dragging an image from Finder into the explorer imports it too. Right-click an asset for **Rename** (which updates every reference to it across all canvases automatically), **Delete** (which warns first if the image is in use, then moves it to the trash, recoverable via undo), **Reveal in Finder**, and **Copy Filename**. Run **Clean Up Unused Assets** from the command palette to sweep out images no canvas references. ## Autosave, no save button Brilliant saves continuously. There is no `Cmd+S` and you never save by hand for normal work. Shortly after you stop editing, the active canvas is written in the background so saves never interrupt you. On quit or when you switch workspaces, every unsaved canvas is flushed first. Each save captures the full canvas: elements and hierarchy, fills, strokes, effects, components, design-system bindings, ruler guides, zoom, pan, and background settings. Selection and undo history are session-only and are not written to disk. If a `.bl` file ever fails to load (corrupt file, schema mismatch), Brilliant blocks saves on that canvas so it can never overwrite good data with an empty state. ## Undo is per canvas Every canvas keeps its own independent undo history. `Cmd+Z` on a canvas undoes that canvas's last action, and switching canvases never clears it. When the file explorer is focused, `Cmd+Z` routes to a separate **explorer undo** stack that covers file-tree operations: create, rename, delete, move, and reorder of canvases and folders. Deletes are never destructive, they move the file into the workspace trash and register an undo that restores it. To undo a file-tree change, focus the explorer with `Cmd+Shift+E` first, then `Cmd+Z`. ## Version control with Git `.bl` files are plain, deterministic Blueprint text: one element per line, stable ordering, inline coordinates, hex colors. That means clean, human-readable diffs. Open your workspace folder in Git, and you can branch designs, review them in pull requests, and `git blame` a stray color change exactly as you would with code. Include the `.brilliant/` folder to share consistent workspace settings. Older canvases saved in Brilliant's earlier YAML format still open. Brilliant migrates each one to Blueprint the next time you save it and archives the original alongside it, so nothing is lost. For local work there is no version-history panel inside Brilliant: undo is per canvas and session-only, so for persistent history use Git or an OS backup like Time Machine. Once you publish a project to brilliant.design, that project also gets named checkpoints and a full version history, covered in [Publish a project](/docs/platform/projects). ## New here? New to Brilliant and want something to poke at? On first launch the guided tour walks you through the essentials, and the starter prompts in the AI chat build something real on your canvas that you can pull apart. See [First steps](/docs/getting-started/first-steps). ## Next - **Bring designs in and ship them out:** [Import and export](/docs/workspace/import-and-export). - **Tune the app:** [Settings](/docs/workspace/settings). - **Reuse elements across canvases:** [Components](/docs/editor/components). --- Source: https://brilliant.design/docs/workspace/import-and-export.mdx # Import and export Brilliant exports the current selection to a long list of formats, copies elements to the clipboard as images or code, and imports from images, SVG, Sketch, Figma, and other Brilliant files. This page is the full tour of both directions. Export always works on your **selection**, never the whole canvas automatically. Select a frame and its children come along. To export a whole canvas, select all with `Cmd+A` first. ## Where export lives There are three ways to export by hand: 1. **The Export panel** in the right toolbar. The main hub. It appears when one or more elements are selected and lets you pick format, resolution, fit, background, video options, and PDF page mode, then run several export configs in one click. It is the only place plain video (MP4 and MOV) and multi-page PDF live. 2. **Commands and the one shortcut.** `Cmd+E` exports a PNG with a save dialog. Every other format is a command in the command palette. 3. **The right-click menu.** An **Export as** submenu for quick file exports, a **Copy as** submenu for the clipboard, and **Send to** for Figma. ## Formats | Format | Kind | Notes | |--------|------|-------| | **PNG** | Raster | Transparency supported. The default for `Cmd+E` | | **JPEG** | Raster | No alpha; a transparent request falls back to the canvas color. Quality 90 | | **WebP** | Raster | Transparency supported. Exports lossy at quality 90 (see the note below) | | **SVG** | Vector | Native filters for drop shadow, outer glow, and layer blur. Shaders, inner shadow/glow, background blur, and filter fills embed as rasterized PNG. Angular gradients approximate as linear | | **PDF** | Vector | Embedded fonts. Any element with an enabled effect rasterizes whole. Single or multi-page | | **HTML** | Code | HTML plus inline CSS, in three flavors: snippet, full document, and flex | | **React (JSX)** | Code | The same markup as HTML with JSX style objects, ready to paste into a `.tsx` file | | **MP4** | Video | H.264 or HEVC on macOS, H.264 on Windows. No alpha | | **MOV** | Video | HEVC with alpha, or ProRes 4444. macOS only, transparent background supported | | **Replay** | Video | An animated reveal: elements fade in one after another with a shimmer pass | MP4 and Replay export work on macOS and Windows (Windows encodes H.264 only). MOV, and with it transparent video, is macOS only. Linux has no video export.
Pixel-clean mockups? The by-hand UI exports WebP as lossy quality 90, which leaves faint gray banding on rounded corners and color ramps. For crisp UI mockups, export PNG instead.
## Export by hand 1. Select elements on the canvas. A frame brings its children. 2. Run the export: `Cmd+E` for a PNG, a command from the palette, right-click **Export as**, or configure the Export panel and click **Export**. 3. A save dialog opens with a timestamped default filename. Choose a location and save. ### Export panel options | Option | Values | Default | |--------|--------|---------| | **Format** | Any of the formats above. Choosing HTML reveals a variant dropdown: Page, Snippet, or Flexbox | PNG | | **Resolution** | Original (1x through 4x), 720p through 8K, social presets (Portrait, Square, Story), device presets (iPhone, MacBook Pro), and Custom. Hidden for SVG and PDF | Original (1x) | | **Width / Height** | Target pixel size. Set one to scale proportionally, or both with a fit mode for an exact size | none | | **Fit mode** | Fit (letterbox), Fill (crop), Stretch, or Repeat (tiled). Used only when both width and height are set | Fit | | **Background** | Transparent or Canvas (the canvas background color) | Transparent | JPEG and MP4 have no alpha, so a transparent request falls back to the canvas color. PNG and MOV keep alpha. For SVG and PDF the resolution row is hidden, because vector output is resolution-independent. ### Batch export The `+` button in the Export panel header adds another export config row, each with its own format, resolution, fit, and background. The **Export** button runs every row at once. Use it to ship, say, PNG at 1x, PNG at 2x, and an SVG together in a single click. ## Video export Video renders animated shader fills frame by frame into a hardware-accelerated file, on macOS and Windows. It is available only from the Export panel (no command, no shortcut). Replay is the exception, since it has a sensible one-click default. For the full platform breakdown and a fuller walkthrough of replay, see [Video and replay](/docs/workspace/video-and-replay). Select elements, choose MP4 or MOV, set the inline options, click **Export**, and save. A progress bar shows the frame count and can be cancelled. | Option | Values | Default | |--------|--------|---------| | **Duration** | 0.5 to 60 seconds | 10s | | **FPS** | 15, 24, 30, 60 | 60 | | **Quality** | Low, Medium, High | Medium | | **Resolution** | Same presets as image export | Original (1x) | The codec follows the format: MP4 offers H.264 and HEVC on macOS and H.264 on Windows (no alpha either way), MOV offers HEVC with alpha and ProRes 4444. Only MOV with an alpha codec supports a transparent background. Video animates shader fills only; static elements look identical in every frame, and there is no keyframe timeline. ### Replay Replay is a one-click animated reveal of your selection: each element fades in with a shimmer pass. It defaults to MP4, but you can switch the container to MOV for a transparent background. Run it from the command palette, from right-click **Export as then Replay** (which uses 2x scale for crisp retina output), or from the Export panel. Total duration comes from your per-element pacing times the element count. ## Multi-page PDF PDF is the one format with a multi-page mode, in the Export panel. A dropdown next to the PDF format pill switches between single and multi-page. - **Single-page** puts the whole selection on one page sized to its bounds. - **Multi-page** makes one page per top-level frame in the selection. The settings (gear) button shows the page list. Drag rows to reorder, or use the per-page checkbox to exclude a page without deselecting it. Because it is a per-config setting, one batch click can ship a single-page and a multi-page PDF together. ## Copy to the clipboard Copy your selection as an image or as code, ready to paste into an editor, a doc, another design tool, or a chat. Reach these from the command palette or the right-click **Copy as** submenu. | Copy as | What you get | |---------|--------------| | PNG (and @2x, @4x) | A PNG image at screen resolution, or multiplied up for retina | | WebP | A WebP image (lossy quality 90, macOS and Windows; Linux falls back to PNG) | | SVG | SVG markup | | HTML, HTML (document), HTML (flex) | An HTML/CSS snippet, a self-contained document, or a flexbox layout | | React | A React JSX snippet | | CSS | CSS properties (size, position, colors, border, radius, rotation, text) | | Blueprint | Brilliant's native element format, full hierarchy, pasteable back into Brilliant or shared with AI tools | **PNG** copies at your display's pixel ratio, so what you see matches what you paste. For general pasting, prefer **Copy as PNG**: most apps pick PNG over WebP anyway. ## Other design tools | Format | Import | Export | |--------|--------|--------| | **Sketch** (`.sketch`) | Yes, via **Import Sketch File** with page selection | Yes, via **Save as Sketch File** | | **Figma** | Via a Figma URL or the Copy to Brilliant plugin | Via **Send to Figma** (needs the paired plugin) | | **Illustrator** (`.ai`) | Bridge through SVG | Bridge through SVG | | **Photoshop** (`.psd`) | Export layers as PNG, then import | Export layers as PNG | **Send to Figma** pushes your selection to the paired Brilliant Figma plugin. It is only enabled when the plugin is connected, and otherwise falls back to the clipboard. ## Import ### Images | Action | How | |--------|-----| | Import a file | `Cmd+Shift+O`, or **Import** in the command palette | | Paste | `Cmd+V` with an image on the clipboard | | Drag and drop | Drag image files onto the canvas | Supported: PNG, JPEG, GIF, BMP, and WebP, plus TIFF, HEIC, HEIF, and AVIF on macOS. Imported images become rectangle elements with an image fill, placed at the canvas center. ### SVG Import an SVG with `Cmd+Shift+O` (pick a `.svg` file), the **Import SVG** command, or `Cmd+V` with SVG markup on the clipboard. It comes in as native, editable Brilliant elements: rectangles stay rectangles, circles stay circles, paths become vectors, groups become frames, and text stays text, with fills, strokes, and transforms preserved. Icons and simple illustrations import cleanly; very complex SVGs may need cleanup. ### Sketch Run **Import Sketch File** from the command palette to browse for a `.sketch` file. After parsing, pick which pages to import; each becomes its own canvas of native elements. ### Figma Bring Figma designs in by pasting a Figma URL into the right toolbar, or with the Copy to Brilliant plugin. It has its own page, since there is a bit to know about sign-in and rate limits: see [Import from Figma](/docs/import/from-figma). ### Brilliant `.bl` files `Cmd+Shift+O` and a `.bl` file imports it into an `Imports/` folder in the current workspace, with its images, registered as a new canvas and switched to. This needs an open workspace. Legacy `.design` files import the same way. ### How paste decides `Cmd+V` inspects the clipboard and handles, in order: Brilliant elements (full hierarchy, same or cross-canvas), image data, SVG markup, Figma data from the plugin, the legacy Brilliant YAML format, Brilliant Blueprint, HTML (converted to native elements), and finally plain text (which becomes a text element). ## Next - **Where files live:** [Canvases and files](/docs/workspace/canvases-and-files). - **The full Figma flow:** [Import from Figma](/docs/import/from-figma). - **Every shortcut:** [Keyboard shortcuts](/docs/workspace/shortcuts). --- Source: https://brilliant.design/docs/workspace/overlay-mode.mdx # Overlay mode Overlay mode floats Brilliant above everything else on your screen as a transparent, always-on-top layer. Instead of designing in a window, you design directly over your desktop, a browser, or any running app. ## What it's for The canvas becomes see-through, so whatever is behind Brilliant shows through your work. That opens up a few things a normal window can't: - **Trace an existing UI.** Pull up a screenshot or a live app and draw straight over it to rebuild it in Brilliant. - **Design over a live app.** Sketch a new screen on top of the real thing while it runs, so your mockup lines up with the actual layout. - **Compare pixel for pixel.** Drop your design over the reference and nudge until they match exactly. ## Turn it on first Overlay mode is macOS only, and it ships turned off. Enable it before the shortcut will do anything: 1. Open Settings with and go to the **General** pane. 2. Switch on **Overlay Mode**. 3. While you're there, use **Open Accessibility Settings** and grant Brilliant accessibility access. The global hotkey needs it to summon Brilliant from any app. The toggle sticks across launches, so this is a one-time setup. Flip it off and the hotkey goes quiet again (and if you're currently in overlay, Brilliant drops you back to your normal window). ## Enter and leave With the setting on, press to summon the overlay, and the same shortcut again to dismiss it. This is a global hotkey: it fires even when Brilliant isn't the focused app, so you can call it up from whatever you're working over. ![Brilliant as a transparent overlay traced over a live app|450x](/docs/images/overlay-mode-tracing.webp) ## Click through to what's underneath By default the overlay catches your clicks, the same as any window. When you want to reach the app behind it, turn on passthrough with . Your clicks and drags fall straight through Brilliant to whatever sits below, so you can scroll a page or operate an app while your design stays pinned on top. Press again to take control back. Passthrough only applies while you're in overlay. ## Clear the backdrop For a cleaner surface to work over, hide your desktop icons with . It's a toggle, so the same shortcut brings them back. ## Your window comes back Leaving overlay restores the exact window you had before. Brilliant saves your studio window's position, size, and whether it was full screen, maximized, or minimized when you enter overlay, then puts all of it back when you exit. Your zoom and pan return to where they were too, so dropping into overlay and back out never disturbs your studio setup. --- Source: https://brilliant.design/docs/workspace/security-and-data.mdx # Security and your data Brilliant is a local-first desktop app. Your designs are plain files on your disk, your AI runs on Brilliant AI or on your own key, your choice, and the app is precise about what it sends over the network and when. This page lays out where everything lives and what talks to what. ## Your API keys The built-in AI has two lanes. **Brilliant AI** (the default) routes your request through Brilliant's proxy to Google under Brilliant's own account, included with your plan; Brilliant records the usage rows for it (the model, token counts, cost, plan, and timestamps), never your prompt text or design content. Google (the Gemini API) processes those requests for Brilliant; payments are processed by Paddle as merchant of record. **Your own AI** (your own keys and coding agents) goes straight from the app to your provider (Anthropic, OpenAI, Google, OpenRouter, or a Claude Code login): a key you bring never routes through Brilliant's servers, your prompts and designs go from your machine to your provider, and you pay your provider directly. ![The AI providers settings, where you connect a key|450x](/docs/images/settings-ai-providers.webp) Keys are stored in your operating system's own credential store: macOS Keychain, or Windows Credential Manager. Brilliant also reads provider environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY`) as a fallback if a key isn't in the credential store. A key you paste never appears in a chat transcript, your conversation history, or a prompt draft. For the full breakdown of what travels with each message and what never does, see [Privacy and consent](/docs/ai/privacy-and-consent). ## Your designs Brilliant is local-first. A workspace is a folder, and every canvas is a plain `.bl` file inside it. When you are signed out, or working in a project you have not published, nothing about your design reaches Brilliant's servers. Open a folder and Brilliant works directly against those files. Because the format is plain, deterministic Blueprint text, you can put your workspace under Git and diff, branch, and review your designs like code. Details are in [Canvases and files](/docs/workspace/canvases-and-files). Publishing is the one deliberate act that changes this. When you publish a project to brilliant.design, its canvases, design systems, and the images those canvases use are uploaded, and from then on that project syncs automatically in both directions while you have it open. That is the whole story of when design content leaves your machine: you sign in, and you publish. The full picture, including exactly what syncs and what never does, is in [How sync works](/docs/platform/sync). Sending a design to the AI is separate and always your choice: the built-in chat includes a text outline of the canvas so the model can see your work in progress. On your own key it goes straight to the provider you connected; on Brilliant AI it routes through Brilliant's proxy to Google, and Brilliant records only the usage rows, never the outline's contents. [Privacy and consent](/docs/ai/privacy-and-consent) is exact about what that outline contains and where it goes. ## What talks to the network Brilliant is local-first, not offline-only. Some things do reach Brilliant's servers (`api.brilliant.design`), and it's worth being clear about which: - **Update checks and downloads.** The app checks for new versions and downloads them when you update. - **Activation and sign-in.** Account tokens are exchanged and refreshed here when you sign in. - **Published projects.** Once you publish a project, its canvases and the assets they use sync to brilliant.design while the project is open. Nothing else on your disk goes with them. See [How sync works](/docs/platform/sync). - **Settings sync.** While you are signed in, a few preferences follow your account: keyboard shortcuts, theme, density, and any extra models you have added. Your API keys are never part of this. - **Built-in AI usage.** On your own key the built-in chat is free up to a daily limit, so the app reports a running count of the messages you send: a bare number, with no prompt, canvas, or design content attached. On Brilliant AI the request routes through Brilliant's proxy and is recorded in usage rows (the model, token counts, cost, plan, and timestamps), never prompt or design content. More on the limits in [Plans and billing](/docs/platform/plans-and-billing). - **Usage data.** Anonymous analytics always, and account-tied analytics only if you opt in (see below). - **Feedback you send.** When you press **Send** on the feedback card, your message posts to Brilliant's servers with a source label and your install id (plus a prompt id when it answers a Brilliant prompt, and a sign-in token when you are signed in, so the team can reply). Any extras you tick, such as app version, a recent error log, the chat transcript, or a canvas screenshot, ride along only then. Nothing posts until you press Send. - **Feedback prompt checks.** From launch onward, the app polls `api.brilliant.design/feedback/prompts` every 30 minutes with your install id, to see whether the Brilliant team has a targeted question for you. This runs whether or not you ever open feedback; it carries only the install id and brings back any waiting prompt. What never goes to Brilliant's servers: - **Unpublished designs.** Until you publish a project, its `.bl` files stay on your disk. - **Your own-key prompts.** What you type into the built-in chat on your own key goes to your provider, not to Brilliant. - **Your API keys.** Provider keys stay in your OS credential store and go only to that provider. Your own AI traffic is separate from all of this: it goes directly from the app to whichever provider you connected, never through Brilliant. Brilliant AI traffic routes through Brilliant's proxy to Google, recorded in the usage rows described above, never your prompt or design content. ## Usage data Brilliant collects two tiers of usage data, and you control the second one. **Anonymous analytics are always collected.** They carry a random per-install ID (a UUID generated once and regenerated if you reinstall) and never your account. The events are coarse: session start and end, active-time pings, and per-session interaction counts (how many times operations, undo, or redo ran), plus platform and app version. Counts and categories: no designs, no prompts. **Account-tied analytics are opt-in.** Turn on **Share Detailed Usage Data** in Settings to tie usage data to your account and help improve Brilliant. It's off by default. When it's off, only the anonymous tier is sent, and your per-install ID never rides along with account-level events. When it's on, those events are tied to your account instead. ![The general settings, including the Share Detailed Usage Data toggle|450x](/docs/images/settings-general.webp) Either way, analytics are counts and categories, not content. Your designs and prompts are not part of them. More on the setting itself in [Settings](/docs/workspace/settings). ## The local MCP server While Brilliant is open, it runs a local server so external coding agents (Claude Code, Cursor, and the rest) can drive your canvas over MCP. That server binds to loopback only: ``` http://127.0.0.1:3333/mcp ``` Localhost only means it's reachable from your own machine, not from the network or other computers. If port 3333 is busy, Brilliant falls back to 3334, then 3335. The connection has no authentication, so treat it like any other local developer tool: any process on your machine can reach it while the app is running. On the web (a brilliant.design tab) there is no server inside the browser. Instead, a small helper you install once (`npx -y @brilliant-hq/mcp`) runs on your machine and bridges your agent to the tab over loopback. The posture is deliberately tighter than the desktop endpoint: - **Loopback only.** The helper binds `127.0.0.1`, never a network interface, so nothing off your machine can reach it. - **Origin-checked.** The tab connection is accepted only from `https://brilliant.design`, over a loopback host, and only with a pairing token or a helper you have already approved. A page from any other origin is refused. - **Approved per agent.** An unrecognized helper has to be approved first, with an in-app card that names the agent, before it can touch your canvas. You can revoke that approval from Settings at any time. - **Zero server involvement.** None of this reaches Brilliant's servers. The agent, the helper, and the tab all talk over `127.0.0.1`, so no MCP or design data leaves your computer. Safari cannot use this yet; Chrome, Edge, and Firefox can. Setup for each agent is in [Use with AI agents](/docs/agents/overview). ## Next - **What each message sends:** [Privacy and consent](/docs/ai/privacy-and-consent). - **What publishing uploads and syncs:** [How sync works](/docs/platform/sync). - **How designs live on disk:** [Canvases and files](/docs/workspace/canvases-and-files). --- Source: https://brilliant.design/docs/workspace/settings.mdx # Settings Open Settings with `Cmd+,`. It is a single window with a left rail of panes: Account, Usage, Your own AI, General, MCP Connections, Permissions, Shortcuts, and About (Permissions is macOS-only; MCP Connections is desktop-only). This page walks each one at a glance, and closes with a platform-support matrix so you know what changes between macOS, Windows, and Linux. This is the desktop app's Settings; your account, profile, teams, and billing on the web have their own home at [Settings and your account](/docs/platform/settings-and-account). ## Account Sign in to your Brilliant account and manage your subscription (usage has its own Usage tab). This is your identity and billing home; connecting AI providers happens in the Your own AI pane. While you're signed in, a few preferences follow your account from machine to machine: your keyboard shortcuts, theme, density, and any extra models you've added. Your API keys never sync. They stay in your device's own credential store and go nowhere else. ## Usage Your AI usage at a glance: the Brilliant AI meter, a percent used with a reset date, and the Your own AI card. Both AI lanes report here in one place. ## Your own AI **Your own AI** (your own keys and coding agents) is where you connect the models the built-in chat runs on. Brilliant's chat runs on Brilliant AI by default. On your own AI, requests go straight from the app to the provider you chose and your keys never touch Brilliant's servers. - **Claude Code** leads the list. It has no API key, since it uses the local Claude Code binary and your own Claude subscription, so it shows a connection status instead of a key field. - **API-key providers** (Anthropic, OpenAI, Google, OpenRouter, and Quiver for image generation) each take a key, shown masked once set. Keys are encrypted in your system keychain and never shared with Brilliant. - **Custom providers** let you add any OpenAI-compatible endpoint. - **Playground** replays bundled demo conversations so you can see how the agent behaves before connecting anything. For the full walkthrough of providers, models, and how to pick between them, see [Providers and models](/docs/ai/providers-and-models). ## General App-wide preferences: - **Appearance:** light, dark, or follow the system setting. - **Disable Update Checks:** stop Brilliant from checking for new versions. - **Launch at Login:** open Brilliant when you sign in. - **Hide Menu Bar Icon** (macOS): hide the icon from the system menu bar; reach the app via the global hotkey or the Dock. - **Share Detailed Usage Data:** opt into usage data tied to your account. Anonymous analytics are always collected. - **Haptic Feedback** (macOS): subtle feedback on a Magic Trackpad. - **Overlay Mode** (macOS): enable the transparent, always-on-top overlay window and its accessibility setup. ![The General settings pane with appearance and app preferences|420x](/docs/images/settings-general.webp) ## MCP Connections Wire external MCP clients (Claude Code, Cursor, VS Code, Windsurf, and more) to Brilliant's local MCP server, so an agent in your editor can drive the canvas. Each row toggles whether Brilliant is in that tool's MCP config, read fresh from the config file every time. A refresh button re-reads the configs. Stdio-only clients that cannot take a remote URL yet (Claude Desktop) sit at the bottom as a non-actionable note. ![The MCP Connections pane with a row per supported client|420x](/docs/images/bank/settings/mcp/general.webp) To actually set up an agent end to end, see [Connect an Agent](/docs/agents/overview). ## Permissions (macOS) This pane is macOS only. It shows the two system permissions Brilliant may need, each with a link straight to the right pane in System Settings: - **Accessibility:** enables window management and the global hotkey. - **Screen Recording:** enables the snip tool and the color picker's eyedropper to read the screen. ## Shortcuts A quick-actions strip of the shortcuts most people rebind (global search, command palette, canvas search, AI input, and hide chrome), plus an **All keyboard shortcuts** link into the full reference where every command is remappable. That full panel is also one `Shift+?` away from anywhere. See [Keyboard shortcuts](/docs/workspace/shortcuts) for the complete list. ## About The app version and update status (click to see what is new), links to **Help and docs** and **Send feedback**, and a row of community links (X, GitHub, Discord, YouTube, Instagram). ## Platform-support matrix Brilliant runs on macOS and Windows. Most features are identical on both, but a few depend on platform capabilities. Here is what differs. | Feature | macOS | Windows | |---------|-------|---------| | **Snip tool** (screen capture, `S`) | Yes | Not available | | **Video export** (MP4, Replay) | Yes | Yes | | **MOV and transparent video export** | Yes | Not available | | **Overlay mode** (transparent always-on-top layer) | Yes | Not available | | **Passthrough** (click-through in overlay) | Yes | Not available | | **Permissions pane** | Shown (Accessibility, Screen Recording) | Hidden (not needed) | | **Haptic feedback** | Magic Trackpad only | Not available | | **Global hotkey** (overlay toggle) | `Ctrl+F`, fires even when unfocused | Unbound by default | Where a feature is not available, its command and any menu entry simply do not appear. ### Windows-remapped shortcuts On Windows, the app substitutes Ctrl for Cmd automatically, which makes a few macOS chords collide with essential Windows shortcuts. Those are either unbound or moved to a free chord. You can reassign any of them in the Shortcuts panel; they also stay reachable from the command palette and inspector. | Command | macOS | Windows | |---------|-------|---------| | Fit all content | `Cmd+Ctrl+A` | `Alt+A` | | Color selector | `Ctrl+C` | `Alt+C` | | Eyedropper | `Ctrl+Shift+C` | `Alt+Shift+C` | | Zoom to selection | `Cmd+Ctrl+F` | `Alt+Z` | | Disable zoom out | `Cmd+Ctrl+D` | `Alt+D` | | Duplicate canvas | `Cmd+Ctrl+N` | `Alt+N` | | Rotation levels 0 to 9 | `Cmd+Ctrl+0` to `9` | `Alt+Shift+0` to `9` | A handful of macOS defaults are unbound on Windows entirely, because their remapped chord would collide with a core editing command: the quick colors (`Ctrl+R/G/B/Y/O/P/W/K`), the gradient colors (`Ctrl+D/L`), Outline Text (`Cmd+Ctrl+O`), Center on selection (`Cmd+Ctrl+C`), and the blue, green, and red highlighter combos. Set those from the inspector or the command palette instead. The full breakdown is on the [Keyboard shortcuts](/docs/workspace/shortcuts) page. ## Next - **Pick and connect a model:** [Providers and models](/docs/ai/providers-and-models). - **Drive Brilliant from your editor:** [Connect an Agent](/docs/agents/overview). - **Remap any key:** [Keyboard shortcuts](/docs/workspace/shortcuts). --- Source: https://brilliant.design/docs/workspace/shortcuts.mdx # Keyboard shortcuts The full keyboard reference, grouped by what you are doing. Press `Shift+?` any time to open the same shortcuts inside the app, where every command is searchable and remappable. This page is long on purpose; use your browser's find to jump to a command. ## View and customize shortcuts Open the Shortcuts panel with `Shift+?`. Commands are grouped and searchable. ![The Keyboard Shortcuts panel, with commands grouped and a search field|420x](/docs/images/shortcuts-panel.webp) To rebind one: 1. Hover a command row to reveal its action buttons. 2. Click the **record** button and press your new key combination. It saves immediately. 3. Use the **trash** button to remove a shortcut (the command stays available in the palette), or **reset** to restore the default. If a new binding conflicts with another command, a warning appears and a context picker lets you set when each command is active, so two commands can share a chord as long as their contexts do not overlap (for example `Cmd+B` for Bold while editing text, and something else on the canvas). ## Platform key convention | This page says | macOS | Windows | |----------------|-------|---------| | `Cmd` | Command | `Ctrl` | | `Ctrl` | Control | `Ctrl` | | `Alt` | Option | `Alt` | | `Shift` | Shift | `Shift` | Read every `Cmd` below as `Ctrl` on Windows. Where that substitution would collide with an essential shortcut, Brilliant remaps or unbinds the command; those cases show a different value in the Windows column, and the exceptions are summarized under [Windows differences](#windows-differences). Anything marked "palette" has no default key and is run from the command palette (`Cmd+Shift+P`), where you can also assign it a key. ## Essentials Learn these first. | Action | macOS | Windows | |--------|-------|---------| | Move tool | `V` | `V` | | Undo / Redo | `Cmd+Z` / `Cmd+Shift+Z` | `Ctrl+Z` / `Ctrl+Shift+Z` | | Global search | `Cmd+K` | `Ctrl+K` | | Command palette | `Cmd+Shift+P` | `Ctrl+Shift+P` | | AI input | `/` | `/` | | Shortcuts reference | `Shift+?` | `Shift+?` | | Settings | `Cmd+,` | `Ctrl+,` | ## Tools | Tool | macOS | Windows | |------|-------|---------| | Move | `V` | `V` | | Hand | `H` | `H` | | Pen | `P` | `P` | | Pencil | `Shift+P` | `Shift+P` | | Rectangle (fill) | `R` | `R` | | Rectangle (stroke) | `Shift+R` | `Shift+R` | | Circle (fill) | `O` | `O` | | Circle (stroke) | `Shift+O` | `Shift+O` | | Line | `L` | `L` | | Arrow | `Shift+L` | `Shift+L` | | Text | `T` | `T` | | Frame | `F` | `F` | | Snip (screen capture) | `S` | Not available | | Scale mode (toggle) | `K` | `K` | Hold `Space` for a temporary hand tool (release to return). While drawing, hold `Shift` to constrain proportions and `Space` to reposition the in-progress shape. ## Selection and navigation | Action | macOS | Windows | |--------|-------|---------| | Select all | `Cmd+A` | `Ctrl+A` | | Select previous sibling | `Tab` | `Tab` | | Select next sibling | `Shift+Tab` | `Shift+Tab` | | Enter frame / edit element | `Enter` | `Enter` | | Select parent frame | `Shift+Enter` | `Shift+Enter` | | Exit / cancel (context-aware) | `Escape` | `Escape` | | Rename selected layer | `Cmd+R` | `Ctrl+R` | | Delete selected | `Backspace` | `Backspace` | | Hide / show selection | `Cmd+Shift+H` | `Ctrl+Shift+H` | A single `Escape` picks the most specific target: close the command palette, exit eyedropper, cancel a label edit, exit crop, clear a vector selection, exit vector or mask edit, close the AI chat, then clear the canvas selection. ## Tabs Open projects and the home screen live as tabs in the top island. These move between them. | Action | macOS | Windows | |--------|-------|---------| | Next tab | `Ctrl+Tab` | `Ctrl+Tab` | | Previous tab | `Ctrl+Shift+Tab` | `Ctrl+Shift+Tab` | | Go to home | `Cmd+Ctrl+0` | `Ctrl+0` | | Jump to tab 1 to 9 | `Cmd+Ctrl+1` to `9` | `Ctrl+1` to `9` | Middle-click a tab to close it, and drag tabs to reorder. On macOS, `Cmd+Ctrl+0` to `9` is a shared chord: with an element selected it sets the rotation level, and with nothing selected it jumps to a tab (home is 0). ## Move and transform | Action | macOS | Windows | |--------|-------|---------| | Nudge 1px | Arrow keys | Arrow keys | | Nudge 10px | `Shift+Arrow` | `Shift+Arrow` | | Flip horizontally | `Shift+H` | `Shift+H` | | Flip vertically | `Shift+V` | `Shift+V` | Modifiers while dragging: `Alt` duplicates while moving, `Shift` constrains to one axis. While resizing a handle: `Shift` keeps aspect ratio, `Ctrl` enters scale mode (also scales fonts, strokes, radii, and children), `Cmd` preserves an image crop in place. `Alt+hover` shows distance measurements to the hovered element. ## Clipboard | Action | macOS | Windows | |--------|-------|---------| | Copy | `Cmd+C` | `Ctrl+C` | | Cut | `Cmd+X` | `Ctrl+X` | | Paste | `Cmd+V` | `Ctrl+V` | | Duplicate | `Cmd+D` | `Ctrl+D` | | Delete | `Backspace` | `Backspace` | ## Arrange ### Align and distribute | Action | macOS | Windows | |--------|-------|---------| | Align left / right | `Alt+Shift+L` / `Alt+Shift+R` | `Alt+Shift+L` / `Alt+Shift+R` | | Align top / bottom | `Alt+Shift+T` / `Alt+Shift+B` | `Alt+Shift+T` / `Alt+Shift+B` | | Center horizontally / vertically | `Alt+H` / `Alt+V` | `Alt+H` / `Alt+V` | | Align horizontally / vertically | `Alt+Shift+H` / `Alt+Shift+V` | `Alt+Shift+H` / `Alt+Shift+V` | | Distribute horizontally / vertically | `Ctrl+Alt+H` / `Ctrl+Alt+V` | `Ctrl+Alt+H` / `Ctrl+Alt+V` | | Fit to parent | `Ctrl+Alt+F` | `Ctrl+Alt+F` | ### Layer order | Action | macOS | Windows | |--------|-------|---------| | Bring to front / Send to back | `]` / `[` | `]` / `[` | | Bring forward / Send backward | `Cmd+]` / `Cmd+[` | `Ctrl+]` / `Ctrl+[` | ### Group, frame, boolean | Action | macOS | Windows | |--------|-------|---------| | Group | `Cmd+G` | `Ctrl+G` | | Ungroup | `Cmd+Shift+G` | `Ctrl+Shift+G` | | Frame selection | `Cmd+F` | `Ctrl+F` | | Add auto layout | `Shift+A` | `Shift+A` | | Flatten to a single vector | `Cmd+Enter` | `Ctrl+Enter` | | Boolean union | `Alt+Shift+U` | `Alt+Shift+U` | | Boolean subtract | `Alt+Shift+S` | `Alt+Shift+S` | | Boolean intersect | `Alt+Shift+I` | `Alt+Shift+I` | | Boolean exclude | `Alt+Shift+E` | `Alt+Shift+E` | | Mask | `Cmd+Ctrl+M` | `Ctrl+M` | | Outline text (text to path) | `Cmd+Ctrl+O` | Unbound (palette) | Boolean ops need two or more elements. If a boolean or mask group is already selected, the same shortcut switches its operation type. ## Components | Action | macOS | Windows | |--------|-------|---------| | Create component | `Cmd+Alt+K` | `Ctrl+Alt+K` | | Detach instance | `Cmd+Alt+B` | `Ctrl+Alt+B` | | Create instance | palette | palette | | Create component set | palette | palette | | Go to master | palette | palette | | Reset instance overrides | palette | palette | | Push overrides to master | palette | palette | ## Text | Action | macOS | Windows | |--------|-------|---------| | Bold | `Cmd+B` | `Ctrl+B` | | Italic | `Cmd+I` | `Ctrl+I` | | Underline | `Cmd+U` | `Ctrl+U` | | Align text left | `Cmd+Alt+L` | `Ctrl+Alt+L` | | Align text center | `Cmd+Alt+T` | `Ctrl+Alt+T` | | Align text right | `Cmd+Alt+R` | `Ctrl+Alt+R` | | Outline text | `Cmd+Ctrl+O` | Unbound (palette) | | Font selector | `Cmd+Shift+F` | `Ctrl+Shift+F` | Text sizing, line height, font weight, and text direction have no default keys; set them from the Typography section or the command palette. ## Color, fill, and stroke | Action | macOS | Windows | |--------|-------|---------| | Add fill / Remove fill | `Shift+F` / `Alt+F` | `Shift+F` / `Alt+F` | | Add stroke / Remove stroke | `Shift+S` / `Alt+S` | `Shift+S` / `Alt+S` | | Swap fill and stroke | `Shift+X` | `Shift+X` | | Open color selector | `Ctrl+C` | `Alt+C` | | Eyedropper | `Ctrl+Shift+C` | `Alt+Shift+C` | ### Quick colors The quick-color chords are macOS defaults. On Windows they are unbound (they collide with core editing shortcuts), so set color from the inspector or the palette. | Color | macOS | Windows | |-------|-------|---------| | Red / Green / Blue | `Ctrl+R` / `Ctrl+G` / `Ctrl+B` | Unbound | | Yellow / Orange / Purple | `Ctrl+Y` / `Ctrl+O` / `Ctrl+P` | Unbound | | White / Black | `Ctrl+W` / `Ctrl+K` | Unbound | | Gradient (dark) / (light) | `Ctrl+D` / `Ctrl+L` | Unbound | ## Levels Level keys jump a property to a preset step. | Action | macOS | Windows | |--------|-------|---------| | Size level 0 to 9 (drawing tools) | `0` to `9` | `0` to `9` | | Increase / decrease size | `Shift+=` / `-` | `=` / `-` | | Transparency 0% to 90% | `Cmd+Shift+0` to `9` | `Ctrl+Shift+0` to `9` | | Increase / decrease transparency | `Cmd+Shift+=` / `Cmd+Shift+-` | `Ctrl+Shift+=` / `Ctrl+Shift+-` | | Corner radius level 0 to 9 | `Cmd+Alt+0` to `9` | `Ctrl+Alt+0` to `9` | | Increase / decrease radius | `Cmd+Alt+Shift+=` / `Cmd+Alt+-` | `Ctrl+Alt+Shift+=` / `Ctrl+Alt+-` | | Scale level 0 to 9 | `Alt+0` to `9` | Unbound | | Scale up / down | `Alt+Shift+=` / `Alt+-` | `Alt+Shift+=` / `Alt+-` | | Rotation level 0 to 9 (clock positions) | `Cmd+Ctrl+0` to `9` | `Alt+Shift+0` to `9` | | Increase / decrease rotation | `Cmd+Ctrl+Shift+=` / `Cmd+Ctrl+-` | `Shift+=` / `Shift+-` | Rotation levels use a clock metaphor: level 1 is 30 degrees, level 2 is 60, and so on, in 30-degree steps. ## Zoom and canvas view | Action | macOS | Windows | |--------|-------|---------| | Zoom in / out | `Cmd+=` / `Cmd+-` | `Ctrl+=` / `Ctrl+-` | | Zoom 100% to 900% | `1` to `9` (Move or Hand tool) | `1` to `9` | | Toggle zoom (current / last) | `0` (Move or Hand tool) | `0` | | Center on selection | `Cmd+Ctrl+C` | Unbound (palette) | | Zoom to selection | `Cmd+Ctrl+F` | `Alt+Z` | | Fit all content | `Cmd+Ctrl+A` | `Alt+A` | | Disable zoom out below 100% | `Cmd+Ctrl+D` | `Alt+D` | Bare digits `0` to `9` control zoom in the Move and Hand tools, but control size levels while a drawing tool is active. ## Grids and snapping | Action | macOS | Windows | |--------|-------|---------| | Toggle layout grids | `Shift+G` | `Shift+G` | | Toggle pixel grid | `Cmd+'` | `Ctrl+'` | | Toggle snap to pixel grid | `Cmd+Shift+'` | `Ctrl+Shift+'` | | Toggle rulers | `Shift+U` | `Shift+U` | Snap guides, dimension labels, vector snapping, and clearing ruler guides have no default keys; toggle them from the command palette. ## Window, background, and overlay | Action | macOS | Windows | |--------|-------|---------| | Show / hide UI chrome | `Cmd+\` | `Ctrl+\` | | Expand / collapse right-toolbar sections | `Cmd+/` | `Ctrl+/` | | Toggle canvas background | `Cmd+Shift+D` | `Ctrl+Shift+D` | | Toggle blackboard | `Cmd+Shift+B` | `Ctrl+Shift+B` | | Toggle whiteboard | `Cmd+Shift+W` | `Ctrl+Shift+W` | | Presentation mode | `Alt+P` | `Alt+P` | | Clear all elements on canvas | `C` | `C` | | Toggle overlay mode | `Ctrl+F` (global) | Not available | | Toggle passthrough (overlay) | `Ctrl+A` (global) | Not available | | Toggle desktop icons (overlay) | `Ctrl+I` | Not available | Overlay mode is macOS only. Its `Ctrl+F` and `Ctrl+A` hotkeys are OS-level and fire even when Brilliant is unfocused. ## Canvas and file management | Action | macOS | Windows | |--------|-------|---------| | New canvas | `Cmd+N` | `Ctrl+N` | | Duplicate canvas | `Cmd+Ctrl+N` | `Alt+N` | | New folder | `Cmd+Shift+N` | `Ctrl+Shift+N` | | Rename canvas | `Alt+Enter` | `Alt+Enter` | | Delete canvas | `Cmd+Shift+Delete` | `Ctrl+Shift+Delete` | | Next / previous canvas | `Alt+Right` / `Alt+Left` | `Alt+Right` / `Alt+Left` | | Previously active canvas | `Ctrl+Alt+Left` | `Ctrl+Alt+Left` | | Focus active canvas in explorer | `Cmd+Shift+K` | `Ctrl+Shift+K` | | Toggle expand/collapse all folders | `Cmd+Shift+C` | `Ctrl+Shift+C` | | Open folder (workspace) | `Cmd+O` | `Ctrl+O` | | Import | `Cmd+Shift+O` | `Ctrl+Shift+O` | | Save as | `Cmd+Shift+S` | `Ctrl+Shift+S` | | Export selection to PNG | `Cmd+E` | `Ctrl+E` | `Cmd+E` is the only export shortcut. Every other export and copy-as format is run from the command palette or the right-click menus. ## Panels and search | Action | macOS | Windows | |--------|-------|---------| | Global search (all categories) | `Cmd+K` | `Ctrl+K` | | Command palette (commands only) | `Cmd+Shift+P` | `Ctrl+Shift+P` | | Canvas search | `Cmd+P` | `Ctrl+P` | | Layer search | `Cmd+L` | `Ctrl+L` | | Chat search | `Cmd+Shift+I` | `Ctrl+Shift+I` | | Font selector | `Cmd+Shift+F` | `Ctrl+Shift+F` | | Settings | `Cmd+,` | `Ctrl+,` | | Combos | `Cmd+Shift+M` | `Ctrl+Shift+M` | | Toggle left / right / bottom toolbar | `Cmd+Shift+Left` / `Right` / `Down` | `Ctrl+Shift+Left` / `Right` / `Down` | | Focus file explorer | `Cmd+Shift+E` | `Ctrl+Shift+E` | | Focus layers explorer | `Cmd+Shift+R` | `Ctrl+Shift+R` | | Focus AI input | `/` | `/` | | Check for updates | `Cmd+Shift+U` | `Ctrl+Shift+U` | ## AI chat | Action | macOS | Windows | |--------|-------|---------| | Focus chat session 1 to 9 | `Cmd+1` to `Cmd+9` | `Alt+1` to `Alt+9` | | Focus chat session 10 | `Cmd+0` | `Alt+0` | | Next / previous chat | `Cmd+Shift+]` / `Cmd+Shift+[` | `Ctrl+Shift+]` / `Ctrl+Shift+[` | | New chat (when the AI input is focused) | `Cmd+N` | `Ctrl+N` | | Toggle chat explorer | `Cmd+Shift+A` | `Ctrl+Shift+A` | | Close chat (when chat is focused) | `Cmd+W` | `Ctrl+W` | `Cmd+N` creates a new chat only when the AI input is focused; with focus on the canvas it creates a new canvas. `Cmd+Shift+A` toggles the chat explorer at any time. ## Combos (macros) Combos chain several actions behind one shortcut. Open the Combos editor with `Cmd+Shift+M`. | Combo | macOS | Windows | |-------|-------|---------| | Yellow highlighter | `Ctrl+Shift+Y` | `Ctrl+Shift+Y` | | Red highlighter | `Ctrl+Shift+R` | Unbound | | Green highlighter | `Ctrl+Shift+G` | Unbound | | Blue highlighter | `Ctrl+Shift+B` | Unbound | ## Limitations - **No chord (multi-key) shortcuts.** Each shortcut is one key plus optional modifiers; you cannot bind sequences like `Ctrl+K, Ctrl+C`. Use Combos (`Cmd+Shift+M`) to chain actions behind a single key. - **Hold-modifier behaviors are fixed.** `Space` (temporary hand), `Alt+hover` (measurements), `Alt+drag` (duplicate), `Shift+drag` (constrain), and `Ctrl+drag` (scale) are built in and cannot be reassigned. The `K` key toggles persistent scale mode as an alternative to holding `Ctrl`. ## Windows differences On Windows, Brilliant substitutes `Ctrl` for `Cmd` automatically. A few macOS chords collide with essential Windows shortcuts, so they are either moved to a free chord or left unbound. Every one is reassignable in the Shortcuts panel and reachable from the command palette. Remapped: | Command | macOS | Windows | |---------|-------|---------| | Fit all content | `Cmd+Ctrl+A` | `Alt+A` | | Color selector | `Ctrl+C` | `Alt+C` | | Eyedropper | `Ctrl+Shift+C` | `Alt+Shift+C` | | Zoom to selection | `Cmd+Ctrl+F` | `Alt+Z` | | Disable zoom out | `Cmd+Ctrl+D` | `Alt+D` | | Duplicate canvas | `Cmd+Ctrl+N` | `Alt+N` | | Increase size | `Shift+=` | `=` | | Increase / decrease rotation | `Cmd+Ctrl+Shift+=` / `Cmd+Ctrl+-` | `Shift+=` / `Shift+-` | | Rotation levels 0 to 9 | `Cmd+Ctrl+0` to `9` | `Alt+Shift+0` to `9` | | Focus chat session 1 to 9 | `Cmd+1` to `9` | `Alt+1` to `9` | | Focus chat session 10 | `Cmd+0` | `Alt+0` | Unbound on Windows: overlay mode, passthrough, desktop icons, the snip tool, the scale levels, quick colors, gradient colors, Outline Text, Center on selection, and the blue, green, and red highlighter combos. ## Next - **Where these actions live in the UI:** [Quick Tour](/docs/getting-started/first-steps). - **Change any binding:** [Settings](/docs/workspace/settings). --- Source: https://brilliant.design/docs/workspace/troubleshooting.mdx # Troubleshooting Something not behaving? This page collects the failures people actually run into, grouped by where they show up, with a link to the deeper fix for each. Skim for your symptom and follow the link. ## Installing ### Why won't macOS let me open Brilliant? macOS shows a one-time confirmation the first time you launch an app that didn't come from the App Store. When the dialog appears, click **Open** and you're in. You only do this once. The full walkthrough, with a screenshot of the dialog, is on the [Download & Install](/docs/getting-started/installation) page. ![macOS Gatekeeper dialog asking to confirm opening Brilliant|450x](/docs/images/install-gatekeeper.webp) ### Why does Windows warn me before installing? If Windows shows a "Windows protected your PC" notice while you run the setup, click **More info**, then **Run anyway**. Brilliant keeps itself up to date from inside the app, so this warning only appears the first time. Details are on the [Download & Install](/docs/getting-started/installation) page. ## The canvas ### The canvas says "Rendering can't start." How do I fix it? If Brilliant can't bring up its renderer at launch, the canvas shows a panel titled **Rendering can't start** with a short reason and two buttons. Click **Retry** to load the renderer again. If it keeps failing, click **Report** to copy diagnostics to your clipboard, then quit and relaunch the app. ### The canvas stopped rendering. Did I lose my work? No. If the renderer stops while you're working, Brilliant first tries to recover on its own, showing a small **Restoring the canvas…** chip while it does. If that doesn't take, it shows a panel titled **Something went wrong rendering the canvas** and tells you your work is safe. Click **Restart rendering** to rebuild the canvas and keep going: your design is intact, and the button brings the view back without touching your file. If it keeps failing, click **Report** to copy diagnostics to your clipboard. ## AI and agents ### Why don't I see Brilliant AI in the model picker? Brilliant AI on desktop needs version 0.1.0-beta.71 or later; on earlier builds the chat runs on your own AI only, so update the app. If the picker shows it but a send answers "Brilliant AI is unavailable right now", the lane is off for your account or network for the moment: sign in, or try again in a while; your own AI keeps working. ### Why was my API key rejected in the built-in chat? Keys are checked the moment you save them, so a bad key fails right away with an inline error instead of failing mid-chat. The usual culprits: the key was pasted with extra whitespace, the account behind it has no billing or credit set up, or (for a custom endpoint) the base URL or model id is wrong. Fix the key on its row in **Settings → Your own AI** and save again. See [Providers and models](/docs/ai/providers-and-models) for where keys live and how validation works. ### Why is the chat replaying a demo instead of answering me? Playground is a toggle, not a fallback: with nothing connected your messages run on Brilliant AI. If you are seeing demos, turn off the Playground toggle at the bottom of the Your own AI pane. More in [Connect an AI Provider](/docs/getting-started/connect-provider). ## Connections ### My coding agent can't see Brilliant's tools. What's wrong? Two things have to be true for an external agent to reach Brilliant. The app has to be open, because the local server only runs while Brilliant is running. And the agent has to be restarted after you connect it, since most tools load their MCP servers once at startup. In Claude Code, run `/mcp` to confirm `brilliant` is listed and connected. Tool-by-tool steps and checks live on each agent page, starting with [Use with AI agents](/docs/agents/overview). ### What if port 3333 is already taken? Brilliant serves its local endpoint at `http://127.0.0.1:3333/mcp`. If that port is busy (a second Brilliant window, or another app holding it), Brilliant falls back to `3334`, then `3335`, so every instance gets its own address. When you connect a tool through **Settings → MCP Connections**, Brilliant writes the current address for you, and that page always shows the live endpoint. See [Use with AI agents](/docs/agents/overview) for the connection model. ### Why does the connection show as off when Brilliant is already in my config? The checkmark on each tool's row reflects one thing: whether that tool's config points at Brilliant's current local address. If you hand-edited the config, or Brilliant landed on a fallback port (3334 or 3335) while your config still points at 3333, the row reads as unchecked even though a `brilliant` entry exists. Toggle it on to rewrite the entry to the correct URL, then restart the tool. More in [Use with AI agents](/docs/agents/overview) and the [Claude Code](/docs/agents/claude-code) page. --- Source: https://brilliant.design/docs/workspace/video-and-replay.mdx # Video and replay A still image is not always the best way to show off a design. When your work has motion in it, or you just want a reveal that pops in a feed, Brilliant renders it straight to video. This page covers two flavors: plain video export (MP4 and MOV) and Replay, a one-click animated reveal of your selection. Both start the same way as any export: they work on your **selection**, not the whole canvas. Select a frame and its children come along. ## Export a video Video lives in the **Export panel** in the right toolbar. It appears when you have something selected. There's no keyboard shortcut and no command for plain video, since the options only make sense inline. 1. Select the elements you want to capture. 2. In the Export panel, set the format to **MP4** or **MOV**. 3. Open the config (the gear) to reveal the video options: duration, FPS, quality, and codec. 4. Click **Export**, then pick a location. A progress bar counts frames as it renders, and you can cancel mid-render. ### Video options | Option | Values | Default | |--------|--------|---------| | Duration | 0.5 to 60 seconds | 10s | | FPS | 15, 24, 30, 60 | 60 | | Quality | Low, Medium, High | Medium | | Resolution | Same presets as image export (Original 1x through 4x, 720p through 8K, social and device presets, Custom) | Original (1x) | The codec follows the format. MP4 gives you H.264 or HEVC (no transparency). MOV gives you HEVC with alpha or ProRes 4444, and only a MOV with one of those alpha codecs can carry a transparent background. Pick a transparent background with an opaque format and it quietly falls back to the canvas color. Video captures **animated shader fills** in motion: a metaball, liquid metal, or holographic fill renders a real frame per tick. Everything else looks identical in every frame, since there's no keyframe timeline. If your design has no animated shaders, an image export gets you the same result faster. ## Which platforms can export video? This is the part worth reading before you plan a deliverable, because it's not the same on every OS. | Platform | MP4 | MOV | Transparent video | |----------|-----|-----|-------------------| | macOS | Yes (H.264 or HEVC) | Yes (HEVC alpha or ProRes 4444) | Yes, via MOV | | Windows | Yes (H.264 only) | No | No | | Linux | No | No | No | macOS has the full picture: both containers, both video codecs, and transparent output through MOV. On Windows you get MP4 with H.264 only, so the codec choice is fixed and the MOV option is hidden. Linux has no video export at all. The practical takeaway: an MP4 for a social post or a message works on macOS and Windows. If you need a video with a transparent background (to composite over something else), that's macOS and MOV only. ## Replay: a one-click reveal Replay animates your selection into view, one element after another, each fading in with a shimmer pass. It's the fastest way to make a build feel alive for a post or a walkthrough, and unlike plain video it has a sensible default, so it doesn't need the full options panel. Run it three ways: - **Command palette:** search for **Export to Replay**. - **Right-click** the selection and choose **Export as**, then **Replay**. This route renders at 2x for crisp retina output. - **Export panel:** set the format to **Replay**. Replay files as an MP4 by default. On macOS you can switch its container to MOV for a transparent background. On Windows it stays MP4. The total length isn't a fixed duration: it comes from a per-element pace (default 150ms each) multiplied by how many elements are in your selection, so a bigger design plays longer. You can also add a short intro text card before the reveal. Because Replay uses the same encoders as plain video, it follows the same platform rules: MP4 on macOS and Windows, MOV (and transparency) on macOS only. It's not available on Linux. Replay animates whatever you've **selected on the canvas**. It's a reveal of your elements, not a recording of an AI chat session, so you don't need a chat open to use it. Point it at a finished layout and it builds that layout back up on screen. ## Sharing what you made - **Social:** an MP4 at a square or story resolution drops straight into a post. Replay is built for exactly this: a design that assembles itself reads well in a feed. - **Handoff:** a short clip shows a teammate how a shader fill actually moves, which a screenshot can't. For a still handoff of the same work, see [Import and export](/docs/workspace/import-and-export). - **Transparent overlays:** need the design to sit over other footage? Export a MOV with an alpha codec on macOS. ## Next - **Every other format:** [Import and export](/docs/workspace/import-and-export). - **The fills that make video worth it:** [Shaders and filters](/docs/editor/shaders-and-filters). - **Where your files live:** [Canvases and files](/docs/workspace/canvases-and-files). # Section: The platform --- Source: https://brilliant.design/docs/platform/collaboration.mdx # Collaborate live Once a project is published, you can bring other people in. Editing is genuinely simultaneous: two people (or ten) can work on the same canvas at once and see each other move. ## Inviting people You invite from **Settings, in the Sharing tab, under Access**. Only the owner can invite. - **Who:** an email address or a Brilliant `@handle`. - **As what:** a **viewer** (can look, inspect, and copy things out) or an **editor** (can change the design). A pending invite expires after **14 days** if it is not accepted. Invites are bound to the person you sent them to, so forwarding one to someone else grants them nothing. Role changes take effect right away. ## Editing together Everyone edits at the same time. Changes land element by element, so two people touching different parts of the canvas never block each other, and when two edits race on the same element, the last one wins. You can see who else is around: - **Presence avatars** show who is in the project. - **Colored cursors** show where each person is working. - **Presence sharing** is on by default for editors and owners. Toggle it off if you would rather not broadcast your cursor, and set yourself **Away** to drop off the roster entirely. ## Collaborating on private projects On a **public** project, collaboration has no strings: invite anyone as an editor and they can edit. On a **private** project, editing is a paid capability on the invitee's side too. If you invite someone to edit private work and they are on the Free plan, they get view-only access plus a hint to upgrade, until they do. This keeps the private-work-is-paid line consistent for everyone involved, not just the owner. There is also a count limit: on Personal a private project allows you plus **one guest editor**, while Team lifts that to unlimited editors on private work. See [Plans and billing](/docs/platform/plans-and-billing). ## Share links vs. invites Invites are for named people with roles. **Share links** are for showing private work to someone without inviting them. A share link is a short, read-only `brilliant.design/s/…` URL that anyone can open, no account needed. It works only on **private** projects, since a public project is already open to anyone with its link. The link stays live until you revoke it. Creating share links is a paid feature. Use one for a quick look; use an invite when you want someone to keep coming back or to edit. ## Sharing a drop A [drop](/docs/platform/drops) is a snapshot, not a live canvas, so its sharing is simpler than a project's. You reach it from the drop's **Settings** tab, under **Sharing**. - **Roles are viewer only.** Invite people by email and they can open the drop and read its comments, but a drop has no editor role at all: nobody but you can change it. This is the one place drops differ from projects, which do have editors. - **Share links are for private drops only.** A drop's share link is the same view-only `brilliant.design/s/…` URL a project uses, for showing a private drop to someone without an account. On a public drop it is refused, because a public drop's own address already opens for everyone. Minting share links is a paid feature, exactly as it is on projects. - **Private drops are paid.** Making a drop private is part of a paid plan, the same private-work-is-paid line as projects. See [Plans and billing](/docs/platform/plans-and-billing). - **Renaming redirects the old address.** Changing a drop's name moves its address; links to the old one keep redirecting until you reuse that name. ## Watching a public project Anyone can open a public project and watch it live, even signed out. If the owner or a collaborator is editing, an anonymous visitor sees the changes stream in and shows up in presence with a friendly auto-generated name. Guests can watch but never write. People who arrive through a read-only share link watch invisibly, with no presence. When someone is inside a project, its card carries a small **live** badge with a count of who is there, so you can tell at a glance which projects have activity right now. ## Next - **Understand what streams and when:** [How sync works](/docs/platform/sync). - **Set who can see the project at all:** [Publish a project](/docs/platform/projects). - **See why private collaboration is paid:** [Plans and billing](/docs/platform/plans-and-billing). --- Source: https://brilliant.design/docs/platform/drafts-and-review.mdx # Drafts and review Opening a project's mainline on the web gives you the full editor, the real Brilliant compiled to run in a browser (see [Brilliant on the web](/docs/platform/overview)). A **draft** is different. It is a proposed version of the project that has not landed yet, and reviewing one opens a lighter three-pane viewer built for looking it over, inspecting any piece of it, and deciding what happens next. If you have used GitHub, a draft is the pull request of the design world: a branch of work kept off the mainline until someone with the keys accepts it. ## What a draft is A draft is a named branch of your project, started from a checkpoint (or from the latest mainline) and worked on separately. Edits saved onto a draft never change what visitors see on the mainline. Nothing about the project's live face moves until you accept the draft. Drafts usually come from an agent or a tool working against your project. Each one is started off a checkpoint, then edits accumulate on top, exactly like commits on a branch. Every draft carries a state as it moves through review: - **Draft.** Being worked on. This is where a draft starts. - **In review.** Submitted, waiting for the owner to accept or send it back. - **Accepted.** Merged into the mainline as a new checkpoint. Its work is now the project's live version. The list of drafts on a project is public: anyone who can see the project can see its open drafts, each with its state and how many checkpoints it is ahead of the mainline. On your projects list, a project row shows its open-draft count next to its other stats, the same way GitHub shows open pull requests. ## The three-pane viewer A draft opens at `brilliant.design/{handle}/{project}/draft/{name}` in a three-pane layout: - **Left, the Canvases tree.** Every canvas in the project as a flat jump list, grouped by folder. It is a read-only index, not an expander: seeing every canvas at once is the point. Click one to bring it into the center. - **Center, the canvas.** The selected canvas, rendered live and fit to the frame. Click any element to select it. The render uses WebGPU, the same engine the editor runs on, so it needs a recent browser. Without WebGPU you still get a static snapshot of the canvas instead of a broken frame. - **Right, the inspector.** Read-only properties for whatever you have selected, plus the tools to copy it out. More on this below. The header carries a breadcrumb (`{handle} / {project}`), a **Draft** or **In review** badge naming the draft, a switcher to hop between the mainline and any other draft, and a **History** link showing the mainline's current checkpoint number. A footer offers the one-line command to open the whole project locally: `brilliant open {handle}/{project}`. The viewer is a desktop-width work surface. On a narrow window it steps aside with a note rather than crushing three panes into slivers, and on a phone or tablet a project opens in a touch-friendly read-only viewer instead. ## Inspecting an element Select an element and the inspector fills in what it is and what it is made of, every value ready to copy: - **Name and type.** - **Position and size:** X, Y, width, height, and corner radius. - **Text:** the content, font, and size, when the element is text. - **Fills, strokes, and effects:** each color as a swatch plus its hex, with opacity and stroke width where they apply. - **Design tokens:** the token each property is bound to, so you can see when a color or size comes from the design system rather than a raw value. With nothing selected, the inspector shows a canvas summary instead: its name, how many elements it holds, and its size. The inspector also gives you a **Link to this element**. Copy it and the URL points straight back at this canvas with this element selected, so a teammate lands exactly where you are. ## Copy as Every element you can see, you can take with you. The inspector's **Copy as** menu copies the selected element, and everything nested inside it, to your clipboard in four formats: - **Blueprint** — Brilliant's own compact design language (see [The Blueprint DSL](/docs/developers/blueprint)). Paste it into your own project to recreate the element exactly. - **React** — JSX with inline style objects, ready to drop into a `.tsx` file. - **HTML** — markup plus inline CSS. - **SVG** — the vector graphic. Brilliant confirms with a short "Copied as ..." note. This is the web mirror of the desktop **Copy as** submenu, and it is why a view-only visitor can still walk away with any piece of a public design: you cannot change the owner's work, but you can copy it out into your own. The [import and export](/docs/workspace/import-and-export) tour covers every format in depth. ## Reviewing a draft as the owner If you own the project, the draft header adds the controls that move it through review: - **Submit** sends an active draft in for review, flipping it to **In review**. - **Withdraw** pulls a draft back out of review to keep working. - **Accept** merges the draft into the mainline as a new checkpoint. When it merges cleanly, Brilliant confirms with the new checkpoint number and drops you into the mainline editor. When the draft's changes overlap edits made on the mainline since it branched, accepting cannot be automatic, and Brilliant hands you the conflict resolver instead. Only the owner sees these controls. Everyone else gets the read-only viewer. ## Resolving conflicts The resolver opens when accepting a draft collides with mainline changes. It is owner-only, and it lays the decision out plainly. You see the two versions of each conflicted canvas rendered **side by side**: **Mine**, the mainline as it stands, and **Theirs**, the draft. Elements that differ are highlighted on both sides. A panel on the right lists every element in conflict with a running count of how many are still left **to decide**. For each conflicted element you pick one of three: - **Mine** keeps the mainline's version. - **Theirs** takes the draft's version. - **Both** keeps them both. When a whole canvas should come from one side, **Take entire Mine** or **Take entire Theirs** decides it in one move. If several canvases conflict, each is a tab across the top. **Confirm and accept** writes your merged result as a new checkpoint on the mainline and accepts the draft. Because that write is a real, published checkpoint, Brilliant asks you to confirm it, and it cannot be undone. Two guardrails sit behind the merge. If either version of a canvas fails to load, the resolver stops and tells you rather than quietly merging around content nobody could read. And a resolution that would empty a canvas that actually holds elements is refused outright. An unreadable side is never treated as an empty one. ## Next - **Put work online and mark versions:** [Publish a project](/docs/platform/projects) covers publishing, checkpoints, and forks. - **Work on a project with other people:** [Collaborate live](/docs/platform/collaboration). - **Take a design into code:** [Import and export](/docs/workspace/import-and-export). --- Source: https://brilliant.design/docs/platform/drops.mdx # Drops A drop is a small, shareable piece of design that stands on its own. Where a project is the whole thing, a drop is one selection you want to send around: a button, a card, an icon set, a hero section. If projects are repositories, drops are gists. Same energy, smaller unit. ## Publishing a drop Select the elements you want to share, then run the **Publish drop** command (or right-click the selection and choose it). Add a title, subtitle, and a few tags if you like; all of them are optional. Confirm, and the drop is published and its link is on your clipboard, ready to paste. You need to be signed in to publish a drop. If you are not, Brilliant prompts you to sign in first. Every drop lives at a clean URL, under the handle that published it: ``` brilliant.design/{handle}/drops/{slug} ``` Older `brilliant.design/drops/{handle}/{slug}` links still work: they redirect to the new address, so anything already shared keeps landing. ## Public by default Like projects, drops are **public by default**, and public is free. A private drop is a paid feature. Drops do not expire. The one hard limit is size: a drop can be up to **5 MB**. Titles, subtitles, and tags have generous length caps, so the practical ceiling is the design itself. ## Browsing and starring Public drops surface in two places, and both are the same wall of tiles. [Explore](https://brilliant.design/explore) is the merged feed: projects and drops together, newest first. The [drops feed](https://brilliant.design/drops) is that same wall, filtered to drops only. On either one you can sort by **Recent** (the default), **Popular**, or **New**, and filter by tag. Drop tiles are sized to the drop itself: each tile's preview takes the shape of the design inside it, so a wide banner and a tall card sit in the wall at their true proportions instead of being cropped to one fixed box. On any drop you can: - **Star** it, GitHub-style, to bookmark it publicly. Your stars show up on your profile. - **Fork** it, to make your own copy to build on. Right-click any tile (or use the **…** button that appears on it) for those actions in one menu: **Copy blueprint** (the drop's design as Blueprint text, ready to paste into your own work), **Copy link**, **Open**, **Open in new tab**, **Star**, and **Fork**. If the drop is yours, the menu also carries a visibility toggle to **Make private** or **Make public**. ## Importing a drop onto your canvas Found a drop you want to use? Run the **Import drop from link** command and paste the drop's URL. Its contents land on your current canvas as real, editable elements, ready to adapt. ## A drop's tabs Open a drop and it carries a small tab strip: **Drop Overview**, **History**, and, if the drop is yours, **Settings**. - **Drop Overview** is the drop itself, the page you land on. - **History** is empty for now. - **Settings** is where you, as the owner, change the drop's name, description, and tags, decide who can see it, and delete it. It is the only door in: there is no separate "edit" button, on the tile or on the drop's page. Changing the name also moves the drop's address, so Settings asks you to confirm that one: links to the old address keep redirecting until you reuse it. Re-publishing a drop's design is not available yet: to share a new version of the design itself, publish a new drop. ## Sharing a drop with specific people A drop's **Sharing** settings are where you decide who can see it: - **Visibility.** Drops are public by default. Private drops are part of a paid plan. - **People.** Invite anyone by email. They get view access: they can open the drop and comment, but never edit it. - **Share links.** For a private drop, mint a short link that lets anyone open it without signing in. Links are view-only, you can see how many times each was opened, and you can revoke any one of them on its own. A public drop does not need one, since its address already works for everybody. ## Next - **How drop access compares to a project's:** [Collaborate live](/docs/platform/collaboration). - **The handle in every drop URL:** [Handles and profiles](/docs/platform/handles-and-profiles). - **When private drops make sense:** [Plans and billing](/docs/platform/plans-and-billing). --- Source: https://brilliant.design/docs/platform/handles-and-profiles.mdx # Handles and profiles Every account has a handle, and every handle has a profile. This is your identity on [brilliant.design](https://brilliant.design): the address people visit, the name on your projects and drops, and the front page of your work. ## Your handle When you sign up, Brilliant assigns you a friendly word-pair handle automatically, something like `@blue-penguin`. Nothing from your email address is ever used or published; the handle is generated, not derived from who you are. Your profile lives at `brilliant.design/{handle}`, and your handle is what appears in every project and drop URL you publish. ## Your profile page Your profile is organized into four tabs: - **Overview:** your README, your pinned and recent projects, and a year-long activity graph. - **Projects:** everything you have published. - **Drops:** your published drops. - **Stars:** the projects and drops you have starred. The tab counts both, and the page renders as a wall of tiles, the same layout as Explore. Starring is public, so anyone can see what you keep here. See [Projects](/docs/platform/projects) for how it works. Every star and fork count you see, on a project or a drop, is a button. Click a star count to see who starred it, or a fork count to see the forks people made from it: both open the same activity list, on the tab you clicked. It is the same list whether you open it from a project card, a drop's page, or a drop tile. Two nice details worth knowing: - **Your README is just a project.** Create a project named exactly your handle, put a `README.md` at its root, and it becomes the README card on your profile. Editing your profile intro is editing a design file, the same way everything else works. - **Pin your best work.** You can pin up to four projects to the top of your Overview so visitors see your strongest work first. Leave the set empty and Brilliant falls back to showing your most-starred instead. ![The Customize your pins picker, showing the four-pin cap and the note that an empty set falls back to your most-starred|720x](/docs/images/profile-pins-editor.webp) You also carry a **Member #N**, your permanent early-adopter number. It never changes, even if you rename your handle. ## Following people Profiles carry a **follower** and a **following** count, right in the identity rail. On anyone else's profile there is a **Follow** button too, the visitor's version of the Edit button you see on your own. Following is instant: the button flips to **Following** and the count ticks the moment you click, and if the request ever fails it quietly rolls back. Click Follow while signed out and Brilliant pops up sign-in, then completes the follow in the same motion once you are in. Both counts are buttons. Click one and a list opens with two tabs, **followers** and **following**, landing on the one you clicked. Each list is searchable, and every row carries the person's avatar, name, handle, and bio, plus its own Follow / Following button, so you can follow someone straight from the list without leaving it. Your own row never shows the button, and it is hidden entirely when you are signed out. If a profile predates follows, or a server does not have the feature, the counts and the button simply are not there. Brilliant never paints a fake zero. ![The Followers and Following list, with a search field, rich rows, and a Follow button on each row|760x](/docs/images/profile-follow-list.webp) ## Notifications There is a **bell** in the platform chrome with an unread badge, and it carries your social life on the platform: someone **followed** you, someone **starred** or **forked** one of your drops or projects, someone **invited** you to a team or to collaborate, and new **publishes** from the people and teams you follow. Every row links straight to the thing it is about: a new follower's row opens their profile, a fork's row opens the fork, an invite's row opens the invite card. Busy events are folded rather than repeated: a drop that picks up stars shows one living row, "Ada and 4 others starred Neon dashboard", and it counts as unread again whenever someone new joins it. Quiet, high-signal events (invites, forks, publishes) always get their own row. Opening the panel marks everything read and clears the badge, and **View all** at the bottom of the panel opens [/notifications](https://brilliant.design/notifications), the full history, grouped by day, going back as far as you like. The bell follows your acting identity: acting as yourself it shows your notifications, and switching to a team shows the team's, so a team's new followers and starred drops live behind the team's bell. Signed out, or on a surface that does not serve notifications yet, it just says you are all caught up rather than showing an error. If a kind of notification is more noise than signal to you, you can mute it per kind (follows, stars, forks, publishes) in [Settings](/docs/platform/settings-and-account); invites always come through. ## Changing your handle You can change your handle any time. Old links do not break: visits to your previous handle redirect to your current one, so anything already shared keeps working. ## Short handles Handles of six characters or more are free. A **short handle**, anything under six characters, is a paid feature. If you try to claim one on the Free plan, Brilliant shows it as available and points you to upgrade, but you cannot save it until you are on a paid plan. See [Plans and billing](/docs/platform/plans-and-billing). ## The blue badge Paid accounts get a **blue badge** on their profile. It is derived live from your plan, so it appears when you are on a paid plan and disappears if you go back to Free. Nothing else about your profile changes. ## The 30-day reclaim window When you rename away from a handle, it enters a **30-day reclaim window**: links still redirect to you, and you can take the old handle back at any time. A short handle you lose to a lapse works a little differently. It is not released: it is **reserved for your account** for 30 days (nobody else can claim it, and links keep redirecting), and **re-subscribing within the window restores it to you automatically**, with no reclaim step. After the window, the handle is free for anyone. ## Next - **Manage your account:** [Settings and your account](/docs/platform/settings-and-account). - **Share small pieces under your handle:** [Drops](/docs/platform/drops). - **What a paid plan unlocks:** [Plans and billing](/docs/platform/plans-and-billing). --- Source: https://brilliant.design/docs/platform/overview.mdx # Brilliant on the web Brilliant is a desktop app first, and your work lives as plain files on your own disk. But there is a whole other half at [brilliant.design](https://brilliant.design): a place to publish your designs, share them by link, collaborate live, and give every project a home on the web. If you have used GitHub for code, the shape will feel familiar. You get a profile, you publish projects, you can fork what other people share, and small pieces travel around as drops. Nothing goes online until you choose to publish, and going public is always free. ## Signing in Brilliant has no passwords. You sign in one of two ways: - **Email code.** Enter your email and Brilliant sends you a 6-digit code. Type it back and you are in. The code is good for a few minutes. - **Google.** One tap, and you are signed in with your Google account. It is the same account on desktop and on the web, so publishing from the app and browsing your profile in a browser are the same identity. Signing in on desktop is required to use the app at all. See [Handles and profiles](/docs/platform/handles-and-profiles) for what your account looks like once you are in. ## Your profile is your home On the web, your home is your profile at `brilliant.design/{handle}`. Every account gets a friendly word-pair handle automatically (something like `@blue-penguin`), and your profile lives there. Your profile has four tabs: - **Overview:** a README card (drawn from a project named after your handle), your pinned and recent projects, and a year-long activity graph. - **Projects:** everything you have published. - **Drops:** your published drops. - **Stars:** the projects and drops you have starred. Full details on handles, pinning, and the blue badge are in [Handles and profiles](/docs/platform/handles-and-profiles). ## Opening a project in the browser A published project opens right in the browser. It is the real Brilliant editor, compiled to run on the web, not a watered-down viewer. - **If you own the project or were invited as an editor,** you get the full editor: the same tools, panels, and design commands you use on desktop. A couple of desktop-only things stay on desktop (moving files around on disk, importing another `.bl` file, and video export), but drawing, editing, importing images and SVGs, and exporting to PNG, SVG, and the rest all work. - **If you are just visiting a public project,** you get a view-only editor: pan, zoom, select, inspect properties, and copy elements out. You can look at everything and take pieces with you, but you cannot change the owner's work. The first time you try to change something, the owner's work stays untouched and you get a couple of doors: **Open in playground** flips the project into a throwaway, editable scratch space right where you are, so you can try your own edits with no account and no commitment; and, when you are signed in, **Fork** makes a permanent copy under your own handle. The web editor renders on WebGPU, so it needs a recent browser. Open it in **Chrome, Edge, Safari 26 or later, or Samsung Internet** and it works. In a browser without WebGPU, Brilliant tells you so instead of rendering a broken canvas. On a phone or tablet, a project opens in a read-only viewer built for touch: pinch to pan, tap an element to inspect it, and copy a share link. Forking and editing both happen in the full editor, one tap away under **Open on desktop**. ## The playground Want to try Brilliant with no account at all? [brilliant.design/playground](https://brilliant.design/playground) drops you straight into the editor. Design something real, then sign in and keep it: the canvas becomes your first project, live, right where you left off. Signed in, an unsaved scratch is kept on your device for an hour, so a refresh brings it back; signed out, nothing is kept until you sign in. This is not the same as the in-app **Playground** toggle, which lives under Settings > Your own AI and replays a recorded demo session rather than opening a live canvas. In the real editor your first message runs on Brilliant AI with no setup at all. ## The desktop home screen ties it together Back in the desktop app, the home screen is the bridge between the files on your disk and your projects in the cloud. It is a sidebar plus a grid of project tiles. The sidebar groups your work: **Recent**, **Local** (folders that live on your machine only), **Synced** (projects that live on your machine and in the cloud), and **Cloud** (projects that live only in the cloud). Each tile shows a few canvas thumbnails, the project name, and a small badge: nothing for a purely local project, a cloud or sync icon for a published one (sync when it also lives on your disk), plus a lock when it is private. So the same project can start as a local folder, become a published cloud project when you are ready, and show up on your web profile, all without ever leaving the app to make it happen. ## Next - **Put a project online:** [Publish a project](/docs/platform/projects) covers publishing, visibility, checkpoints, and forks. - **Understand what leaves your machine:** [How sync works](/docs/platform/sync) is the honest data story. - **See what you pay for:** [Plans and billing](/docs/platform/plans-and-billing). --- Source: https://brilliant.design/docs/platform/plans-and-billing.mdx # Plans and billing The model is simple: nothing about designing is paywalled. You pay for two things, private work and more built-in AI. The built-in AI comes two ways. **Brilliant AI** is the default: Brilliant-provided models, included with every plan, usage shown as a percent used and a reset date, that just work on your first message with no setup. **Your own AI** (your own keys and coding agents) is the alternative: bring an Anthropic, OpenAI, Google, or OpenRouter key, or a Claude Code or Codex subscription, and chat goes straight to your provider with zero markup, ever. Brilliant never takes a cut of your own-key tokens. > Free is for working in public. Personal is for working in private. ## Free Free is a real plan, not a trial. Everything in the editor is yours, with no feature gates: - **Every design feature**, unlimited. - **Unlimited public projects and drops.** - **Unlimited external agents over MCP.** Point [Claude Code, Cursor, or any MCP agent](/docs/agents/overview) at Brilliant and drive it as much as you want. - **Brilliant AI every month**, to use the built-in AI with no setup (see below). - **10 built-in chat messages a day on your own key** (see below). - **2 GB of storage.** ### The built-in AI on Free Free gives you two ways to use the built-in AI: - **Brilliant AI, included every month.** Your first message just works, no key and no setup, on a Brilliant-provided model. Free includes a small monthly allowance of Brilliant AI usage, which resets monthly with no rollover; usage shows as a percent used with a reset date (Settings > Usage, or `/usage` in the chat), never dollars. Part of it can be used before you sign in; sign in to keep going, free, and signing in starts a full month. - **10 messages a day on your own key.** Connect your own provider key and the built-in chat allows 10 messages a day. This is not Brilliant giving you 10 free AI messages: you pay your own provider for the tokens, so the cap is on the built-in chat feature, not on the AI itself. It counts the messages you type (one send is one message), applies even on your own key, resets at midnight UTC (shown in your local time), and playground demos never count. (See [why the built-in chat is worth paying for](/docs/getting-started/why-pay).) When the month's Brilliant AI usage or the daily key cap runs out, you get two doors: 1. **Upgrade** to a paid plan. Paid plans make the built-in chat unlimited on your own key and include Brilliant AI usage. A message blocked at the wall is held and sent automatically the instant your plan flips. 2. **Bring your own AI**, or keep driving Brilliant from an external agent over MCP: both are unlimited on every plan, including Free, and nothing you send that way counts against either limit. Brilliant AI never cuts a turn mid-response: the request in flight always finishes, then the next send opens the upgrade page, a page on the command-palette shell (the same overlay as sign in), which names when your usage resets; its primary door is the next paid rung and its secondary door brings your own AI. At the daily key cap the page reads "You've used today's 10 messages on your own AI. Personal removes the daily limit." with an Upgrade to Personal button, and agents over MCP keep going, unlimited. Your own AI is never metered or walled by usage. ## Personal Personal is **$20/month** (monthly only). It adds the private half of Brilliant, plus a base allowance of Brilliant AI: - **Private projects and drops.** - **Collaborate on private work.** Editing a private project needs a paid plan on the editor's side too, and Personal caps a private project at you plus **one guest editor** (Team lifts this to unlimited editors); details in [Collaborate live](/docs/platform/collaboration). - **Control what visitors see** (show only [checkpoints](/docs/platform/projects), not live work). - **Brilliant AI, the base allowance**, plus **unlimited built-in chat on your own key** (no daily cap). - **A short handle and the blue badge.** - **50 GB of storage.** Need more Brilliant AI? The usage tiers are standalone monthly prices that multiply the base, each including everything in Personal: **Pro $50 (5x), Max $100 (10x, most popular), Max 20x $200 (20x)**. Usage is sold as multiples of the base, shown as a percent used and a reset date, never credits or token math. Monthly only: the annual option is retired for new subscriptions, and an existing annual subscriber keeps their cycle until they choose otherwise. Switching rungs happens on your plan card in Settings > Usage: raising a tier applies immediately and is prorated; lowering one takes effect at renewal. ## Team **Team** starts at **$25 per seat per month** (monthly only), with a two-seat minimum. It is Personal for everyone on the team, plus a per-seat base allowance of Brilliant AI, plus a shared identity that owns the work (so projects survive any one person leaving), one invoice for the whole group, and an admin view. Seats come in whole-price tiers an admin assigns per member and mixes freely: **Personal seat $25, Pro seat $55 (5x), Max seat $105 (10x), Max 20x seat $205 (20x)**. Each seat's Brilliant AI usage is metered per seat on the team's cycle, never pooled, so one member cannot drain the team. The admin sees usage counts and each seat's tier, never the content of anyone's chats or designs. Your own keys and their API bills stay per-designer; Brilliant never marks up your own AI for teams either. A paid team also lifts the private-project editor limit: unlimited editors on private work, where Personal allows one guest editor. A member whose seat is spent can ask an admin for a bigger seat from the wall; the request clears when the admin raises it. A removed member falls back to their own plan or Free with its monthly Brilliant AI; teams get no separate allowance beyond the seats, and there is no team-wide daily message cap. For what a team is and how roles work, see [Teams](/docs/platform/teams). ![The three plans: Free, Personal, and Team](/docs/images/platform-pricing-tiers.webp) Full details and the current numbers live on the [pricing page](/pricing). ## Storage Your quota counts three things: the projects you **own** (a collaborator's edits count against the owner, not the editor), your drop payloads, and media like images and your avatar. Free personal accounts get 2 GB, and a paid Personal plan gets 50 GB. Team storage is pooled across the whole team rather than counted per seat: a paid team shares 1 TB, and a free team shares 4 GB. To see where you stand, open **web Settings, Plan & billing**, which shows a storage meter. On desktop, Settings > Usage shows your Brilliant AI usage and your daily count on your own AI; storage lives on the web. At the cap, the behavior is safe and predictable: - **Saves are refused, cleanly.** Nothing is half-written. You will see a plain message like "Storage full: 2.1 GB of 2 GB used." - **Viewing, exporting, and deleting always work.** You are never locked out of your own work. - **Deleting frees space immediately.** Remove something and the room comes back at once. In a live session, hitting the cap does not kick you out: your edits stay live, they just cannot be saved into the project's history until you free space or upgrade. ## Billing mechanics Checkout runs through Paddle. A Personal purchase completes inside the editor (a checkout page on the same overlay as sign in, hosting Paddle) and the plan flips live, continuing the message you were holding; a switch between paid rungs happens in place on the plan sheet (or on your plan card in web Settings > Usage); team plans finish in your browser and the app updates itself once you are done. The wall shows the plan's monthly price; usage is never quoted in dollars. You can **cancel any time.** A cancellation keeps your plan active until the end of the period you already paid for, then drops you to Free. Your invoices are in Settings. ## How team billing behaves A team's plan is managed from its **Plan & billing** settings, which only admins can open. It works like the personal plan, with a few things that are specific to buying seats for a group. **Seats prorate one way and defer the other.** A team pays per seat, with a two-seat minimum, and you can change the count at any time. Adding seats takes effect immediately: the proration is billed to your card right away and the new seats are usable at once. Removing seats is deferred: nothing is billed now, your current seats stay until your next renewal, and the lower count takes effect then. You can never drop below two seats, or below the number of people already seated, whichever is higher. **Plans are monthly only.** New subscriptions, personal and team, bill monthly; the annual option was retired in 2026. A team already on an annual cycle keeps it until it chooses otherwise. **A failed payment gets a grace period.** A failed renewal does not cut the team off. The plan enters a grace period in which everything keeps working, and the billing page shows when access ends along with a way to update the card. Fix the card in time and nothing changes. If it is not fixed, at the end of the window the team drops to Free: private projects become view-only and the pooled storage tier falls to 4 GB. Nothing is ever deleted, and updating the card or resuming the plan restores everything. **Invoices.** Every charge is listed on the billing page with its date and amount and a link to download the invoice. There is one invoice for the whole team, not one per member. **Pooled storage.** Team storage is pooled: every team project draws from the same quota, shared across the whole team, while each member's personal projects keep their own separate quota. The team's **Usage** settings show the pool as a whole (1 TB on a paid team, 4 GB on a free one) and a per-project breakdown, so an admin can see which projects use the most space, each with its visibility, canvas count, and size. ![A team's Plan and billing page: the plan card, the seat stepper, payment method, and invoices](/docs/images/team-billing.webp) ## What happens if you downgrade A downgrade removes capability. It never destroys your work and never exposes anything that was private: - **Private projects and drops become view-only.** You can still read, export, or delete them, and you can flip any of them public to resume free editing instantly. Nothing is deleted. - **Storage returns to 2 GB.** Bytes you already have stay put. If that puts you over the 2 GB line, you cannot save new changes until you free space, but everything already there is safe and you can always delete to make room. - **A short handle reverts** at once to the handle you had before you claimed it. The short handle is then **reserved for your account for 30 days** (see [handles and profiles](/docs/platform/handles-and-profiles)): nobody else can claim it, links keep redirecting, and re-subscribing within the window **restores it to you automatically**, with no reclaim step. Claiming a brand-new short handle still needs a paid plan. - **The blue badge disappears.** - **Brilliant AI returns to Free:** the monthly Free allowance, and the 10-a-day cap on your own AI comes back. That is the whole list. A lapse takes away the paid extras and leaves everything else exactly as it was. ## Next - **Why the built-in chat earns the upgrade:** [Why pay for the integrated AI chat](/docs/getting-started/why-pay). - **The private work you are paying for:** [Publish a project](/docs/platform/projects). --- Source: https://brilliant.design/docs/platform/projects.mdx # Publish a project Publishing is the one act that puts a project on [brilliant.design](https://brilliant.design). Until you publish, a project is just files on your disk and nothing about it reaches Brilliant's servers. This page covers how to publish, what public and private mean, and how checkpoints, forks, and history work once a project is online. ## Publishing from the desktop app Publishing lives in **Settings, in the Sharing tab**. You can reach it from the Sharing command, the account avatar menu, or a project tile's menu on the home screen. There is exactly one gesture: tick the **Published** row. Before it goes live, Brilliant shows you a confirmation and a **What gets published** review, so you can see every file that will be uploaded. It also flags anything that looks like a secret (a stray `.env` file, an SSH key) as a heads-up, never a block. Confirm, and your project goes live. The link is copied to your clipboard so you can paste it straight into a message. ## Publishing from the web Signed in on the web, the **New** menu gives you two ways to start a cloud project: - **New project:** starts a fresh project in the cloud. If you belong to a team, this opens a dialog first, where you name the project, choose its visibility, and confirm the owner, which is whichever identity you are acting as (a team when you are acting as it, otherwise yourself). - **Create from Figma:** bring a Figma file in as a new project. Both start a new project in the cloud, public by default. ## Public by default New projects are **public by default**. That is the model, not an accident: > Free is for working in public. Personal is for working in private. A public project is visible to anyone with the link, and going public is always free. Keeping work private is the paid part. ## Visibility: public and private Every published project is either public or private, and only the owner can flip it. - **Public:** anyone with the link can view your latest work. - **Private:** only you and the people you have invited can see it. On a paid plan you can also hand out a read-only [share link](/docs/platform/collaboration) so someone can view it without an invite. Making a project private is a paid feature. Flipping a private project back to public is free and takes effect immediately. See [Plans and billing](/docs/platform/plans-and-billing) for what private work is bundled with. ## Control what visitors see By default, visitors to a published project see your latest work, live, including autosaves as you make them. If you would rather they see only a polished version, switch the audience to **Checkpoints only**. Visitors then see your most recent checkpoint instead of your in-progress canvas, while you keep working freely behind it. "Control what visitors see" is a paid feature. It pairs naturally with checkpoints, described below. ## Checkpoints and version history A **checkpoint** is a named, saved version of a published project. It is the unit of version history on the web. You create checkpoints on purpose. In the desktop app, hit the **bookmark button** in the top island, or run the **Create Checkpoint** command, give it a name (or take the suggested one), and it is minted. Autosave keeps your work safe continuously, but autosave never creates a checkpoint. Checkpoints are the moments you decide are worth marking. Your checkpoints are listed at `brilliant.design/{handle}/{project}/history`. There is no cap on how many you keep and no retention limit; the only ceiling is your storage quota. Restoring an older checkpoint **rolls forward**. Instead of erasing what came after, a restore writes a new checkpoint that matches the one you picked, and your unsaved work is saved first as its own checkpoint. Nothing is ever destroyed, so you can restore, look around, and restore again without losing a thing. ## Forks Anyone signed in can **fork** any project they can see. A fork is a fresh copy under your own handle: it starts clean, with no history carried over, and it records where it came from so the provenance is visible. Forking a public project is free for everyone. Forking a private project (one you were invited to) creates a private fork, so it needs a paid plan. ## Drafts Checkpoints mark the mainline as it moves forward. A **draft** is the other half of version history: a proposed version kept off the mainline until you accept it, the way a pull request keeps a branch out of `main`. A draft starts from a checkpoint, collects its own edits, and changes nothing about what visitors see until it lands. Drafts live on the web, where the owner reviews one and accepts it as a new checkpoint, resolving any conflicts along the way. A project row shows its open-draft count, and [Drafts and review](/docs/platform/drafts-and-review) is the full story: the three-pane viewer, inspecting and copying elements out, and the conflict resolver. ## Clone with Git A published project is a real Git repository with a clone URL of the form `https://api.brilliant.design/git/{handle}/{project}.git`. Public projects clone anonymously (unless you have limited visitors to checkpoints), so you can pull a design into a local workflow the same way you would clone code. ## Unpublishing To take a project offline, untick the **Published** row and confirm by typing the project's name. The moment you do, its links stop working for everyone, including you. Your local files are untouched: unpublishing only removes the cloud copy, and everything on your disk stays exactly as it was. The cloud copy is not purged right away either. It is held for 30 days before it is permanently removed, so an accidental unpublish has a recovery window. ## Stars Star any project you want to keep track of. On the projects list, the two ways into a project are its name and its canvas thumbnails, and both open it. The **Star** button sits at the top right of each row, on its own, so starring a project never opens it. Starring works the way it does on GitHub: click to star, click again to unstar, and each project shows how many people have starred it. It is a free, public signal, so it doubles as a bookmark and a way to say a project is worth a look. Your starred projects live on the **Stars** tab of your profile, and because stars are public, anyone visiting your profile can see them. Starring while signed out is fine: Brilliant asks you to sign in and then stars the project in the same step. ## Next - **Know exactly what uploads and when:** [How sync works](/docs/platform/sync). - **Work on a project with other people:** [Collaborate live](/docs/platform/collaboration). - **Review and accept proposed versions:** [Drafts and review](/docs/platform/drafts-and-review). - **See which visibility features are paid:** [Plans and billing](/docs/platform/plans-and-billing). --- Source: https://brilliant.design/docs/platform/settings-and-account.mdx # Settings and your account Everything about your account on the web lives at [brilliant.design/settings](https://brilliant.design/settings): your profile, how you sign in, the teams you belong to, and your plan. It is one page, a sidebar of sections beside their content, and every section has a real address of its own (`/settings`, `/settings/account`, and so on) so you can link straight to it and the back button works the way you expect. (`settings` is a reserved word, so this page can never collide with someone's profile.) Open it while signed out and Brilliant sends you to sign in first, then drops you back on the exact section you were heading for. ## You, or a team The avatar in the top-right corner is not just a picture: it is the identity you are currently acting as. Click it to switch between yourself and any team you belong to, and the whole page re-keys to match. Acting as yourself, `/settings` shows your personal settings. Acting as a team, the same page shows that team's settings instead. The choice sticks. It survives a reload and stays put until you switch back, so you are never surprised by which account you are editing. Anyone on a team can switch into it, but changing a team's settings is admin-only (more on that below). Acting as yourself is the plain default: nothing about it changed when teams arrived. ## Your personal settings ### Profile This is the same profile you see on your public page, editable here as a set of cards: your photo, your public details (name, handle, bio, website, company, location), and your links, which you can drag to reorder. It is wired to the exact same save-and-validate machinery as the inline editor on your profile page, so the two can never drift apart. Your handle lives here too, including the short-handle upsell if you reach for one on the Free plan. See [Handles and profiles](/docs/platform/handles-and-profiles) for how handles and short handles work. ![The personal Settings page: the section sidebar (Profile, Account and security, Teams, Plan and billing) beside the Profile cards|820x](/docs/images/web-settings-personal.webp) ### Account and security - **Sign in.** Brilliant has no passwords, so this card just shows how you get in: the email that is your account identity, and Google for the same address. - **Connected apps.** If you have connected Figma, it shows up here with a **Disconnect** button. Nothing connected means the card simply is not there, rather than a fake empty list. - **Delete account.** Deleting is deliberately heavy, and honest about what it does. Your projects and drops disappear right away and are permanently deleted after a 30-day window. Forks other people made of your public work stay with them. Your handle is held for 30 days, then released. Stars, follows, and collaborator access are removed, and you are signed out everywhere. The flow walks you through it: an explanation, then typing your handle to confirm (the server re-checks it, so the gate is real), then one more explicit confirm. If you still have an active subscription, or you are the only admin of a team, Brilliant tells you to sort that out first instead of letting the delete go through. ![The Account and security section: the passwordless Sign in card (email and Google) and the red Delete account card|820x](/docs/images/web-settings-account.webp) ### Teams This section appears once you belong to at least one team, and it is where your own relationship to each team lives (the team's own settings are a separate lens, covered below). - **Membership visibility.** Each team has a Public / Private switch that only you control. It is private by default, and team admins cannot flip it for you. Public simply shows that membership on your profile. - **Leaving a team.** Teams where you are a plain member show a **Leave** button. Leaving frees your seat immediately, and the work you made stays with the team. Admins are not listed here, because a team's last admin cannot walk out and strand it. Handing a team over is part of team settings, covered in [Teams](/docs/platform/teams). ### Notifications Per-kind switches for what lands in your [notification bell](/docs/platform/handles-and-profiles#notifications): follows, stars, forks, and publishes can each be muted, and the change applies from that moment on. Invites are always delivered; an invitation is never something Brilliant decides you did not want to see. ### Plan and billing Your plan, your subscription, and your invoices. The full billing story (upgrading, seats, intervals, what a paid plan unlocks) is in [Plans and billing](/docs/platform/plans-and-billing). Your Brilliant AI usage (a percent used and a reset date) and the plan card where you switch rungs live in the Usage section. Raising a tier applies immediately and is prorated; lowering one takes effect at renewal. ## Managing a team Switch the top-right avatar to a team and `/settings` becomes that team's settings, in the same shell, with its own sidebar: **Profile**, **Members**, **Usage**, **Notifications** (the same per-kind mute switches, here governing the team's bell), **Plan and billing**, and a **Danger zone**. ![The team lens of Settings: the sidebar now shows Profile, Members, Usage, Notifications, Plan and billing, and a red Danger zone, beside the team profile cards|820x](/docs/images/web-settings-team.webp) This whole surface is for admins. If you are a member of the team but not an admin, opening it shows an honest card ("Only admins manage team settings") with a button to switch back to your personal account, rather than a blank page or an error. You will see the same card if the team context has gone stale, say you were removed since you last switched in. - **Profile** is the team's public profile: its avatar, name, handle, bio, and links. Renaming the team's handle keeps old links working, they redirect to the new one. - **Members**, **Usage**, **Plan and billing**, and the **Danger zone** (the roster and invites, pooled storage, seats and billing, and transferring or deleting the team) are the deep team-management surfaces. Rather than repeat them here, see [Teams](/docs/platform/teams) for how a team is run and [Plans and billing](/docs/platform/plans-and-billing) for the money side. ## Next - **Set up your handle and profile:** [Handles and profiles](/docs/platform/handles-and-profiles). - **Run a team:** [Teams](/docs/platform/teams). - **The desktop counterpart:** [Settings](/docs/workspace/settings) covers the in-app Settings window (Account, Usage, Your own AI, MCP, shortcuts, and more). --- Source: https://brilliant.design/docs/platform/sync.mdx # How sync works This is the honest version of what leaves your machine and when. The short answer: nothing does until you publish, and after that, the project you are working in stays in step with the cloud automatically. ![Local-first on the left, brilliant.design on the right: publish is one explicit click, then sync runs both ways](/docs/images/diagrams/platform-sync-flow.webp) ## Nothing uploads until you publish Signed out, or signed in but with a project you have not published, no design content reaches Brilliant's servers. Full stop. Signing in does not quietly sync your local canvases. **Publishing is the moment.** It is an explicit act, and it is the point at which a project's contents go to the cloud. Everything below only applies to a project you have chosen to publish. See [Publish a project](/docs/platform/projects) for how that works. ## What syncs Once a project is published, sync covers the design and only the design: - Your canvases (`.bl` files). - Your design systems (`.ds` files). - The images your canvases actually use. - A small preview thumbnail of each canvas (it is what project tiles show). It does **not** upload your Git history, loose scratch files, or other files your canvases do not reference. It is your design, not your whole folder. One thing to know: the **names** of your files and canvases are visible to the service (they have to be, to sync them), even though the design content stays scoped to what you publish. ## When it syncs While a published project is open in your active tab, sync is automatic and goes both ways: - **Outbound:** a moment after you stop editing, your changes go up. This includes changes made outside the app entirely. If an agent, a script, or a Git operation edits your files while the project is open, Brilliant notices and uploads those too. - **Inbound:** edits made to the project elsewhere (a collaborator, a change pushed to the repo) come down to you, roughly every minute or so and immediately when you focus the app. Sync follows the tab you are in. The project in your active tab is the one that syncs; projects in other tabs wait their turn. ## Working offline Lose your connection and nothing stops. Keep designing. Your changes queue up locally, survive a restart, and upload the moment you are back online. Work you did while away lands in the project's history as a single checkpoint named **Changes made while away**, so the gap is marked rather than smeared across the timeline. ## Conflicts are never a silent overwrite If the cloud version of a file moved on while you were editing the same file, Brilliant does not quietly pick a winner and lose the other. It **merges the two, element by element**, so both your edits and the cloud's changes are kept, and where the exact same property was changed on both sides it keeps your value. Before writing the merge it saves the previous cloud version as a checkpoint (**Pre-merge server state**), then tells you once, with a single notification, that the two were combined and nothing was lost. When the two versions cannot be merged cleanly, it falls back to keeping your local version live and saving the server's version as a checkpoint named **Pre-conflict server state** instead, again with one notification. Nothing is thrown away. Whatever did not survive the merge is sitting in a checkpoint you can go back to. Inbound changes also never overwrite a file you have unsaved edits in. ## Sync health: no news is good news You do not have to babysit sync. When everything is flowing, there is nothing to see. If sync runs into trouble, a **red notification dot** appears in the top island, and it stays until sync recovers on its own. As long as there is no red dot, your work is syncing. ## Live sessions make the web view live While a published project is open, its edits stream continuously, element by element. That is what makes the web view genuinely live: someone watching your public project in a browser sees your changes as you make them, not on a delay. More on watching and co-editing in [Collaborate live](/docs/platform/collaboration). ## Next - **Publish, and the rest of this kicks in:** [Publish a project](/docs/platform/projects). - **Design together in real time:** [Collaborate live](/docs/platform/collaboration). --- Source: https://brilliant.design/docs/platform/teams.mdx # Teams A team is a shared identity on brilliant.design that owns work on behalf of a group, so projects, handles, and billing belong to the team rather than to any one person. When someone leaves, the work stays. ## A team is its own identity A team has its own name and its own **handle**, and it lives in the same handle namespace as people: `brilliant.design/acme` resolves to the team when no person has claimed that handle first (users resolve first, teams second). A team has a profile page, owns projects directly, and can star and follow as itself. Every project carries an owner, either a person or a team. A team-owned project counts against the team's storage, follows the team's plan rather than yours, and survives any single member leaving. ## Free and paid teams Teams are free to create, and a free team is a real team, not a trial: - **Free team.** Unlimited public projects, unlimited members, and 4 GB of pooled storage. Free forever. - **Paid team.** Everything a free team has, plus private projects and drops, unlimited editors on private work, and 1 TB of pooled storage. Seats are monthly only, with a two-seat minimum: Personal seat $25, Pro seat $55, Max seat $105, Max 20x seat $205 per month. An admin assigns a tier per member and can mix them freely. Only private work and more Brilliant AI are paid, the same line personal accounts follow: a free team can do everything in the open and pays only to work in private. A paid seat also carries that member's Brilliant AI usage, measured per seat on the team's cycle and never pooled, so one member cannot drain the team. Storage, on the other hand, is **pooled** across the whole team rather than counted per seat. See [Plans and billing](/docs/platform/plans-and-billing) for the full picture. ## Roles: admin and member A team has two roles: - **Admin.** Manages the roster, manages billing, and controls anything the team shows the world (its profile, and starring or following as the team). - **Member.** Works inside the team on its projects, but cannot change billing or the roster, and cannot act as the team in public. Anything public-facing is admin-only by design, so a member can contribute to the team's work without holding the keys to its identity. A team always keeps at least one admin. The last admin cannot be removed or demoted, and Brilliant blocks that before it happens rather than after: if you are the only admin and try to step down or you would leave the team without one, it asks you to promote someone else first. This holds the same way whether the change is you stepping down or another admin demoting you. ## Seats and invites A paid team buys **seats**, and each seat is a person you invite, by email or by `@handle`. An invite holds its seat until it is accepted, and a pending invite expires after **14 days**. You hold the first seat yourself, and the paid seat minimum is two. Free teams have no seat cap: invite as many people as you like. Each seat has a tier the admin sets. A member whose seat is spent can ask for a bigger one from the wall; the request clears when an admin raises the seat. Per-seat prices are admin-only; members see their own tier read-only. ## Receiving an invite When someone invites you to a team, the invite reaches you two ways: the email link, and a row in your [notification bell](/docs/platform/handles-and-profiles#notifications) (it is waiting there even if you sign up after being invited, so a lost email no longer means a lost invite). Either way you land on an invite card that names the team, who invited you, and the role you would hold, with a note that joining takes one of the team's seats and that everything you make in the team is owned by the team. You can **Accept** or **Decline**, and declining is a clean, first-class choice: it frees the seat the invite was holding and quietly closes the invitation. Nobody on the team is notified that you declined. An invite can also already be in a terminal state by the time you open it, and each one gets an honest card rather than a dead end. If you **declined** it, there is no un-decline; wanting back in means an admin sends a fresh invite. If you are **already a member**, there is nothing to do. If it **expired** (a pending invite lapses after 14 days), ask an admin for a new one. An **invalid** link matches no real invite, and a **wrong account** invite was addressed to a different account than the one you are signed in as, so you switch accounts and open it again. ![The team invite card, offering Accept or Decline](/docs/images/team-invite-accept.webp) ## Switching between your identities You can belong to more than one team and still have your personal account. The **acting-identity switcher** lives on your avatar: open it and a checkmark shows which identity you are currently acting as. Switch to a team and new stars and follows are attributed to the team (where your role allows it); switch back to personal and they are yours again. When you are acting as a team, the notification bell shows the team's notifications (its new followers, stars and forks on its work), and the avatar menu's team-settings entry opens **Settings** in the team's lens: one surface that holds the team's **Profile**, **Members**, **Usage**, **Notifications**, **Plan & billing**, and a **Danger zone**. The whole surface is admin-only, so a member who navigates there gets an honest "only admins manage team settings" message with a way back to their personal account. For how Settings works across your personal and team identities, see [Settings and account](/docs/platform/settings-and-account). ## Leaving a team You can leave any team you are a member of. Your seat is freed and the work you made stays with the team. Leaving is done from [Settings and account](/docs/platform/settings-and-account), which lists the teams you can step out of. The one case Brilliant will not let you leave is being the team's only admin: promote someone else first, or transfer the team, so a team is never left without an admin. A removed member falls back to their own plan or Free with its monthly Brilliant AI; teams get no separate allowance beyond the seats. There is no Enterprise tier. ## The danger zone The team's **Danger zone** lives in its Settings, and everything in it is admin-only. **Transfer ownership.** A team has admins rather than a single owner, so handing it over is a guided two-step move. First you pick a member and make them an admin, which gives them full control of the team, its projects, and its billing. Then, optionally, you step down to member yourself. Stepping down is a real change: as a member you no longer manage the team's settings, which is the honest outcome of handing the team over. If everyone is already an admin, the first step is done and only the step-down remains. ![The Transfer ownership dialog: pick a member to make an admin](/docs/images/team-transfer-ownership.webp) **Step down to member.** You can drop your own admin role to member whenever you like, as long as you are not the last admin. **Delete the team.** Deleting is guarded: you confirm by typing the team's `@handle`. The moment you confirm, the team disappears for everyone. Its projects and drops are kept for **30 days** and then permanently deleted, and its handle is held for **30 days** before it is released. Within that 30-day window the team is recoverable; once it passes, nothing comes back. ![A team's Danger zone: transfer ownership and delete the team](/docs/images/team-danger-zone.webp) ## Transfer a project to a team You can hand an individual project to a team from the project's own side, which moves ownership so the project counts against the team's storage and follows the team's plan instead of yours. Only a team you administer can receive a transfer: in the destination picker, teams you admin are selectable, while teams where you are only a member are shown but disabled with the reason spelled out, so you never pick a destination that would be refused. This is the same rule everywhere: establishing team ownership is admin-only. ![The Transfer project dialog handing a project to a team you administer](/docs/images/team-transfer-project.webp) ## Creating a team Create a team from **+ New team** in the account menu, or from **Create a team** on the [pricing page](/pricing). You give it a name and a handle (with live availability as you type), pick whether it works in public only or also in private, and if you pick private you set the seat count and pay through checkout. A short team handle is a paid feature, the same per-entity rule that handles follow for people. ## Next - **What each plan costs:** [Plans and billing](/docs/platform/plans-and-billing). - **Names and profile pages:** [Handles and profiles](/docs/platform/handles-and-profiles). - **Work together on a project:** [Collaborate live](/docs/platform/collaboration). # Section: Design system --- Source: https://brilliant.design/docs/design-system/authoring.mdx # Authoring The DSL grammar in detail. Every form you'll need to author or modify a `Styles/default.ds` file. If you're new to the design system, start with [Overview](/docs/design-system/overview) for the conceptual tour. This page is the syntax reference. ## Comments Both `//` line comments and `/* ... */` block comments work: ``` // This is a line comment. brand: #FF6600 // trailing comment /* This is a block comment. Useful for hiding multiple lines temporarily. */ ``` The hash character `#` is reserved for hex color literals (`#FF6600`), so don't use it for comments. ## Two kinds of declarations The DSL distinguishes **primitives** (1:1 mappings, mode-independent) from **semantics** (1:N mappings, mode-aware). Primitives are the author-layer building blocks; semantics are the agent-facing surface. The split is visible in the syntax, and it's the central thing to know. Five generators do the work: | Generator | Kind | Output | |-----------|------|--------| | `color(seed)` | primitive | 11-step OKLCH ramp: `.50, .100, ..., .950` | | `number(seed, count, ramp)` | primitive | N values from a seed using a ramp | | `number(range(min, max), count, ramp)` | primitive | N values between bounds | | `number([stops...])` | primitive | explicit list, stops named `.1..N` | | `boldness(scale)` | semantic | 9 mode-aware stops: hint, faint, subtle, soft, mid, firm, bold, strong, intense | | `tshirt(scale, min, max)` | semantic | variable t-shirt stops: xs, sm, md, lg, xl, 2xl, ..., NxL | | `looseness(scale)` | semantic | 6 mode-aware stops: none, tight, snug, normal, relaxed, loose | ### Primitive: `color(seed)` A `color(...)` primitive expands one seed into an 11-step OKLCH lightness ramp. Output stops are named `.50, .100, .200, .300, .400, .500, .600, .700, .800, .900, .950`. The seed value IS the `.500` stop. ``` primary: color(#1976D2) // sRGB hex primary: color(oklch(63.7%, 0.237, 25.331)) // OKLCH (L on 0–1 or as %, C, H) ``` Brilliant's seed template uses OKLCH for the 21 Tailwind palettes (because that's Tailwind v4's source of truth) and lets you pick whichever form is convenient for your own palettes. Custom palettes work the same way. Any name, either form: ``` acme_blue: color(oklch(60%, 0.21, 230)) acme_orange: color(#FF6A00) ``` ### Primitive: `number(...)` `number(...)` is polymorphic: it accepts a seed, a range, or an explicit list. **Generated from a seed.** `number(seed, count, ramp)` produces `count` values starting from `seed`, stepping according to `ramp`: ``` spacing.raw: number(4, 32, linear()) // → spacing.raw.1=4, .2=8, ..., .32=128 spacing.raw: number(4, 32, linear(step: 8)) // → .1=4, .2=12, .3=20, ... font.size.raw: number(12, 13, geometric(ratio: 1.2)) // → geometric ramp from 12 ``` Ramps available: - `linear()` (default step equals the seed, so values become `seed * n`) - `linear(step: S)` (each stop adds S) - `geometric()` (default ratio 2) - `geometric(ratio: R)` (each stop multiplies by R) **Generated within a range.** `number(range(min, max), count, ramp)` clamps the generated series: ``` opacity: number(range(0, 1), 21, linear()) // → 21 stops from 0 to 1 ``` **Explicit list.** Pass a literal list and `count` / `ramp` are omitted: ``` weight.raw: number([100, 200, 300, 400, 500, 600, 700, 800, 900]) // → weight.raw.1..9 ``` ### Semantic: `boldness(scale)` Wrap a primitive scale in `boldness(...)` to produce a 9-stop semantic with the **boldness vocabulary**: ``` hint, faint, subtle, soft, mid, firm, bold, strong, intense ``` The same vocabulary applies across every domain. Use it for color tones, font weights, stroke widths, visibility: ``` primary: boldness(color(#0080FF)) // → primary.{hint..intense} font.weight: boldness(number([100, 200, 300, 400, 500, 600, 700, 800, 900])) // → font.weight.{hint..intense} stroke.width: boldness(number([0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 8])) // → stroke.width.{hint..intense} visibility: boldness(number([0.02, 0.05, 0.10, 0.20, 0.40, 0.60, 0.80, 0.95, 1.0])) // → visibility.{hint..intense} ``` The catalog maps the 9 boldness names to whatever stops make sense per domain. For colors, `bold → .700` (matches OKLCH convention). For font weight, `bold → 700` (matches CSS canon). The mapping is positional for explicit lists (`list[0] → hint`, `list[8] → intense`). **Mode behavior.** Every scale generator (`boldness`, `tshirt`, `looseness`) accepts an optional `transforms:` argument declaring per-mode index ops on the role list: `shift(N)`, `mirror`, `outward(N)`. The seed binds these so `theme.dark` mirrors color stops, `density.compact` shifts spacing tighter, `accessibility.large-text` shifts font size up, etc. See [Modes & brands](/docs/design-system/modes-and-brands) for the full vocabulary. ### Semantic: `tshirt(scale, min, max)` `tshirt(...)` produces variable-count semantic stops with the **tshirt vocabulary**: ``` xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl, 7xl, 8xl, 9xl ``` The vocabulary extends as needed; you'll see as many stops as your underlying scale provides. Use it for spacing, radius, font size: ``` spacing: tshirt(number(4, 32, linear())) // → spacing.{xs..6xl} (catalog picks 10 stops) font.size: tshirt(number([12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 80, 96, 128])) // → font.size.{xs..9xl} (positional) ``` **Boundary stops.** `min` and `max` named-arg records add custom stops at either end: ``` radius: tshirt(number([4, 8, 16, 24, 32, 48]), min: { none: 0 }, max: { full: 9999 }) // → radius.{none, xs, sm, md, lg, xl, 2xl, full} ``` **Mode behavior.** `density.compact` shifts the role mappings down a notch toward smaller values (md → sm's old value, etc.). `accessibility.large-text` shifts them up. ### Semantic: `looseness(scale)` `looseness(...)` produces 6 semantic stops with the **looseness vocabulary**: ``` none, tight, snug, normal, relaxed, loose ``` Used for line height and letter spacing: ``` font.lineHeight: looseness(number([1.0, 1.25, 1.375, 1.5, 1.625, 2.0])) font.letterSpacing: looseness(number([-0.05, -0.025, -0.0125, 0, 0.025, 0.1])) ``` **Mode behavior.** `accessibility.large-text` shifts toward looser by one step. ### Bare alias convention For a color seed wrapped in `boldness(...)`, the bare reference resolves to the `mid` stop (the seed value): ``` $primary // → $primary.mid ``` Use it for "the canonical version of this color scale." Number-backed scales (`spacing`, `radius`, `font.size`, `font.weight`, `stroke.width`, and anything else built on `number(...)`) emit **no** bare token, so you must name a stop: ``` $spacing // error: pick a named stop $spacing.md // ✓ ``` Chrome aliases (`$color.surface` etc.) keep their full name; they are their own names, not shortcuts. ### Single-value primitives Some primitives don't have a scale. They're a single value: ``` font.family: Manrope brand: #0080FF // single hex, no ramp font.size: 16 // single number brand.300: #B8B9D6 // a specific stop, no ramp ``` Bareword identifiers (no quotes) are fine when the value is a single word. Quote when there's whitespace or special characters: ``` font.family: "Fira Code" ``` If you want multiple font families, declare them as separate primitives: ``` font.family.serif: "Noto Serif" font.family.mono: "Fira Code" ``` Reference them with the full dotted path (`$font.family.mono`). ### Pinning a single stop If a generator gives you a stop that doesn't quite fit, override it by declaring the stop as its own primitive. Explicit beats generated: ``` primary: boldness(color(#1976D2)) primary.300: #B8B9D6 // hand-picked, replaces what the resolver produced ``` This is a separate declaration, not a nested block. Stop overrides are just primitives with a dotted name. ### Semantic alias declarations Beyond the `boldness/tshirt/looseness` generators, you can declare alias semantics that point at another resolved value. These are the chrome roles: ``` color.surface: neutral.hint color.surface.container: neutral.faint color.text.primary: neutral.bold color.primary: primary.mid color.shadow: neutral.950 // primitive ref → mode-immune ``` The right-hand side can be another semantic (`primary.mid`), a primitive stop (`neutral.950`), or a literal value. Refs to semantics inherit the source's mode-awareness; refs to primitive stops or literals are mode-immune. ### Override semantics Three rules govern how mode branches stack: 1. **Block at a path = full replace.** The block fully replaces the generated semantic's mappings at that path. 2. **Dotted-path mode segment = additive override.** Use a dotted path to override just one branch without touching the rest: ``` color.primary.bold.dark: #00CCFF // override just the bold/dark branch ``` 3. **Block with only `$default` = mode-immune lock.** A block containing only `$default` locks that path across all modes: ``` color.shadow { $default: neutral.950 } // never flips per mode ``` ### Explicit mode block form When you want to author per-mode branches by hand, use the block form: ``` color.primary { $default: primary.mid dark: primary.firm } color.text.primary { $default: neutral.bold dark: neutral.faint density.compact: neutral.firm theme.dark, density.compact: neutral.subtle } ``` `$default` is the fallback when no mode-specific key matches. Mode keys can be a bare axis value (`dark`), a fully-qualified path (`theme.dark`), or a comma-separated combo (`theme.dark, density.compact`). Combo branches apply only when **all** listed modes are active. The generator rejects a mode key whose value isn't one of these axis values; it does not check that a value is paired with the right axis (see `modes { ... }` below). ## Composites Composites combine atoms into a structured value. Same shape as primitives, but the value is a record or list. ### Typography composites Records with named fields. Field values can be literals or references: ``` typography.h1: { fontSize: font.size.3xl, fontWeight: font.weight.bold, lineHeight: 1.2 } typography.body.md: { fontSize: 16, fontWeight: 400, lineHeight: font.lineHeight.normal } typography.code: { fontSize: font.size.sm fontWeight: font.weight.soft lineHeight: font.lineHeight.normal fontFamily: font.family.mono } ``` Apply a typography composite to text via the **Apply Typography Token** palette command. A composite that needs to flip per mode can wrap in a semantic block. Records merge per field, so only the overridden fields change: ``` typography.h1 { $default: { fontSize: font.size.3xl, fontWeight: font.weight.bold, lineHeight: 1.2 } density.compact: { fontSize: font.size.2xl } // only fontSize overrides } ``` ### Shadow composites Lists of `drop(...)` layers. Each layer takes named args: ``` shadow.sm: [ drop(y: 1, blur: 2, color: rgba(0, 0, 0, 0.05)) ] shadow.md: [ drop(y: 2, blur: 4, spread: -1, color: rgba(0, 0, 0, 0.06)), drop(y: 4, blur: 6, spread: -1, color: rgba(0, 0, 0, 0.10)), ] ``` `x`, `y`, `blur`, `spread`, `color` are the layer args. `color` accepts hex literals or `rgba(r, g, b, a)`. Apply via the **Apply Shadow Token** palette command. Replaces existing drop shadows on the element; preserves inner shadows, glows, and blurs. ## References Anywhere a value is expected, you can pass a token reference instead of a literal. A reference is a dotted path that the resolver follows to a concrete value: ``` color.text.primary: neutral.bold typography.h1: { fontSize: font.size.3xl, lineHeight: font.lineHeight.tight } shadow.subtle: shadow.sm ``` Bareword identifiers in value position are interpreted as references when the path matches a known token, or as plain string values for string-typed primitives like `font.family`. Brilliant figures it out from context. References can chain: a semantic can point at another semantic. The resolver walks the chain to a final concrete value. ## Mode axes Declare available axes once at the top of the file: ``` modes { theme: [light, dark] density: [comfortable, compact] accessibility: [standard, high-contrast, large-text] } ``` Each axis has a list of values. The first value is the default. Semantic blocks below carry per-mode branches keyed by these axis values. The generator rejects a mode key whose value isn't one of these axis values; it does not check that a value is paired with the right axis. ## Active brand and active mode `active { ... }` sets the default brand and active mode set for descendants of this folder: ``` active { brand: acme_corporate modes: { theme: dark, density: compact } } ``` The chat picker in the AI panel can override per session. Per-element and per-canvas settings override per folder. ## Removing tokens `unset { ... }` drops entries from the merged file. Top-level only. Paths that don't match anything emit a warning instead of failing, so it's safe to leave in place. ``` ds_file("corporate-blue") unset { color.primary // drop the entire semantic primary.300 // drop a specific stop override color.surface.dark // drop just the dark branch of a semantic *.dark // drop the dark branch across ALL semantics } ``` The wildcard form `*.` strips that branch from every mode-keyed semantic in one go. Built-in axes (`theme`, `density`, `accessibility`) and any custom axes you declare are recognized automatically. Useful for "switch this brand to light-only" sweeps. Nested `unset` inside another block isn't supported. ## Picker visibility ``` hide: stone, zinc, gray, slate // remove from inspector pickers (still resolvable) pin: primary, color.text.*, color.surface // float favorites to the top of relevant pickers ``` `hide:` and `pin:` accept comma-separated paths. Globs (`color.text.*`) work for path prefixes. ## Cascade flags Two flags control how this file participates in the parent-folder cascade. Both follow `.editorconfig` semantics. ``` root: true // stop the parent-folder walk at this file inherits: none // (in a brand file) opt out of merging with sibling default.ds ``` `root: true` truncates the cascade. Files in ancestor folders are not merged. Useful in monorepos where one sub-project shouldn't inherit from a parent design system. Default is `false`. `inherits: none` makes a brand file standalone instead of layering on top of its sibling `default.ds`. Default is `default` (merge enabled). See [Modes & Brands](/docs/design-system/modes-and-brands) for when this matters. ## Anti-patterns A few things that look like they should work but don't: - **Decimal stop names.** `radius.0.5: 4` won't lex as a path because `0.5` reads as a decimal literal. Use a name (`radius.hairline: 4`) or quote it as a string key inside a `number([...])` list. - **Variables in literals.** There's no string templating; you can't write `red.500: "#${something}"`. References work in value position only. - **Computed scales.** No `spacing.4 * 2` arithmetic. If you need a value, write it. - **Mode keys on primitives.** Primitives are mode-independent by design. To pick between two values per mode, declare two primitives and route via a semantic. - **Imports / extends across files.** A `.ds` file can't `import` another file. The cascade across folders gives you the equivalent declaratively. Anything else, the parser will surface a clear error pointing at the line and column.
Tip: When you're not sure if something is valid, write it and save. Brilliant's resolver runs on every save and surfaces parse + resolve warnings in the log. The grammar is small enough that most things you'd want to write just work.
--- Source: https://brilliant.design/docs/design-system/modes-and-brands.mdx # Modes & Brands Two ways to make your design system flex: **modes** (light/dark, custom axes like density) and **brands** (different palette + alias overlays for different audiences). Both are first-class in the DSL. ## Modes A mode is a named variant. The seed template declares three axes: ``` modes { theme: [light, dark] density: [comfortable, compact] accessibility: [standard, high-contrast, large-text] } ``` Each axis has a list of values. The first value is the default; the others are alternatives you can switch into. The generator rejects a mode key whose value isn't one of these axis values; it does not check that a value is paired with the right axis. ### Mode behavior is baked into each generator A bare semantic generator already carries sensible mode behavior. Write one line, get a mode-aware scale: ``` primary: boldness(color(#0080FF)) // dark mirrors; high-contrast pushes outward spacing: tshirt(number([4, 8, 12, 16, 24, 32, 48, 64])) // compact -1, large-text +1 font.lineHeight: looseness(number([1.0, 1.25, 1.5, 2.0])) // large-text widens ``` Per-generator defaults: | generator | default transforms | |---|---| | `boldness(color(...))` | `theme.dark: mirror`, `accessibility.high-contrast: outward(1)` | | `boldness(number(...))` | `accessibility.high-contrast: shift(+1)` | | `tshirt(number(...))` | `density.compact: shift(-1)`, `accessibility.large-text: shift(+1)` | | `looseness(number(...))` | `accessibility.large-text: shift(+1)` | Three built-in **ops**, all work on any generator (they only see indices, not value types): | op | semantics | |---|---| | `shift(N)` | Offset the role's stop index by N. Sum-composes when multiple modes are active. | | `mirror` | Reflect around the middle of the token's materialized stops (`hint ↔ intense` on the nine boldness roles, `xs ↔ 6xl` on the shipped 10-stop spacing scale). | | `outward(N)` | Push N steps away from the middle, toward whichever terminal stop is closer. | ### Override per-token when you want different behavior `transforms:` on a generator call **replaces** the defaults entirely (no merging: we don't want "which fired?" debugging). Two shapes: ``` // Custom transform list: replaces defaults. font.size: tshirt(number([12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 80, 96, 128]), transforms: { accessibility.large-text: shift(+1) }) // skip compact (no text shrink) // Explicit opt-out: no mode behavior at all. radius: tshirt(number([2, 4, 6, 8, 12, 16, 20, 24]), transforms: none) // corner radius stays stable across modes ``` Mode keys must be axis-qualified (`density.compact`, never bare `compact`) so values reused across custom axes don't collide. ### Explicit per-mode branches When the catalog defaults don't fit, override per mode with an explicit block. Per-mode branches live inline with the role: ``` color.text.primary { $default: neutral.bold dark: neutral.faint } color.surface { $default: neutral.faint, dark: neutral.950 } ``` `$default` is the fallback when no mode-specific key matches. Mode keys can be the bare axis value (`dark`), a fully-qualified path (`theme.dark`), or a comma-separated combo (`theme.dark, density.compact`). A few notes: - **Primitives are mode-independent.** `$primary.500` is the same hex in every mode. If you want a seed to differ per mode, declare two primitives and route between them with a semantic. - **Records merge per-field.** A semantic-wrapped composite like `typography.h1 { $default: { ... }, density.compact: { fontSize: font.size.2xl } }` only changes `fontSize`; the other fields inherit from `$default`. - **Lists replace.** A mode-specific shadow list fully replaces the base. ### Override semantics Three shapes of override apply when you want to tweak the auto-generated semantics: 1. **Block at a path = full replace.** A block fully replaces the generated semantic's mappings at that path. 2. **Dotted-path mode segment = additive override.** Use a dotted path to override just one branch without touching the rest: ``` color.primary.bold.dark: #00CCFF // override just the bold/dark branch primary.bold.dark: oklch(45%, 0.22, 245) ``` 3. **Block with only `$default` = mode-immune lock.** A block containing only `$default` locks that path across all modes: ``` color.shadow { $default: neutral.950 } // never flips per mode ``` ### Combining modes A single branch can apply when multiple modes are simultaneously active. Comma-list the mode keys: ``` color.text.primary { $default: neutral.bold dark: neutral.faint density.compact: neutral.firm theme.dark, density.compact: neutral.subtle } ``` The combo branch applies only when **all** listed modes are active. Order doesn't matter; the resolver normalizes. ### Switching modes The right toolbar's **Design system** section (top) has a dropdown per mode axis. Hover to preview; click to commit. With nothing selected, switching sets the canvas-level override (`CanvasState.designSystem`), which persists in the `.bl` file and applies only to that canvas. With a selection, it sets `Element.designSystem` on each selected element instead. There's no default keyboard shortcut for mode switching, but you can bind one through the keybindings file if you do it often. ### Custom axes `modes { ... }` accepts any axes you want: ``` modes { theme: [light, dark] contrast: [normal, high] motion: [auto, reduced] } ``` Then key branches on any of them: ``` color.text.primary { $default: neutral.bold contrast.high: neutral.950 } color.outline { $default: neutral.soft contrast.high: neutral.bold } ``` Each axis is independent. You can be in `theme.dark + contrast.high + motion.reduced` simultaneously, with each axis contributing its overrides through whatever branches match. ## Brands A brand is a sibling `.ds` file living next to `default.ds` in the same `Styles/` folder: ``` my-project/ └── Styles/ ├── default.ds ← shared base ├── acme.ds ← brand A overlay (sparse) └── globex.ds ← brand B overlay ``` Brand files are sparse. They declare only the deltas vs. the base: typically a brand seed, maybe a font family, and a few semantic overrides where the brand wants different mode behavior. Everything else cascades. A typical brand file: ``` // Styles/acme.ds primary: boldness(color(#FF6A00)) font.family: Inter color.primary { $default: primary.firm, dark: primary.subtle } // override a single alias ``` That's it. Tailwind palettes, typography composites, the other chrome aliases, all inherit from `default.ds`. ### Activating a brand Three ways to make a brand the active one: 1. **In `default.ds` for the folder.** Declare an `active` block: ``` active { brand: acme } ``` This makes Acme the default brand for every canvas in this folder and below. 2. **Per-element through the inspector.** The right toolbar has a brand picker for each element's design system reference. 3. **Per-canvas.** With nothing selected, the same **Design system** section in the right toolbar sets the canvas-level override (`CanvasState.designSystem`), which persists in the `.bl` file and applies only to that canvas. The per-element setting wins, then per-canvas, then folder. Standard cascade. ### Authoring a brand inline from a blueprint The `ds_file("name")` directive in a blueprint creates or updates a brand file alongside any element rows in the same block: ``` ds_file("corporate-blue") primary: boldness(color(#1976D2)) neutral: boldness(color(#5F6368)) font.family: Roboto color.primary { $default: primary.bold, dark: primary.subtle } color.surface { $default: neutral.faint, dark: neutral.950 } ``` The directive runs before any element rows in the block, so subsequent rows can stamp `ds(corporate-blue)` to use the freshly-written brand. Multiple `ds_file(...)` blocks for the same name in one call concatenate. Output lands at `/Styles/corporate-blue.ds`; sibling canvases pick it up automatically. ### Removing branches in a brand A brand can strip branches from the merged result with `unset { ... }`. The wildcard form is useful for "this brand is light-only": ``` ds_file("acme-light-only") primary: boldness(color(#FF6A00)) unset { *.dark // drop every dark branch from every semantic } ``` You can also unset a single branch (`color.surface.dark`) or a whole semantic (`color.primary`). Idempotent: paths that don't match anything emit a warning instead of failing. ### Brand `.gen.yaml` Each brand gets its own resolved artifact: `Styles/.gen/.gen.yaml`. The generated file includes the merged base and brand state. Every alias, primitive, and composite from `default.ds` shows up, with the brand's deltas applied on top. External tools that consume the brand's `.gen.yaml` see the full effective design system. ### Standalone brands If you want a brand that doesn't inherit from `default.ds` (say, a completely different design system that happens to live in the same project), opt out of base inheritance: ``` // Styles/standalone.ds inherits: none primary: boldness(color(#000000)) neutral: boldness(color(#888888)) // ... full standalone declarations ``` `inherits: none` declares that this brand stands alone instead of layering on top of base. Default is `default` (inherit on). The flag is honored when the brand's `.gen.yaml` artifact is written, in the `ds_file` pre-write gate, and at paint time: the live render cascade truncates the merge at the isolated brand, so base and parent-brand layers are shed before generation. External tools, the generated file, and the canvas all see the same isolated system. ## Cascade across folders Brilliant's design system cascades through the folder tree, like `.editorconfig`. If a sub-folder has its own `Styles/default.ds`, the resolver walks parent folders root → leaf, with later layers winning per-field. ``` my-project/ ├── Styles/ │ └── default.ds ← project-wide design system └── Marketing/ ├── Styles/ │ └── default.ds ← Marketing-specific overrides └── Hero.bl ← uses project-wide + Marketing overrides ``` For canvases under `Marketing/`, the resolver merges `my-project/Styles/default.ds` then `Marketing/Styles/default.ds` (sub-folder wins). ### Stopping the cascade Truncate the parent walk with a `root: true` declaration in any `.ds` file: ``` // Marketing/Styles/default.ds root: true primary: boldness(color(#FF6A00)) // ... fully self-contained design system ``` Files in ancestor folders are not merged. Useful when a sub-project should be a self-contained design system rather than an overlay. This mirrors `.editorconfig`'s `root = true` behavior exactly. ## Putting it together A reasonable multi-brand, multi-mode setup might look like: ``` my-project/ ├── Styles/ │ ├── default.ds // base; semantics + chrome aliases; active.brand=acme │ ├── acme.ds // Acme brand deltas │ └── globex.ds // Globex brand deltas └── Internal/ ├── Styles/ │ ├── default.ds // root: true; standalone internal-tools system │ └── alpha.ds // an internal alpha-build brand └── Dashboard.bl ``` Switching `theme` to `dark` walks every semantic's branches and picks the dark variant where one exists (and the boldness/tshirt/looseness generators do their auto-flip). Setting `active.brand` to `globex` applies the Globex overlay across the whole design (or per element). Canvases under `Internal/` use a separate, isolated design system. Here is what that looks like on one token-bound card. It opens in dark mode; switch the theme back to light, change the brand, or tighten the density, and every bound property re-resolves at once while the layout holds still:
Tip: If you're not sure which design system applies to an element, the right toolbar's Design system section shows the active brand and modes. Hover a dropdown to see where each value is inherited from (e.g. "Theme · Inherited from canvas", "Inherited from folder default").
--- Source: https://brilliant.design/docs/design-system/overview.mdx # Design System Overview Brilliant has a built-in design token system. Tokens turn the values you use over and over (your brand color, your default corner radius, your body font size) into named decisions you can change in one place and watch propagate. This page is the gentle tour. The next pages cover authoring details, every token type, and modes/brands. ## Why use tokens Two reasons it's worth caring about, and they show up immediately when you start using them. **Change once, update everywhere.** Suppose your brand color is `#0080FF` and you've used it on twenty buttons across thirty canvases. With raw hex values, swapping it to a new color means twenty edits. With a token, you call your color `primary.mid` once in the design system file and reference `primary.mid` on every button. Update the brand seed in one line, all twenty buttons follow. **Semantic naming.** A button styled with `color.text.error` says what it's for, not what color it happens to be. When you later swap "error" red to "error" orange, the meaning stays right. The element still represents an error state, the color just changed. Brilliant handles the hard parts: generating an 11-step OKLCH color ramp from one seed, light/dark theming, brand variants, cascade across folders. You write a few lines, you get a full design system. ## See it in motion Here's the payoff, live. One card, one design system, three controls: brand, theme, and density. Flip any of them and every value the system drives moves at once. That is what tokens buy you: change the system in one place, the whole design follows. The rest of this page is how that system is built. Those three controls are quietly swapping the pieces below. ## Where it lives Each project has a `Styles/` folder at the root with two files inside: ``` my-project/ ├── Styles/ │ ├── default.ds ← you edit this │ └── .gen/ │ └── default.gen.yaml ← auto-generated, don't edit └── Canvas.bl ``` `Styles/default.ds` is the file you edit. It contains your design system: which colors, which spacings, which typography, which shadows. Brilliant writes a fully-populated version when you open a fresh project, so you can poke around immediately. `Styles/.gen/default.gen.yaml` is the resolved output. Brilliant regenerates it every time you save the source, expanding everything to concrete values. External tools (Style Dictionary, Tokens Studio plugins, build scripts) read it. Don't edit it: your changes get overwritten on the next save. It's added to `.gitignore` automatically. ## Primitives vs semantics The central mental model: **primitives are 1:1 mappings (one fixed value), semantics are 1:N mappings (mode-aware, one value per mode)**. - **Primitives** are the author-layer building blocks. They produce raw stops like `primary.50..950`, `spacing.1..32`, `font.weight.1..9`. Same value in every mode. - **Semantics** are the agent surface. They produce mode-aware role names like `primary.mid`, `spacing.md`, `font.weight.bold`. The same name resolves to different values in light vs dark, comfortable vs compact, etc. When you reference a token in a blueprint or property, you almost always use the semantic name (`$primary.mid`, `$font.size.lg`). The primitive stops are still resolvable if you need a specific value pinned across all modes. ![A primitive token, primary.500, rendered on a light and a dark panel with the same #0080FF value in both. Next to it a semantic token, color.text.primary, resolves to #454545 in light mode and #C6C6C6 in dark mode](/docs/images/diagrams/ds-primitives-vs-semantics.webp) ## Five generators The DSL has five built-in generators. Two produce primitives; three produce semantics. **Primitive generators:** ``` color(seed) // 11-step OKLCH lightness ramp → .50, .100, ..., .950 number(seed, count, ramp) // generate N values from a seed using a ramp number(range(min, max), count, ramp) // generate N values bounded by min/max number([stops...]) // explicit list of stops → .1, .2, ..., .N ``` **Semantic generators:** ``` boldness(scale) // 9 mode-aware stops: // hint, faint, subtle, soft, mid, firm, bold, strong, intense tshirt(scale, [min], [max]) // variable t-shirt stops: // xs, sm, md, lg, xl, 2xl, 3xl, ..., NxL // plus optional named boundary stops looseness(scale) // 6 mode-aware stops: // none, tight, snug, normal, relaxed, loose ``` The semantic generators wrap a primitive scale and produce role-keyed outputs that flip per mode automatically. ## Two kinds of declarations (plus composites) Open `default.ds` and you'll see two main kinds of statements, primitives and semantics, plus a third structured form for typography and shadows. ### 1. Primitive seeds A primitive declares a fixed value, or a fixed scale of values. The same primitive resolves to the same thing in every mode. **Single value.** One line, one value: ``` font.family: Manrope brand: #0080FF brand.300: #B8B9D6 ``` **Generated ramp.** Wrap a seed in `color(...)` or `number(...)` to expand it: ``` red: color(oklch(63.7%, 0.237, 25.331)) // → red.50..950 (OKLCH ramp) spacing: number(4, 32, linear()) // → spacing.1..32 (linear scale) weight: number([100, 200, 300, 400, 500, 600, 700, 800, 900]) // → weight.1..9 (explicit list) ``` `$primary.500` is the same hex in light and dark; `$neutral.50` is always near-white. Tailwind-style. ### 2. Semantics: mode-aware resolvers A semantic wraps a primitive scale in `boldness(...)`, `tshirt(...)`, or `looseness(...)`. The output is role-named (mid, lg, normal, etc.) and flips per mode: ``` primary: boldness(color(#0080FF)) // → primary.{hint..intense} spacing: tshirt(number(4, 32, linear())) // → spacing.{xs..6xl} font.weight: boldness(number([100, 200, 300, 400, 500, 600, 700, 800, 900])) // → font.weight.{hint..intense} font.lineHeight: looseness(number([1.0, 1.25, 1.375, 1.5, 1.625, 2.0])) // → font.lineHeight.{none..loose} ``` Mode behavior is carried by default: each generator applies the catalog's default transforms unless you override them with a `transforms: { ... }` argument or opt out with `transforms: none`. Those defaults are `theme.dark: mirror` on color seeds (so `primary.hint` reads near-white in light, near-black in dark), `density.compact: shift(-1)` and `accessibility.large-text: shift(+1)` on the t-shirt scales, and so on. They live in the catalog, not the seed file. See [Modes & brands](/docs/design-system/modes-and-brands) for the full op vocabulary (`shift(N)`, `mirror`, `outward(N)`). Semantics can also be **aliases** that just point at another resolved value. These are the chrome roles (surface, text, borders): ``` color.primary: primary.mid color.surface: neutral.hint color.text.primary: neutral.bold color.shadow: neutral.950 // primitive stop = mode-immune ``` When a role doesn't need to flip per mode, the bare alias is enough. Reach for an explicit block when you want to override per mode: ``` color.text.primary { $default: neutral.bold accessibility.high-contrast: neutral.950 } ``` ### 3. Composites: structured tokens Composites bundle multiple fields into one named unit. The two built-in kinds are typography and shadow. **Typography composite.** A record of font properties: ``` typography.h1: { fontSize: font.size.3xl, fontWeight: font.weight.bold, lineHeight: 1.2 } ``` The Apply Typography Token command sets all the fields in one go. Edit the composite and every text using it follows. **Shadow composite.** A list of layers: ``` shadow.md: [ drop(y: 2, blur: 4, spread: -1, color: rgba(0, 0, 0, 0.06)), drop(y: 4, blur: 6, spread: -1, color: rgba(0, 0, 0, 0.10)), ] ``` The `drop(...)` function describes a single drop-shadow layer. Multi-layer shadows (the kind that look believable) are just a list. Apply via the Apply Shadow Token command. ## Vocabularies Three uniform vocabularies the semantic generators produce. Same words used across every domain they apply to. | Vocabulary | Stops | Used by | |------------|-------|---------| | **boldness** (9) | `hint, faint, subtle, soft, mid, firm, bold, strong, intense` | Color tones, font weight, stroke width, visibility | | **tshirt** (variable) | `xs, sm, md, lg, xl, 2xl, 3xl, ..., NxL` | Spacing, radius, font size | | **looseness** (6) | `none, tight, snug, normal, relaxed, loose` | Line height, letter spacing | For color scales, `mid` is the bare alias: `$primary` resolves to `$primary.mid` (the step-500 tone). This is the canonical "the default version of this scale" reference. Only color seeds and `font.family` get a bare form, though. Number-backed scales have no bare token, so always name a stop: write `$spacing.md`, never a standalone `$spacing`. For the t-shirt-backed ones (spacing, radius, font size) the center role is `md`, not `mid`; the boldness- and looseness-backed ones (`font.weight`, `stroke.width`, `font.lineHeight`, and friends) keep their own centers, `mid` and `normal`. ## Editing the file The fastest way to open the design system is the Open Design System File command in the command palette. It opens `Styles/default.ds` in Brilliant's built-in code editor with syntax highlighting. You can also edit it externally, in VS Code or any other editor. Brilliant watches the file for changes and regenerates the resolved output as soon as you save. Edits show up on the canvas in real time. For most day-to-day changes (picking a different brand color, tweaking spacing) you don't need to open the file at all. The right toolbar's color picker, spacing inputs, and so on let you bind properties to tokens with a click. ## What's next - [Authoring](/docs/design-system/authoring): the full grammar reference. Every generator and form in detail with examples. - [Tokens](/docs/design-system/tokens): every token type, the built-in defaults, and what to use which for. - [Modes & Brands](/docs/design-system/modes-and-brands): light/dark theming, custom mode axes, and managing multiple brand variants.
Tip: If you've removed everything from your default.ds and want the built-in defaults back, run the Reset Design System command: it rewrites Styles/default.ds with Brilliant's seed template, the same content a fresh project starts with. It's undoable with , so you can back out if you changed your mind.
--- Source: https://brilliant.design/docs/design-system/tokens.mdx # Tokens A reference for every token type in Brilliant's design system: what they look like, how they're generated, and when to use which. The seed template ships every section below populated; this page tells you what's in it. Every role and scale on this page resolves live on one card. Flip the brand, theme, or density and the whole system moves together, then read on for what each token type is doing underneath: ## Color tokens Color tokens come from seeds. A seed is a single hex or OKLCH literal that, wrapped in `color(...)`, the resolver expands into an 11-step ramp via the OKLCH color space: ``` red: color(oklch(63.7%, 0.237, 25.331)) ``` Generates the primitive stops: `red.50, red.100, red.200, red.300, red.400, red.500, red.600, red.700, red.800, red.900, red.950`. The seed value IS the `.500` stop. Here is the default `primary` seed (`#0080FF`) expanded into its actual generated ramp: ![The primary seed color expanded into the 11 primitive stops, from a near-white .50 to a near-black .950, with the seed marked at .500](/docs/images/diagrams/ds-oklch-ramp.webp) Without `color(...)`, the assignment is a single-value primitive (no ramp): `red: #0080FF` gives you `$red` only. Primitive palette stops are **fixed values, not mode-aware tones**. `$neutral.50` is always near-white in every mode; `$neutral.950` is always near-black. If you want a color that flips with mode, wrap the primitive in `boldness(...)` to get the semantic, mode-aware role names. ### Lightness scale | Stop | Approx lightness | Typical use | |------|------------------|-------------| | `.50` | 97% | Subtle backgrounds | | `.100` | 94% | Light backgrounds | | `.200` | 87% | Hover states | | `.300` | 80% | Borders | | `.400` | 71% | Muted elements | | `.500` | 62% | **Primary** (the seed) | | `.600` | 50% | Hover on primary | | `.700` | 42% | Active states | | `.800` | 36% | Dark accents | | `.900` | 29% | Very dark | | `.950` | 23% | Darkest accents | Lower numbers = lighter. Higher numbers = darker. ### Semantic role names (boldness) Wrap a `color(...)` primitive in `boldness(...)` to expose mode-aware role names. The 9 boldness stops map to OKLCH lightness stops: | Role | Maps to | In light mode | In dark mode | |------|---------|---------------|--------------| | `hint` | `.50` | very light | very dark | | `faint` | `.100` | light | dark | | `subtle` | `.200` | hover light | hover dark | | `soft` | `.300` | border light | border dark | | `mid` | `.500` | the seed | the seed | | `firm` | `.600` | hover dark | hover light | | `bold` | `.700` | active dark | active light | | `strong` | `.800` | dark accent | light accent | | `intense` | `.900` | very dark | very light | `boldness(color(...))` carries a `theme.dark: mirror` transform by default, reflecting low↔high (`hint ↔ intense`, `faint ↔ strong`, etc.) so the same role name reads correctly across themes (it also gets `accessibility.high-contrast: outward(1)` by default). You don't write these in the seed; they're the generator's defaults. Override them with an explicit `transforms: { ... }` arg, or opt out with `transforms: none`. Here are the nine roles of the default `primary` seed, resolved by the engine in each theme: ![The nine boldness roles of the primary blue ramp shown twice, one row resolved in light mode and one in dark mode. The ramp mirrors in dark mode: hint is palest in light and darkest in dark, while mid stays the same #0080FF in both](/docs/images/diagrams/ds-boldness-mirror.webp) The `.400` and `.950` primitive stops carry no role name at all: the boldness mirror reflects over the nine role stops only, so in dark mode `hint` lands on `.900` and `firm` on `.300`, never on `.400` or `.950`. Those two stops stay reachable only as raw primitive references. ### What ships in the seed template | Category | Names | |----------|-------| | **Brand slots** | `primary`, `secondary`, `tertiary`, `quaternary` (defaults `#0080FF` / `#FF3377` / `#FF9900` / `#FFDD00`, the Brilliant logo palette). Hue-agnostic role slots, not named for what they are. | | **Neutral** | `neutral` (default `oklch(55.6%, 0, 0)`) | | **Tailwind v4 palettes (21)** | `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `slate`, `gray`, `zinc`, `stone` | Each Tailwind palette is declared as an OKLCH literal matching Tailwind v4's source-of-truth, then wrapped in `boldness(color(...))`: ``` red: boldness(color(oklch(63.7%, 0.237, 25.331))) ``` This gives you both the primitive ramp (`red.50..950`) and the boldness roles (`red.hint..intense`). ### Chrome aliases The seed template ships with a set of chrome aliases under `color.*`. These are semantic role names that point at the underlying palettes, and they're the names that show up in the inspector picker: | Alias | Default reference | Use | |-------|-------------------|-----| | `color.surface` | `neutral.hint` | Page backgrounds | | `color.surface.container` | `neutral.faint` | Card / container backgrounds | | `color.surface.container.high` | `neutral.subtle` | Elevated surfaces | | `color.surface.hover` | `neutral.faint` | Hovered surface | | `color.surface.pressed` | `neutral.subtle` | Pressed surface | | `color.surface.selected` | `primary.hint` | Selected surface | | `color.on-surface.selected` | `primary.bold` | Content on a selected surface | | `color.on-surface` | `neutral.strong` | Text on surfaces | | `color.outline` | `neutral.soft` | Primary borders | | `color.outline.variant` | `neutral.subtle` | Subtle borders | | `color.text.primary` | `neutral.bold` | Body text | | `color.text.secondary` | `neutral.firm` | Secondary text | | `color.text.disabled` | `neutral.soft` | Disabled text | | `color.text.display` | `primary.firm` | Hero / title color | | `color.text.display.alt` | `secondary.firm` | Hero callout variant | | `color.primary` / `on-primary` / `primary.container` | `primary.mid` / `neutral.50` / `primary.hint` | Primary role trio | | `color.secondary` / `on-secondary` / `secondary.container` | `secondary.mid` / `neutral.50` / `secondary.hint` | Secondary role trio | | `color.tertiary` / `on-tertiary` / `tertiary.container` | `tertiary.mid` / `neutral.50` / `tertiary.hint` | Tertiary role trio | | `color.quaternary` / `on-quaternary` / `quaternary.container` | `quaternary.mid` / `neutral.900` / `quaternary.hint` | Quaternary role trio (dark text: yellow is too bright for white) | | `color.success` / `on-success` / `success.container` | `green.mid` / `neutral.50` / `green.hint` | Success role trio | | `color.error` / `on-error` / `error.container` | `red.mid` / `neutral.50` / `red.hint` | Error role trio | | `color.warning` / `on-warning` / `warning.container` | `orange.mid` / `neutral.900` / `orange.hint` | Warning role trio | | `color.info` / `on-info` / `info.container` | `blue.mid` / `neutral.50` / `blue.hint` | Info role trio | | `color.shadow` | `neutral.950` | Default shadow cast (primitive, mode-immune) | | `color.glow` | `neutral.50` | Default outer-glow color (primitive, mode-immune) | Each alias is a single line. Refs to a `boldness(color(...))` semantic inherit that semantic's mode-awareness; refs to a primitive stop are mode-immune. `color.shadow: neutral.950` always resolves to a near-black hex, in every mode, because `neutral.950` is a fixed primitive stop. Compare to `color.text.primary: neutral.bold`, which mirrors to a near-white in dark mode because `neutral.bold` carries the `theme.dark: mirror` transform from its `boldness(color(...))` seed. ### Custom palettes Add your own palette by declaring a new color seed with any name: ``` acme_blue: boldness(color(oklch(60%, 0.21, 230))) acme_orange: boldness(color(#FF6A00)) ``` Immediately produces `acme_blue.50..950` (primitive stops) and `acme_blue.hint..intense` (mode-aware roles). References like `color.cta: acme_blue.bold` work normally. ## Spacing tokens The `spacing` declaration is an explicit list wrapped in `tshirt(...)` for semantic role names, with a `none: 0` boundary stop: ``` spacing: tshirt(number([4, 8, 12, 16, 24, 32, 48, 64, 96, 128]), min: { none: 0 }) ``` This produces both `$spacing.1..32` (positions 1 through 10 are your explicit list values; 11 through 32 continue as base multiples) and `$spacing.xs..6xl` (t-shirt role names mapped to list positions, `list[i] → role[i]`), plus `$spacing.none` (0). Like every number-backed scale (spacing, radius, font.size, font.weight, font.lineHeight, font.letterSpacing, stroke.width, visibility), `spacing` emits **no bare token**: always name a stop (`$spacing.md`, never `$spacing`). Only color seeds and `font.family` get a bare form. | T-shirt stop | Default value | |--------------|---------------| | `spacing.none` | 0 | | `spacing.xs` | 4 | | `spacing.sm` | 8 | | `spacing.md` | 12 | | `spacing.lg` | 16 | | `spacing.xl` | 24 | | `spacing.2xl` | 32 | | `spacing.3xl` | 48 | | `spacing.4xl` | 64 | | `spacing.5xl` | 96 | | `spacing.6xl` | 128 | The seed template also ships negative overlap stops for pulling auto-layout children together (avatar stacks, badges biting into a card corner): ``` spacing.overlap.xs: -2 spacing.overlap.sm: -4 spacing.overlap.md: -8 spacing.overlap.lg: -16 spacing.overlap.xl: -24 spacing.overlap.2xl: -32 ``` Edit the list to rescale everything: ``` spacing: tshirt(number([8, 16, 24, ...])) // each stop scaled up ``` `density.compact` shifts the t-shirt mapping down a notch automatically (md → sm's old value, etc.). `accessibility.large-text` shifts up. (These are the `tshirt(...)` generator defaults.) ## Radius tokens T-shirt scale with boundary stops. Radius opts out of mode transforms (`transforms: none`) so corners don't change with density or accessibility modes: ``` radius: tshirt(number([2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96]), min: { none: 0 }, max: { full: 9999 }, transforms: none) ``` | Token | Default value | Use | |-------|---------------|-----| | `radius.none` | 0 | Sharp corners | | `radius.xs` | 2 | Subtle rounding | | `radius.sm` | 4 | Small (chips, badges) | | `radius.md` | 6 | Default (cards, buttons) | | `radius.lg` | 8 | Prominent rounding | | `radius.xl` | 12 | Larger rounding | | `radius.2xl` | 16 | Cards, panels | | `radius.3xl` | 20 | Extra-large | | `radius.4xl` | 24 | Hero containers | | `radius.5xl` | 32 | Big blobs | | `radius.6xl` | 40 | | | `radius.7xl` | 48 | | | `radius.8xl` | 64 | | | `radius.9xl` | 96 | | | `radius.full` | 9999 | Fully rounded (pills, circles) | Both scales at their real pixel values: ![Two ladders: the spacing scale from spacing.none at 0 to spacing.6xl at 128, drawn as bars at their true pixel heights, and the radius scale from radius.none to radius.full, drawn as squares with the real corner radius, ending in a full circle](/docs/images/diagrams/ds-spacing-radius-ladders.webp) ## Stroke width tokens Boldness scale (uses the same 9-stop vocabulary as color tones): ``` stroke.width: boldness(number([0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4]), min: { none: 0 }, max: { thick: 8, thicker: 16, thickest: 48 }) ``` | Token | Default value | |-------|---------------| | `stroke.width.none` | 0 | | `stroke.width.hint` | 0.5 | | `stroke.width.faint` | 0.75 | | `stroke.width.subtle` | 1 | | `stroke.width.soft` | 1.25 | | `stroke.width.mid` | 1.5 | | `stroke.width.firm` | 2 | | `stroke.width.bold` | 2.5 | | `stroke.width.strong` | 3 | | `stroke.width.intense` | 4 | | `stroke.width.thick` | 8 | | `stroke.width.thicker` | 16 | | `stroke.width.thickest` | 48 | There's no bare `$stroke.width` form: number-backed scales emit no bare token, so always name a stop (e.g. `$stroke.width.mid`). A bare reference is flagged as a mistake.
Heads-up: Stroke width tokens can be bound from the inspector's thickness dropdown (it lists the canvas's stroke-width tokens), or via blueprint syntax (st[(#000,w($stroke.width.soft))]).
## Visibility tokens Boldness scale of opacity-ish values: ``` visibility: boldness(number([0.05, 0.10, 0.20, 0.40, 0.50, 0.60, 0.80, 0.90, 0.95]), min: { invisible: 0 }, max: { opaque: 1 }) ``` | Token | Default value | |-------|---------------| | `visibility.invisible` | 0 | | `visibility.hint` | 0.05 | | `visibility.faint` | 0.10 | | `visibility.subtle` | 0.20 | | `visibility.soft` | 0.40 | | `visibility.mid` | 0.50 | | `visibility.firm` | 0.60 | | `visibility.bold` | 0.80 | | `visibility.strong` | 0.90 | | `visibility.intense` | 0.95 | | `visibility.opaque` | 1 | Use for layered transparency overlays, scrim opacity, gradient stops, etc. ## Font size tokens T-shirt scale, Tailwind-aligned: ``` font.size: tshirt(number([12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 80, 96, 128]), transforms: { accessibility.large-text: shift(+1) }) ``` The explicit `transforms:` overrides the t-shirt default (which would also shrink text under `density.compact`); only the large-text upshift is kept, since shrinking text by mode hurts readability. | Token | Default value | |-------|---------------| | `font.size.xs` | 12 | | `font.size.sm` | 14 | | `font.size.md` | 16 | | `font.size.lg` | 20 | | `font.size.xl` | 24 | | `font.size.2xl` | 32 | | `font.size.3xl` | 36 | | `font.size.4xl` | 40 | | `font.size.5xl` | 48 | | `font.size.6xl` | 64 | | `font.size.7xl` | 80 | | `font.size.8xl` | 96 | | `font.size.9xl` | 128 | `md` is the body-text size; everything else multiplies up or down from there in spirit. The seed also ships `font.size.base` as a 1.0x alias of `md`, so a token bound to `base` tracks whatever `md` resolves to. ## Font weight tokens Standard 9-step weight scale, exposed via boldness: ``` font.weight: boldness(number([100, 200, 300, 400, 500, 600, 700, 800, 900])) ``` | Token | Value | CSS Equivalent | |-------|-------|----------------| | `font.weight.hint` | 100 | Thin | | `font.weight.faint` | 200 | Extra Light | | `font.weight.subtle` | 300 | Light | | `font.weight.soft` | 400 | Normal | | `font.weight.mid` | 500 | Medium | | `font.weight.firm` | 600 | Semi Bold | | `font.weight.bold` | 700 | Bold | | `font.weight.strong` | 800 | Extra Bold | | `font.weight.intense` | 900 | Black | There's no bare `$font.weight` form: number-backed scales emit no bare token, so always name a role stop (e.g. `$font.weight.bold`). A bare reference is flagged as a mistake. ## Line height tokens Looseness scale: ``` font.lineHeight: looseness(number([1.0, 1.25, 1.375, 1.5, 1.625, 2.0])) ``` | Token | Value | Use | |-------|-------|-----| | `font.lineHeight.none` | 1.0 | Single-line, icons | | `font.lineHeight.tight` | 1.25 | Headings | | `font.lineHeight.snug` | 1.375 | Compact body | | `font.lineHeight.normal` | 1.5 | Body text | | `font.lineHeight.relaxed` | 1.625 | Comfortable reading | | `font.lineHeight.loose` | 2.0 | Double-spaced | `accessibility.large-text` shifts mappings toward looser by one step. ## Letter spacing tokens Looseness scale: ``` font.letterSpacing: looseness(number([-0.05, -0.025, -0.0125, 0, 0.025, 0.1])) ``` | Token | Value | Use | |-------|-------|-----| | `font.letterSpacing.none` | -0.05 | Very tight (display) | | `font.letterSpacing.tight` | -0.025 | Tight (headings) | | `font.letterSpacing.snug` | -0.0125 | Slightly tight | | `font.letterSpacing.normal` | 0 | Default | | `font.letterSpacing.relaxed` | 0.025 | Wide | | `font.letterSpacing.loose` | 0.1 | All-caps spacing | ## Font family tokens Three families ship by default: ``` font.family: Manrope font.family.serif: "Noto Serif" font.family.mono: monospace ``` Add more with separate primitive declarations: ``` font.family.display: "Fraunces" ``` Reference with the full dotted path (`$font.family.mono`). The font picker in the right toolbar sets `fontFamily` literally. To bind a family to a token, use a typography composite that has `fontFamily` set, or wire it through blueprint syntax. ## Typography composites The seed template ships these typography composites: | Token | fontSize | fontWeight | lineHeight | |-------|----------|------------|------------| | `typography.display.lg` | `font.size.6xl` (64) | bold | 1.1 | | `typography.display.md` | 52 | bold | 1.1 | | `typography.display.sm` | 44 | bold | 1.15 | | `typography.h1` | `font.size.3xl` (36) | bold | 1.2 | | `typography.h2` | 30 | bold | 1.25 | | `typography.h3` | `font.size.xl` (24) | firm | 1.3 | | `typography.h4` | `font.size.lg` (20) | firm | 1.35 | | `typography.body.lg` | 18 | soft | normal | | `typography.body.md` | `font.size.md` (16) | soft | normal | | `typography.body.sm` | `font.size.sm` (14) | soft | normal | | `typography.button` | `font.size.sm` (14) | firm | tight | | `typography.input` | `font.size.md` (16) | soft | 1.4 | | `typography.label` | `font.size.sm` (14) | mid | 1.4 | | `typography.caption` | `font.size.xs` (12) | soft | 1.4 | | `typography.code` | `font.size.sm` (14) | soft | normal (monospace family) | | `typography.editorial.lg` / `.md` / `.sm` | same scale as display | bold | matched | Serif (Noto Serif) variants for editorial headlines | ![Type specimen of the typography composite tokens from display.lg at 64px down to caption at 12px, plus code in a monospace font and editorial.md in Noto Serif, each row rendered as live text at its real size, weight, and line height](/docs/images/diagrams/ds-typography-specimen.webp) Apply a composite via the **Apply Typography Token** palette command. It sets fontSize, fontWeight, fontFamily, and lineHeight on the selected text in one go, and binds the composite `typographyTokenRef` (clearing any individual font-property token bindings). ## Shadow composites Tailwind-aligned shadow elevations: | Token | Layers (visual feel) | |-------|----------------------| | `shadow.2xs` | y:1, blur:0, opacity:0.05 (almost imperceptible) | | `shadow.xs` | y:1, blur:1, opacity:0.05 (very subtle) | | `shadow.sm` | y:1, blur:2, opacity:0.05 (small lift) | | `shadow.md` | 2-layer, y:2/y:4 (default elevation) | | `shadow.lg` | 2-layer, y:4/y:10 (pronounced lift) | | `shadow.xl` | 2-layer, y:10/y:20 (floating) | | `shadow.2xl` | y:25, blur:50, opacity:0.25 (dramatic) | | `shadow.inner` | y:2, blur:4 (inset feel) | ![The eight shadow composite tokens, shadow.2xs through shadow.2xl plus shadow.inner, each rendered by the engine on an identical white card, with the real offset, blur, and opacity under each token name](/docs/images/diagrams/ds-shadow-ladder.webp) Apply via the **Apply Shadow Token** palette command. Replaces existing drop shadows on the element; preserves inner shadows, glows, and blurs. ## The canonical default.ds shape The seed template's structure, condensed: ``` modes { theme: [light, dark] density: [comfortable, compact] accessibility: [standard, high-contrast, large-text] } // Brand and palette colors primary: boldness(color(#0080FF)) secondary: boldness(color(#FF3377)) tertiary: boldness(color(#FF9900)) quaternary: boldness(color(#FFDD00)) neutral: boldness(color(oklch(55.6%, 0, 0))) red: boldness(color(oklch(63.7%, 0.237, 25.331))) // ...21 Tailwind palettes total // Geometry & motion spacing: tshirt(number([4, 8, 12, 16, 24, 32, 48, 64, 96, 128]), min: { none: 0 }) radius: tshirt(number([2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96]), min: { none: 0 }, max: { full: 9999 }, transforms: none) stroke.width: boldness(number([0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4]), min: { none: 0 }, max: { thick: 8, thicker: 16, thickest: 48 }) visibility: boldness(number([0.05, 0.10, 0.20, 0.40, 0.50, 0.60, 0.80, 0.90, 0.95]), min: { invisible: 0 }, max: { opaque: 1 }) // Typography font.family: Manrope font.family.serif: "Noto Serif" font.family.mono: monospace font.size: tshirt(number([12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 80, 96, 128]), transforms: { accessibility.large-text: shift(+1) }) font.weight: boldness(number([100, 200, 300, 400, 500, 600, 700, 800, 900])) font.lineHeight: looseness(number([1.0, 1.25, 1.375, 1.5, 1.625, 2.0])) font.letterSpacing: looseness(number([-0.05, -0.025, -0.0125, 0, 0.025, 0.1])) // Chrome aliases (semantic role names pointing at resolver outputs) color.surface: neutral.hint color.surface.container: neutral.faint color.surface.container.high: neutral.subtle color.on-surface: neutral.strong color.outline: neutral.soft color.outline.variant: neutral.subtle color.text.primary: neutral.bold color.text.secondary: neutral.firm color.text.disabled: neutral.soft color.text.display: primary.firm color.text.display.alt: secondary.firm color.primary: primary.mid color.on-primary: neutral.50 // primitive ref → mode-immune color.primary.container: primary.hint // (mirror for secondary, tertiary, quaternary, success, error, warning, info) color.shadow: neutral.950 color.glow: neutral.50 ``` ## What's not built in A few token types you might expect but Brilliant doesn't ship: - **Animation tokens** (duration, easing). Not yet. Could be added as user-defined primitives later. - **Numeric tokens with units.** Numbers are stored as plain doubles. No px/rem/em/% unit system. Everything else is a custom token away. Pick a name, add it to your `Styles/default.ds`, and reference it. # Section: Import --- Source: https://brilliant.design/docs/import/from-figma.mdx # Import from Figma Three ways to bring Figma designs into Brilliant, ordered from least to most setup: - **On the web, from the "+" New menu.** Paste a Figma file link (or click **Create from Figma**) in the navbar's New menu. The first time, Brilliant sends you through Figma's sign-in and then picks up right where you left off; the import lands in a brand-new project named after the file and opens straight in the editor. - **In the desktop app, from the right toolbar.** Paste a Figma URL into the editor's **Import** section to pull a whole file (or specific pages, or one selected subtree) into the document you have open. Each Figma page becomes a Brilliant canvas. - **From the Brilliant plugin, inside Figma.** Push a selection out of Figma with **Copy to Clipboard** (no sign-in, it rides your system clipboard) or **Send to Brilliant** (drops straight onto the canvas once the plugin is connected to your desktop app or paired with your account). All three route through the same import pipeline, so the fidelity is identical. Pick based on where you're starting. ## Create from Figma on the web If you're on [brilliant.design](https://brilliant.design) in the browser, the fastest path is the **New menu**: the **+** button in the top navbar. ![The web New menu open, showing New project, a "Paste a Figma file link" field, and the Create from Figma button|55%x](/docs/images/webflow-new-menu-figma.webp) The menu has one job for Figma: paste a link, then hit **Create from Figma** (or press in the field). Brilliant accepts the same link shapes everywhere: - `https://www.figma.com/design/FILE_KEY/…` (the current form) - `https://www.figma.com/file/FILE_KEY/…` (the older form) - a bare file key (10–30 letters and digits) The first time you do this, your account isn't linked to Figma yet, so Brilliant stashes your link, sends you through Figma's consent screen, and returns you to the same page. You don't have to click anything twice: once you approve, the import **resumes on its own** and finishes the job you already asked for. On later imports the account is already linked, so there's no round-trip at all. When it runs, Brilliant claims a **new project named after the Figma file** (the slug in the link becomes the project name; a link with no slug falls back to `figma-import`) and drops you straight into the editor, where the file imports onto the canvas. No dialog, no name to type. ![The New menu mid-import: the URL is in the field and the Create from Figma button reads Creating…|55%x](/docs/images/webflow-new-menu-importing.webp)
Paste a whole link. The web path always brings the whole file. To cherry-pick pages or a single selected subtree, use the desktop URL import below, which exposes those toggles.
## Import a whole file by URL (desktop) In the desktop app, Brilliant's right toolbar has an **Import** section that accepts a Figma URL or bare file key. This is the path with the most control: whole file, specific pages, or just a selected subtree. ### 1. Sign in once Paste a URL (`figma.com/design/…`, the older `/file/…` form, or a bare 10–30 character file key) and click the slider button that appears next to the field. Brilliant opens Figma's consent screen in your browser; once you approve, you're set. Tokens persist and refresh silently, and the section shows a **Connected** indicator at the bottom (hover it to reveal **Disconnect**). This desktop link is separate from the web one, so connecting on desktop doesn't connect the web New menu, and vice versa. ### 2. Pick what to import Three modes, same field: - **Whole file:** click **Import from Figma** with everything as-is. This is the default. - **Specific pages:** click the slider button to expand the page picker and uncheck the pages you don't want. - **Selection only:** if your URL contains `?node-id=…` (Figma generates this when you right-click an element → **Copy link**), an extra checkbox appears next to the slider button. Toggle it and only that subtree is fetched.
Tip: If you paste a URL with a node-id but don't toggle the selection-only checkbox, Brilliant imports the whole file anyway. The toggle is what activates the node-id.
While the import runs, the section reports its stage: *Fetching, Downloading images (m/n), Converting*. It also surfaces Figma rate limits inline (`Rate limited by Figma, retrying in 4s…`). Cancellable any time. ### 3. Where it lands How Brilliant places the imported pages depends on how many came back: - **More than one page:** Brilliant creates a folder named after the Figma file and drops one canvas per page into it. - **Exactly one page, and your current canvas is empty:** the import lands on the current canvas. No folder, no new canvas. - **Exactly one page, and your current canvas has content:** a single new canvas at the document root. Selection-only imports always produce a single page, so they follow the one-page rule above: an empty current canvas keeps its name and receives the import; otherwise a new canvas named ` Selection` lands at the document root. Canvas names elsewhere come from the Figma page names, with dots replaced by spaces to avoid colliding with the `.bl` extension. Name collisions get a ` (2)`, ` (3)`, … suffix. ### Files you don't own Brilliant calls Figma's REST API as you, so Figma's per-user rate limits apply to your account. The catch worth knowing about: your limit depends on your **seat type** on the plan that owns the file, not on the file itself. If you're a Full or Dev seat (the file owner, or a paid editor on the team), you're fine. The endpoints Brilliant uses (`GET file`, `GET file nodes`, `GET image fills`) start at 10 calls per minute on the Free / Starter plan and climb from there. If you're a **View or Collab seat** on someone else's plan (for example, someone shared a file with you and you don't have edit access), Figma caps file fetches at **6 per month**. One Brilliant import burns one of those six. After that, every import returns HTTP 429 and Brilliant reports *Rate limited by Figma, retrying in {n}s…* until the bucket refills (which won't be soon, since the reset is monthly). Two ways out: - **Duplicate the file into your drafts.** From Figma, open the file menu and choose **Duplicate to your drafts**. The copy is yours: you become the owner, your seat type for that file is now Full, and the limit jumps from 6 a month to ten or more per minute. (File owners can disable duplication; if the option is greyed out, that's why.) - **Use the plugin instead.** The plugin's [Copy to Clipboard](#push-a-selection-from-the-plugin) button ships data over your clipboard: no API call, no quota, no auth. It's the safest path when you only need a frame or two, or when you're stuck behind the monthly cap. ## Push a selection from the plugin If you're already in Figma and just want a frame or two on the canvas you have open, the **Brilliant** plugin is the faster path. It has two buttons, and they cover two different situations. ### 1. Install the plugin Grab **[Brilliant](https://www.figma.com/community/plugin/1611333431302009837)** from the Figma Community and click **Open In Figma** to add it to your account. ### 2. Run it on a selection In Figma, select what you want to copy, then open **Plugins → Brilliant** (or right-click → **Plugins**). A small panel appears with up to two action buttons and a connection status row at the bottom: - **Copy to Clipboard** is always there. Its label follows the selection: *Copy "Hero" to Clipboard* for a single layer, *Copy 5 to Clipboard* for a multi-selection. - **Send to Brilliant** shows up only once the plugin has a live connection (see below). Its label mirrors the other button: *Send "Hero" to Brilliant*, *Send 5 to Brilliant*. - The **status row** tells you what the plugin can reach right now: *Looking for Brilliant…*, *Connected to Brilliant* (your local desktop app), *Connected via cloud* (with a count of live editors), or *Brilliant not running*. ### Copy to Clipboard, then paste (no sign-in) The zero-setup path. Click **Copy to Clipboard** (or press or ). The button flashes **Copied to clipboard** when the payload is on your clipboard. Then switch to Brilliant, click anywhere on the canvas, and press . Brilliant recognizes the Figma payload and runs it through the full import pipeline. No Brilliant sign-in, no API access: the selection travels over your system clipboard, so this path works even when you're behind Figma's monthly fetch cap.
Use Figma Desktop if you can. The browser version of Figma occasionally blocks plugin clipboard writes. If you see Failed. Try Figma Desktop, click the button again. These hiccups are often a one-off. If it still fails, run the plugin from the desktop app instead.
### Send to Brilliant (connect once) **Send to Brilliant** skips the clipboard and drops the selection straight onto your active canvas. It appears once the plugin is connected, over one of two lanes: - **Your local desktop app.** If the Brilliant desktop app is running, the plugin finds it automatically over localhost and the status row reads *Connected to Brilliant*. Nothing to set up. - **The cloud relay.** When the desktop app isn't reachable (you're in the Figma web editor, say), the plugin offers a **Connect to Brilliant** button that pairs through an opt-in cloud relay. Pairing is one click, and you approve it yourself: 1. Click **Connect to Brilliant** in the plugin. The status row switches to *Waiting for approval…* and Brilliant opens an approval page in your browser. 2. On that page (`/figma-connect`), you confirm which Brilliant account is being linked, then click **Approve**. If you're signed out, Brilliant sends you through sign-in first and lands you right back on the card. 3. The page confirms **Connected**, the plugin picks the pairing up on its next check, and the status row reads *Connected via cloud*. The link stays until you disconnect it. ![The web approval card for the Figma plugin: it names the Brilliant account being linked, with Approve and Cancel|60%x](/docs/images/webflow-figma-connect.webp) The approval link is single-use and expires after about ten minutes. If it lapses, the page says so and you just reopen the connection from the plugin. To drop the cloud link later, use **Disconnect cloud** in the plugin's status row. ## Manage your Figma connection The **web** Figma link (the one the New menu's *Create from Figma* uses) is managed from your account settings: open **Settings → Account & security**, and if a link exists you'll see a **Connected apps** card with **Figma** listed. Click **Disconnect** to revoke Brilliant's access; the next web import will ask you to sign in again. The **desktop** link is separate and lives in the editor's right-toolbar **Import** section (hover the **Connected** indicator to reveal **Disconnect**). The plugin's **cloud** pairing is separate again, and you drop it with **Disconnect cloud** in the plugin. ## What carries over - **Layout:** auto layout (direction, sizing, padding, gap, wrap, alignment), absolute-positioned children, layout grids, frames, and groups. - **Geometry:** vector paths, boolean operations, ellipse arcs and rings, corner radii (uniform or per-corner), aspect-ratio locks. - **Fills and strokes:** solid colors; linear, radial, and angular gradients (diamond gradients flatten to their first stop's color); image fills (bytes come along with the import, so no broken references on the Brilliant side). Stroke weight, alignment, joins, and caps. - **Effects:** drop shadow, inner shadow, layer blur, background blur. - **Text:** font family, weight, size, line height, letter spacing, alignment, fills, plus per-character overrides for mixed-style text. - **Components and instances:** if you include both the master component and its instances in your selection, the master / instance link is preserved and overrides are detected automatically. Instances copied without their master come over as standalone elements with their final appearance, but no link back. - **Element-level:** opacity, blend mode, visibility, and masks. ## What doesn't - **Non-variant component properties.** Variant axes come through as real, editable component sets, one variant per combination. Figma's boolean, text, and instance-swap properties don't: each variant keeps its resolved look, but those editable knobs are dropped. - **Interactive prototypes.** Connections, transitions, and prototype flows aren't part of the canvas model. - **Variables and shared styles.** Resolved values (colors, sizes, type) come through, but the bindings back to Figma Variables or shared color / text / effect styles don't. - **FigJam, Figma Slides, Dev Mode.** The plugin runs in the Figma design editor only. ## Next - **Tour the editor:** [Quick Tour](/docs/getting-started/first-steps). - **See how the two tools compare:** [Brilliant vs Figma](/docs/compare/figma). # Section: Solutions --- Source: https://brilliant.design/docs/solutions/brand-changes.mdx # Apply a brand change everywhere A rebrand is only painful because the brand lives in a thousand places. In Brilliant it lives in one: a plain-text file in your project's `Styles/` folder. A brand overlay is often three or four lines, and switching to it re-skins every element bound to a token. The work is in the binding, not the switch. Do it once and the switch is free forever. ## Bind properties to roles, not values A token is a named decision. Instead of `#0080FF` on twenty buttons across thirty canvases, you reference `primary.mid` and change the seed in one line. Semantic roles are the ones to reach for, because they're mode-aware: the same name resolves to one value in light and another in dark. `color.surface`, `color.text.primary`, `spacing.md`, `radius.lg`. Primitive stops like `$primary.500` stay identical in every mode, which is what you want for something that must never flip. You don't have to open the file to bind. The right toolbar's color picker, spacing inputs, and typography controls all bind a property to a token with a click, and an agent working on the canvas reaches for tokens on its own when a design system is active. Type and shadows bind too, through **composites**: `typography.h1` bundles font size, weight, and line height into one named unit, and `shadow.md` is a list of drop layers. Apply the composite once and editing it later moves every element wearing it. That matters in a rebrand, where the type ramp and the elevation ladder usually change alongside the color. ## A brand is a sparse overlay Brands are sibling `.ds` files next to `default.ds`. They declare only the deltas: a seed, maybe a font family, a few overrides where this brand wants different behavior. Everything else cascades from the base. ``` // Styles/acme.ds primary: boldness(color(#FF6A00)) font.family: Inter color.primary { $default: primary.firm, dark: primary.subtle } ``` Tailwind-style palettes, typography composites, and the rest of the chrome aliases all inherit. Ask an agent to write one for you ("add an alternate brand called sunset with a warm orange primary") and it drops the file into `Styles/` and flips the canvas to it. ## Switch and watch The **Design system** section sits at the top of the right toolbar, with a dropdown per mode axis plus the brand picker. Hover to preview, click to commit. With nothing selected you set the canvas-level override, which persists in the `.bl` file; with a selection you set it per element instead. Here is a card already switched to an alternate brand. Change the brand, flip the theme, or tighten the density, and everything bound to a token re-skins at once: Surfaces darken, ink lightens, accents flip, and nothing in the layout moves, because each property resolves through a role rather than a fixed value. The cascade is per element first, then per canvas, then folder, so you can hold one screen in the old brand while the rest of the project moves. And because the design system cascades down the folder tree like `.editorconfig`, a `Marketing/` subfolder can carry its own overrides on top of the project-wide base, or stop the walk entirely with `root: true`. ![One card, two brands, re-skinned from a single switch|75%x](/docs/images/tutorial-brand-reskin.webp) ## What won't follow Anything set to a raw hex stays that hex. That's the whole failure mode, and it's easy to fix: select the element and tell the agent to bind the fill to the closest design system token, then switch again. Two other edges worth knowing. Primitives are mode-independent on purpose, so a seed that must differ per mode needs two primitives with a semantic routing between them. And `transforms:` on a generator replaces the defaults entirely rather than merging, so an override is a full statement of the behavior you want. ## Next - **Theming, custom axes, and multi-brand:** [Modes & Brands](/docs/design-system/modes-and-brands). - **Every token type and the built-in defaults:** [Tokens](/docs/design-system/tokens). - **Watch a card re-skin, step by step:** [Design tutorials](/docs/ai/design-tutorials). - **Start from the beginning:** [Design system overview](/docs/design-system/overview). --- Source: https://brilliant.design/docs/solutions/build-feature-ui.mdx # Build feature UI from a spec The slow part of feature work is rarely the idea. It's laying out the fifth variant of a settings panel with the right spacing, the right components, the right tokens. Brilliant's agent does that pass for you, using the primitives already in your project and over a hundred of the app's own commands, each with full undo. What lands is vectors. Not a mockup image, not a code sketch. ## Give it the spec and the context Focus the chat with `/` and describe the surface. Then point it at what already exists: - **Selection.** Click a frame first and the agent reads it, so "make this the empty state" is unambiguous. - **`@` mentions.** Type `@` and pick an element by name to attach its structure and a render to the message: "Match the secondary button's radius and height to @Get Started." - **Attachments.** Paste a screenshot, drag a file in, or use the paperclip when the spec is a picture. ![A section an agent built from a prompt, live and editable on the canvas|75%x](/docs/images/ai-before-after.webp) The agent can also look things up itself. It searches across canvases by name, text, type, color, or component, so "match the empty state to the one on the Settings canvas" is a request it can act on without you finding the file first. ## It builds on your system, not near it When a design system is active, every color, size, and font slot the agent writes resolves through it. Ask for tokens explicitly and you get `$color.surface`, `$spacing.md`, `$radius.lg` instead of hex codes, which means the screen re-skins with everything else later. Components work the same way. Instances stay linked to their master, so the row of cards you asked for isn't five copies that will drift. Override the text on one and the rest keep syncing, and everything you didn't override keeps flowing down when the master changes. Masters can live on a different canvas from their instances, which is the usual setup: one canvas of components, instances everywhere else. An instance remembers which canvas its master is on and syncs to its latest values when you open it. When the spec has states rather than one look, a **component set** covers it. Named properties become axes, every combination of values maps to one variant, and an instance carries a configuration: one value per property. Flip the dropdowns in the right toolbar's Component section and the instance re-renders as the matching variant. No swapping, no duplicating a tree per state. ![The right toolbar inspector, where you tune what the agent produced|360x](/docs/images/bank/right-toolbar/general.webp) ## The loop catches its own mistakes Every Blueprint line is validated before it touches the canvas, executed if valid, then linted for composition problems on the real geometry: text with no fill on a light background, an element that resolves to `0 x 120` because `fill` sits inside a `hug` parent, duplicate sibling names, content spilling out of a clipping frame. Each one comes back with a stable code, a plain sentence, and a concrete fix, plus a render of what actually landed. The agent usually corrects it inside the same turn, so you're reviewing design decisions instead of debugging layout. ## Then you take over Everything is a native element with the usual handles, inspector, and shortcuts. Nudge spacing, swap a fill, restructure the frame. Each chat keeps its own undo stack too, so you can hand-edit while an agent is still running and roll back just its turn with **Revert** if a direction was wrong. If a change you made by hand belongs to everyone, **Push Overrides to Master** sends it back up so every other instance follows. That one needs the master on the canvas you're viewing. ## Where it stops The agent works with the components you have; it can't invent a swap-instance gesture that doesn't exist, and repointing an instance at a different master means detaching and making a fresh one. Slots (children an instance fully owns) are designated through Blueprint authoring, with no by-hand control. And structural drift inside an instance is blocked on purpose: add or reorder children on the master and let it propagate. ## Next - **Follow a full build:** [Design tutorials](/docs/ai/design-tutorials). - **Masters, instances, and sets:** [Components](/docs/editor/components). - **Bind it to your tokens:** [Design system overview](/docs/design-system/overview). - **What the checks catch:** [Quality linter](/docs/developers/quality-linter). --- Source: https://brilliant.design/docs/solutions/design-engineers.mdx # Brilliant for design engineers You live in an editor with an agent in it. The design tool is the one place that agent can't reach. Brilliant closes that: it runs a local MCP server the whole time the app is open, at `http://127.0.0.1:3333/mcp`, and any tool that speaks MCP can design on your canvas. Driving Brilliant from an external agent is unlimited on every plan, Free included. ## Point your editor at the canvas Setup is a one-time step per tool. If the tool is in Brilliant's built-in list it's a single toggle; otherwise you paste one line of config. Claude Code, Claude Desktop, Cursor, Codex CLI, GitHub Copilot in VS Code, Windsurf, and Antigravity each have a page with exact steps, and **Settings → MCP Connections** carries the live list (Gemini CLI, Zed, and OpenCode included). ![Settings, MCP Connections: a flat list of tools each with a connect checkmark|420x](/docs/images/bank/settings/mcp/general.webp) If you use Claude Code, the fastest setup is no setup. Open the built-in chat in Brilliant once and it registers the endpoint with `claude` for you. From then on: "Add a hero to the dashboard canvas." ![A layout built on the canvas by Claude Code over MCP|75%x](/docs/images/agent-claude-code-result.webp) ## Agents write a language, not JSON An agent driving Brilliant writes Blueprint: a compact, line-oriented DSL for 2D design. One element per line, children indented under their parent, references that let one line point at another, and a grammar that fits on a page. ```blueprint al(v,g($spacing.lg),pad($spacing.xl)) s(360,hug) f[($color.surface)] rd($radius.md) "Card" t("Settings",$font.family,$font.size.lg,sb) f[($color.text.primary)] #title ``` That density is the point: a card that runs ten lines of JSON is one line of Blueprint. Values like `$spacing.lg` aren't literals, they're design tokens resolved through the document's design system, so an agent stays on your system by default rather than guessing hex codes. You never have to write Blueprint yourself. Brilliant serves the grammar to agents over MCP, so they arrive fluent. ## Design files that behave like source `.bl` files are plain, deterministic Blueprint text: one element per line, stable ordering, inline coordinates, hex colors. Clean diffs, blameable history, branchable experiments. ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` A published project is also a real Git repository with a clone URL of the form `https://api.brilliant.design/git/{handle}/{project}.git`. ## Getting markup back out Export the selection as HTML plus inline CSS (snippet, full document, or flex), or as React JSX ready to paste into a `.tsx` file. **Copy as** puts the same thing on your clipboard, along with CSS properties or raw Blueprint. ## The honest boundaries The MCP endpoint is localhost only and unauthenticated, and Brilliant has to be open for a connected tool to reach it. Brilliant components are design components: they don't run your app code, and there's no way to drop a live React component onto the canvas. The generated HTML and React come from vector shapes rather than being the design's native format, so treat them as a starting point for your stack, not a build artifact. ## Next - **Connect your editor:** [Use with AI agents](/docs/agents/overview). - **Read the language agents speak:** [The Blueprint DSL](/docs/developers/blueprint). - **See every export format:** [Import and export](/docs/workspace/import-and-export). - **Understand the file layout:** [Canvases and files](/docs/workspace/canvases-and-files). --- Source: https://brilliant.design/docs/solutions/design-system-leads.mdx # Brilliant for design system leads A design system only holds if the people using it can't easily go around it. In Brilliant the system is a plain-text file in the project, one seed color expands into an 11-step OKLCH ramp, and every agent working on the canvas gets the token catalog in context. Agents paint with roles, not hex. Nobody has to remember to comply. The tokens are the shortest path. ## The system is a file in the repo Each project has a `Styles/` folder with `default.ds` inside it: the file you edit. Brilliant writes a fully populated version when you open a fresh project, so there's something real to start from on day one. ``` my-project/ ├── Styles/ │ ├── default.ds ← you edit this │ └── .gen/ │ └── default.gen.yaml ← auto-generated, don't edit └── Canvas.bl ``` `default.gen.yaml` is the resolved output, regenerated on every save with everything expanded to concrete values. That's the file external tooling reads: Style Dictionary, Tokens Studio plugins, build scripts. ## Primitives, semantics, and five generators The central model: **primitives are 1:1 (one fixed value), semantics are 1:N (one value per mode)**. Primitives give you raw stops like `primary.50..950`. Semantics give you mode-aware roles like `primary.mid` and `spacing.md`, which resolve differently in light versus dark, comfortable versus compact. ![A primitive token rendered identically on light and dark panels, next to a semantic token resolving to a different value in each mode|75%x](/docs/images/diagrams/ds-primitives-vs-semantics.webp) Five built-in generators do the expansion: `color()` and `number()` produce primitives, and `boldness()`, `tshirt()`, and `looseness()` wrap a primitive scale into mode-aware roles. Three vocabularies cover every domain: boldness for tones and weights, t-shirt for spacing, radius, and font size, looseness for line height and letter spacing. ## Agents are held to it When a design system is active, every color, size, and font slot an agent writes is a `$token` resolved through the current brand and mode rather than a bare value: ```blueprint al(v,g($spacing.md),pad($spacing.lg)) s(280,hug) f[($color.surface)] rd($radius.lg) "Card" t("Real-time sync",$font.family,$font.size.lg,b) f[($color.text.primary)] ``` The agent has your catalog in context, so it reaches for `color.surface` and `spacing.md` on its own. Ask it to bind a stray fill to the closest token and it will. Because every slot is a role, the whole design answers to one switch. This card is on an alternate brand; change the brand, flip the theme, or tighten the density, and everything the agent painted moves with the system: ## Modes and brands, without a second file tree Modes are named axes. The seed template ships three (`theme`, `density`, `accessibility`) and you can declare any others you want. A brand is a sparse sibling `.ds` file that declares only its deltas: a seed, maybe a font family, a few overrides. Everything else cascades from the base, and the cascade walks the folder tree like `.editorconfig`, including `root: true` to stop it. ## Where the edges are Brilliant has no published component library and no library export: masters are regular frames you keep on a canvas by convention, and there's no component description or metadata layer. Primitives are mode-independent by design, so a value that must differ per mode needs a semantic in front of it. And a raw hex on an element stays a raw hex: it won't re-skin when you switch, which is the one failure mode worth auditing for. ## Next - **Start here:** [Design system overview](/docs/design-system/overview). - **Every token type and the defaults:** [Tokens](/docs/design-system/tokens). - **Theming and multi-brand:** [Modes & Brands](/docs/design-system/modes-and-brands). - **The reuse layer above tokens:** [Components](/docs/editor/components). --- Source: https://brilliant.design/docs/solutions/explore-variations.mdx # Explore variations fast Exploration dies when each direction costs an hour. In Brilliant a direction costs a prompt: chat sessions run in parallel on the same canvas, the first ten answer to `Cmd+1` through `Cmd+9` and `Cmd+0`, and each one keeps its own model, history, and undo stack. Run several at once, keep the one that works, finish it by hand. ## Fork the conversation, not the file With the chat focused, press `Cmd+N` to start a fresh session and prompt the alternate take there. The old session stays live, so you can jump between experiments instead of choosing early. Sessions dock as tabs to the right of the AI input. Minimized, each shows its topic, a spinner while it works, and its context usage; click to expand, double-click to rename, drag to reorder. `Cmd+Shift+]` and `Cmd+Shift+[` walk between them. ![The chat panel with an active session and the canvas behind it|75%x](/docs/images/quick-tour-ai-chat.webp) ## Speak in deltas Inside a session, follow-ups see the full conversation and the current canvas, so you steer instead of restating: > Try a bolder version: bigger type, fewer words, one button. > Same layout, but make it feel calmer. Describing the problem works as well as prescribing the fix. Add `#` in the composer for style modifiers (dark, mobile, minimal, and so on), and `@` to point at an element you want the new direction to match. Two habits make the loop faster. Send while the model is still working and your message queues, firing automatically when the current response lands, so you don't sit and wait. And `/model` switches provider and model mid-conversation, which is how you run the same brief through two models without retyping it. ![The chat panel alongside a design being iterated on the canvas|75%x](/docs/images/chat-step2.webp) ## Undo that doesn't punish you Each chat has its own undo stack, separate from the canvas's main history. So you can keep hand-editing while a session runs, and a turn you don't like has a **Revert** control on its card that rolls back exactly what that turn changed, with a **Redo** to bring it back. Stop a run mid-flight with the stop button or `/stop`. Anything already created stays on the canvas, and the stopped turn still gets its Revert. ## Keep the winner, park the rest Variations are just elements, so the usual moves apply. Duplicate a canvas with `Cmd+Ctrl+N` to hold a direction. Make a new canvas with `Cmd+N` when the explorer is focused. Because canvases are plain-text `.bl` files, you can also branch the whole workspace in Git and review the diff. To send one option around on its own, publish it as a [drop](/docs/platform/drops): select the elements, run **Publish drop**, and the link is on your clipboard. Sessions themselves are worth keeping too. Toggle the chat explorer with `Cmd+Shift+A` to browse, search, and manage every session in a sidebar, and the header's copy button exports a whole conversation as Markdown with its metadata (model, date, project, canvas, tokens, turns) when you want the reasoning, not just the result. ## The catch Canvas undo is session-only and per canvas. It survives switching canvases but not quitting the app, so a direction you want to keep needs to live somewhere real: its own canvas, a drop, a checkpoint on a published project, or a Git commit. Autosave keeps your work safe continuously, but autosave is not history. ## Next - **Tabs, slash commands, and revert:** [Sessions and chat](/docs/ai/sessions-and-chat). - **A worked example of iterating by prompt:** [Design tutorials](/docs/ai/design-tutorials). - **Where canvases and undo live:** [Canvases and files](/docs/workspace/canvases-and-files). - **Share a single direction:** [Drops](/docs/platform/drops). --- Source: https://brilliant.design/docs/solutions/founders.mdx # Brilliant for founders and small teams Design is usually the thing you skip because there's nobody to do it. Brilliant is free: the whole tool, not a trial or a stripped-down tier. Auto layout, components, vector editing, effects, design systems, tokens, GPU shaders, ten export formats, Figma import, and unlimited MCP usage, on macOS and Windows. The agent does the first draft. You do the taste. ## Start from a sentence Press `/` on the canvas, describe the outcome, and the agent builds it as native, editable elements: pages, dashboards, illustrations, diagrams, wireframes. Then you keep talking to it. > Build a dashboard stat card: a small label, a big metric number, a trend indicator, and a sparkline area below. Nothing is flattened to an image, so when something is 80% right you fix the last 20% by hand instead of re-prompting forever. Haven't connected a model? You don't need to: your first message runs on Brilliant AI. Playground, a toggle in Settings, replays bundled demo conversations if you'd rather watch first. ## On-brand before you have a brand Every fresh project ships with a fully populated design system: colors, spacing, radius, type, all already wired. Ask the agent to use your tokens and it paints surfaces with roles like `color.surface` instead of hard-coded hex. The payoff shows up the day you change your mind about the brand. Switch the theme or brand dropdown in the right toolbar and every screen re-skins at once. You can even have the agent author the new brand: "add an alternate brand called sunset with a warm orange primary" writes a sparse file into `Styles/` and flips the canvas over to it. ![The same stat card under two brands, re-skinned from one switch|75%x](/docs/images/tutorial-brand-reskin.webp) ## Ship it somewhere Export the selection to PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, or Replay. Copy a section straight to the clipboard as HTML, React, CSS, or an image and paste it into your codebase or a thread. Or publish. A project goes live at brilliant.design with one toggle, and the link lands on your clipboard. Public is the default and it's free. Investors, contractors, and early users open the real editor in a browser, view-only, and can copy pieces out. When you do bring someone in, invite them by email or `@handle` as a viewer or an editor. Editing is genuinely simultaneous: presence avatars show who's around, colored cursors show where they're working, and two people on different parts of the canvas never block each other. On a public project that costs nothing. Already have assets somewhere? Figma files import directly, by pasting a file URL or through the Copy to Brilliant plugin, and Sketch files import too. You don't start from an empty canvas just because you started from another tool. ## What it costs The design tool is free forever, with no per-seat charge. Two things are paid: more built-in AI, and keeping work private (private projects, private drops, share links). The AI comes two ways: **Brilliant AI**, the default, included with every plan (Free includes it every month; paid plans raise it in multiples of the base), or **Your own AI** (your own keys and coding agents), with zero markup and a 10-a-day cap on Free that any paid plan lifts. Driving Brilliant from an external agent over MCP is never capped. That shape is deliberate: free is for working in public, paid is for working in private. If your early work can live in the open, a small team can run on Brilliant without a line item. ## The honest part You still need judgment. The agent produces professional structure, auto layout, components, tokens, effects, but it doesn't know your positioning or which of three directions is the right one. Brilliant is also a static canvas today, with click-through prototypes on the roadmap. And the built-in AI beyond the Brilliant AI Free includes every month and the 10-a-day cap on your own key is a paid feature: more Brilliant AI usage comes with the monthly plans, and your own AI is unlimited on any paid plan. A couple of platform edges are worth knowing before you plan around them: video export runs on macOS and Windows only, transparent video is macOS only, and the web editor needs a WebGPU-capable browser (Chrome, Edge, Safari 26 or later, or Samsung Internet). ## Next - **Get it running:** [Download & Install](/docs/getting-started/installation). - **Build something end to end:** [Design tutorials](/docs/ai/design-tutorials). - **Make it look like you:** [Design system overview](/docs/design-system/overview). - **Put it online:** [Publish a project](/docs/platform/projects). --- Source: https://brilliant.design/docs/solutions/move-off-figma.mdx # Move off Figma Switching tools usually means rebuilding a year of work by hand. It doesn't here. Brilliant imports Figma files directly through two paths, and each Figma page becomes one Brilliant canvas, with components and instances arriving as real, linked components. This page is the map of the move. Each step links to the page that actually walks you through it. ## 1. Bring the files over Two routes, same import pipeline, so the fidelity is identical either way. Pick based on where you're standing: - **Paste a Figma URL** into Brilliant's right toolbar to pull a whole file, specific pages, or one selected subtree. Needs a one-time browser sign-in to Figma. - **Use the Copy to Brilliant plugin** to push a selection from inside Figma onto the canvas you already have open. No sign-in, no API call, no quota: the selection rides over your clipboard. The full walkthrough, including where imports land and the seat-type rate limit that catches people importing files they don't own, is in [Import from Figma](/docs/import/from-figma). Read the rate-limit section before you start on a shared file: a View or Collab seat on someone else's plan gets six file fetches a month, and the plugin route sidesteps it entirely. Plenty comes across. Auto layout with its direction, sizing, padding, gap, wrap, and alignment. Vector paths, boolean operations, arcs and rings, per-corner radii. Solid fills, linear, radial, and angular gradients, image fills with their bytes. Drop shadow, inner shadow, layer blur, background blur. Text down to per-character overrides for mixed-style runs. Opacity, blend mode, visibility, masks. Components survive the trip as components. Include a master and its instances in the same import and the link is preserved, with overrides detected automatically. Variant axes arrive as real, editable component sets, one variant per combination. If your history is mixed, Sketch files import too: run **Import Sketch File**, pick which pages you want, and each becomes its own canvas of native elements. SVG, images, and other Brilliant `.bl` files come in the same way. ## 2. Point your habits somewhere Most of what you know transfers. Pages become canvases, frames stay frames, auto layout stays auto layout, component sets keep their variant axes, and Figma's styles and variables land as design system tokens. `V`, `F`, `R`, `O`, `T`, `P`, `Cmd+G`, `Cmd+D`, and the align chords are all where you left them. A handful of things genuinely differ, and knowing them up front saves a confusing first afternoon: selection is grouped per frame, there's no save button, undo is per canvas, and components live on the canvas rather than in a panel. [Coming from Figma](/docs/getting-started/coming-from-figma) is the concept-by-concept map and the full shortcut translation, including the ones that changed. Every shortcut is remappable from the Shortcuts panel with `Shift+?`, so if a chord you rely on moved, you can put it back rather than relearn it. ## 3. Know what you're trading Some things don't come across the wire, and some don't exist on this side at all. Both lists are short and both are written down rather than discovered later. **Doesn't survive the import:** non-variant component properties (boolean, text, and instance-swap knobs; variant axes do come through), interactive prototypes, and the bindings back to Figma Variables and shared styles. Resolved values arrive, the links don't. FigJam, Slides, and Dev Mode are out of scope: the plugin runs in the Figma design editor only. **Isn't in Brilliant:** interactive prototypes. Brilliant is a fully editable static canvas today, with no transitions or clickable hotspots yet and prototyping on the roadmap. There's also no swap-instance command; within a component set you flip property dropdowns instead. It runs the other way too. Brilliant adds a team of agents working inside the canvas, plain-text files that diff in Git, native GPU shaders as first-class fills, ten export formats including code and video, unlimited MCP usage on Free, and no seat charge for the design tool at all. The feature-by-feature account in both directions is in [Brilliant vs Figma](/docs/compare/figma). ## 4. You don't have to move all at once Import is not a one-way door. Brilliant also has **Send to Figma**, which pushes a selection back to the paired plugin, so a canvas can round-trip while a team is still deciding. Keep a project in both places for a sprint if that's what it takes. The move is also cheap to try, because the design tool is free: the whole thing, not a trial. Import a file, work in it for a week, and decide with real evidence instead of a feature table. Your files stay yours the entire time. A Brilliant workspace is a folder on your disk, canvases are plain-text `.bl` files inside it, and nothing reaches Brilliant's servers until you choose to publish a project. There's no lock-in to escape if you change your mind. ## Next - **The concept map and shortcut translation:** [Coming from Figma](/docs/getting-started/coming-from-figma). - **The actual import, step by step:** [Import from Figma](/docs/import/from-figma). - **The full comparison:** [Brilliant vs Figma](/docs/compare/figma). --- Source: https://brilliant.design/docs/solutions/moving-from-motiff.mdx # Moving from Motiff If you designed in Motiff, you're looking for a new home, and not by choice. Motiff has been discontinued, and the window to get your work out is closing. This page is the honest map of the move: what to export before the deadline, the real ways to get those designs into Brilliant, what you'll be trading, and why a tool whose files live on your own disk is a safer place to land after a cloud tool disappears. No spin here. Some of this is a rebuild, not a one-click import, and we say so plainly below. ## 1. Where things stand A red banner now sits at the top of every page on motiff.com (checked 2026-08-25): > Motiff has been discontinued. Data export is available until October 31, 2026
The date that matters. Motiff's data export closes October 31, 2026. Motiff was a cloud tool, so that copy on their servers is the only copy of your work. When the export path closes, it's gone. Get out anything you want to keep before then.
Two things are worth knowing so you're not surprised. First, the professional editor you remember, the Figma-class vector tool, has been replaced on motiff.com by an AI UI generator called "Motiff AI." The old editor still exists, but only as an export destination: look for the **Export old Motiff editor files** link in the site footer, which points at [motiff.com/export-files](https://motiff.com/export-files). Second, Motiff never published an open or documented file format, so there's no `.motiff` file you can hand to a converter. The way out is through the formats Motiff exports, which is what the rest of this page is about. This is a real loss of a tool people chose and got good at. The goal here is just to make the move as clean as it can be. ## 2. Export your work before October 31 Start here, today, even if you haven't decided where you're going. The export is the one step with a hard deadline, and once it passes there's no second chance. Open Motiff's own **[Export old Motiff editor files](https://motiff.com/export-files)** path and export everything Motiff offers you, in the richest format it gives you. Grab more than you think you need. Storage is cheap; a permanently deleted design is not recoverable. When you get to choose a format, favor the ones a design tool can actually read back in, because that's what makes the next step possible: - **Figma, if your plan offers it.** Motiff's paid tier can copy generated UI to Figma. If you can get your work into a Figma file, that's the highest-fidelity path out, and Brilliant imports Figma directly (see below). - **SVG for vector work.** Motiff's editor historically exported SVG. SVG comes into Brilliant as real, editable vector elements, not a flat picture. - **Images (PNG, JPEG, WebP) for everything else.** Every Motiff tier can export images. They won't be editable geometry on the other side, but they're a faithful record of the design and a solid reference for rebuilding. Whatever you pull down, keep it organized by screen or project so it's easy to find later. That's the whole job for now: get the bytes off Motiff's servers while you still can. ## 3. Getting your designs into Brilliant Here's the honest part. **Brilliant does not import Motiff files directly.** There is no Motiff importer, and because Motiff's format was never open or documented, there's nothing to convert straight across. Anyone who tells you a Motiff-to-anything migration is lossless and automatic is selling you something. What's real are three paths, and most moves use a mix of them. ### Through Figma This is the best path if it's open to you. If your work already lives in Figma too, or you can get a Figma copy out of Motiff before the deadline, Brilliant imports Figma files directly, and a lot comes across intact: auto layout, vector paths, boolean operations, arcs and rings, per-corner radii, solid and gradient fills, image fills with their bytes, drop shadow and blurs, text down to per-character overrides, and components and instances with their variant axes as real editable component sets. Some things don't survive the wire, and they're written down rather than discovered later: non-variant component properties, interactive prototypes, and the bindings back to Figma Variables and shared styles (the resolved values come through, the links don't). The full walkthrough and fidelity notes are in [Import from Figma](/docs/import/from-figma). ### Through SVG and images For individual assets, export them from Motiff and bring them straight in. SVG imports as native, editable elements you can keep working on. Images (PNG, JPEG, WebP, and more) import as image fills. This is the reliable way to preserve logos, icons, and illustrations even when a whole-screen import isn't on the table. ### Rebuild with agents Where a clean import isn't possible, Brilliant is built for the rebuild. It's an MCP server as much as an editor, so an agent like Claude Code or Cursor can work directly on the canvas. Point it at one of the screenshots you exported, or describe the screen, and it builds it out as real, hand-editable vector elements, never a flattened image. Every canvas command it runs is undoable, so you can steer and revert freely, and MCP is unlimited even on the free plan. Be clear-eyed about what this is: a re-creation, not a byte-for-byte transfer. The agent rebuilds from what it can see and what you tell it, and you finish the details by hand. For a lot of Motiff work, especially screens you were going to touch up anyway, that turns out to be faster than it sounds, and you land on a file you fully own. ## 4. What you'll be trading Two honest concessions, so nothing catches you out after you've moved. - **Variable modes.** Motiff's editor historically supported up to 99 modes per variable collection, on every plan. Brilliant's [design system](/docs/design-system/overview) does tokens, modes, and brands, but not at that count. If you leaned on a large stack of modes, plan to consolidate. - **Interactive prototypes.** Motiff's editor shipped clickable prototyping. Brilliant is a static canvas today, with no transitions or interactive hotspots (it's on the roadmap). If wiring up flows is central to your work, know that going in. ## 5. Why Brilliant is a safe place to land The reason to move here, more than any single feature, is the thing that just went wrong with Motiff can't happen the same way. - **Your files are yours, on your disk.** A Brilliant workspace is a plain folder on your machine. Each canvas is a plain-text `.bl` [Blueprint file](/docs/workspace/canvases-and-files), images sit in `Assets/` directories beside them, and there's no hidden project database. Nothing uploads until you choose to publish. A folder on your own drive can't be discontinued out from under you, and because the files are plain text they drop straight into Git: branch them, review them, `git blame` a color change. Even a project you publish to brilliant.design is a real Git repository you can clone. - **The design tool is free, for real.** Every design feature, unlimited public projects, unlimited MCP, unlimited export, and version history cost nothing on the free tier. It's the whole tool, not a trial, which means you can move a project over and work in it for a week before committing to anything. - **Agent-native, with your own keys.** Unlimited MCP on every plan, agents driving the canvas through more than a hundred undoable commands, and your own AI, so you bring your own API key and Brilliant never hosts or proxies the inference. See [Connect an agent](/docs/agents/overview). - **No lock-in on the way out, either.** Brilliant imports from Figma and has a Send to Figma path back, so you're never trapped here the way a cloud tool can trap you. Brilliant is a static canvas today, so if you need clickable prototypes it isn't a full replacement yet. On everything else, you land on a real, professional editor whose files you actually own. ## Next - **The actual Figma import, step by step:** [Import from Figma](/docs/import/from-figma). - **How your files work on disk and in Git:** [Canvases and files](/docs/workspace/canvases-and-files). - **Point your AI tool at the canvas:** [Connect an agent](/docs/agents/overview). - **How Brilliant compares as an editor:** [Brilliant vs Figma](/docs/compare/figma). --- Source: https://brilliant.design/docs/solutions/product-designers.mdx # Brilliant for product designers You already know how to do the work. What you don't have is a way to hand off the mechanical half of it without leaving the file. Brilliant is a full professional design tool with AI agents built into the canvas: chat sessions run in parallel, the first ten answer to `Cmd+1` through `Cmd+9` and `Cmd+0`, and each one carries its own model, history, and undo stack. Everything an agent makes is a real vector. Nudge it, recolor it, or throw it away, exactly like something you drew. ## The primitives are the ones you have Auto layout, components and instances, vector editing, the pen tool, boolean operations, design tokens, typography controls, frames, groups. Sit down and design by hand and you get near-complete feature parity with Figma, on macOS and Windows. ![The Brilliant editor: canvas in the middle, file and layer explorers on the left, property inspector on the right|75%x](/docs/images/full-ui-overview.webp) One habit does change. When your selection spans more than one frame, Brilliant draws a separate rectangle per frame, each with its own handles, and every operation runs once per frame in that frame's own space. Align Left lines up Frame A's children among themselves and Frame B's among themselves, never across the boundary. It's deliberate, and it makes multi-frame edits behave sanely. Flip the toggle below between the per-parent model and the flat one you're used to, on the same selection across two frames. The blue outlines are drawn to stand in for the real selection chrome. A few things you'd normally reach for a plugin to get are native. Brilliant ships a real GPU shader stack as first-class canvas fills (`metaballs()`, `metal()`, `irid()`, `steel()`) that render live while you design, compose with the rest of your effects, and export cleanly to raster or video. ## Agents that work in the file, not next to it Press `/` anywhere on the canvas to focus the AI input, then describe the outcome. The agent streams elements on as it builds, so you watch the layout come together instead of waiting for a result. It reads your selection, so you can click a button and say "make this a warmer orange" without naming anything. It runs Brilliant's own commands, over a hundred of them, through the exact path a keyboard shortcut takes. And it works in your design system: ask for tokens and it paints with `color.surface` and sizes gaps with `spacing` roles instead of hard-coded values. The composer is built for pointing at things rather than describing them. `@` mentions attach an element by name, with its structure and a render. Paste a screenshot, drag a file in, or use the paperclip. `#` opens a dropdown of style modifiers (dark, mobile, minimal). Step back through past prompts with the arrow keys, or reverse-search them with `Ctrl+R`. ![A pricing section an agent built, live and editable on the canvas|75%x](/docs/images/ai-before-after.webp) The chat is bring-your-own-key. Requests go from the app straight to Anthropic, OpenAI, Google, or OpenRouter, or through a Claude Code subscription you already have. Nothing routes through Brilliant's servers. If you'd rather not leave your editor, the same canvas is reachable from Claude Code, Cursor, Codex, Windsurf, Antigravity, or anything else that speaks MCP. Same canvas, same undo stack, and plenty of people keep both. ## Run several directions at once Start a new session with `Cmd+N` while the chat is focused and prompt an alternate take there. Old sessions stay live, so you can jump between parallel experiments and keep the one that works. Each chat also keeps its own undo stack, separate from the canvas's main history. You can keep editing by hand while an agent runs, and roll back a specific turn with the **Revert** control on its card without touching your own edits. ## Your files are files A workspace is a folder on disk. Each canvas is a plain-text Blueprint `.bl` file, images live in `Assets/` beside them, and there's no save button because Brilliant autosaves continuously. Because the files are plain text, they drop straight into Git: branch a canvas to try a direction, review it in a pull request, `git blame` the commit where the hero color drifted off-brand. Undo is per canvas and follows you around: `Cmd+Z` undoes the active canvas's last action, and switching canvases never clears it. The file explorer keeps its own separate undo stack for create, rename, delete, move, and reorder, so a mis-drag in the tree is one keystroke away from being fixed. ## What isn't here Brilliant is a static canvas today, with prototyping on the roadmap. There's no prototype mode yet: no frame-to-frame transitions, no clickable hotspots, no smart animate. If a click-through prototype is the thing you can't ship without this week, that one step stays in your current tool; the rest of the work, the editor, the tokens, the agents, is here. ## Next - **Map your Figma habits over:** [Coming from Figma](/docs/getting-started/coming-from-figma). - **See the full range of the agent:** [What the AI can do](/docs/ai/what-the-ai-can-do). - **Run parallel chats and revert turns:** [Sessions and chat](/docs/ai/sessions-and-chat). - **Learn the canvas:** [Quick Tour](/docs/getting-started/first-steps). --- Source: https://brilliant.design/docs/solutions/product-managers.mdx # Brilliant for product managers The gap between a written spec and something a team can react to is usually a week and somebody else's calendar. Brilliant closes it to one prompt: describe the outcome in plain language and the agent builds it on the canvas as native, editable elements. Your first prompt just works on Brilliant AI with no setup, on the usage Free includes every month, which is enough to get a first draft in front of people (Free also includes 10 built-in chat messages a day on your own key). What comes back is a real frame with real text and button elements, not a flattened picture. ## Write the outcome, not the steps Press `/` on the canvas to focus the AI input and describe what you want. Don't spell out tools or steps, just the result. > Create a landing page hero for a habit-tracking app called Momentum. A big headline, one line of supporting copy, a primary "Get Started" button and a secondary "Watch Demo" button, and room for a product screenshot on the right. The agent streams the hero on as it builds it. When it finishes, every layer is yours to edit. ![The hero the agent built, live and editable on the canvas|75%x](/docs/images/tutorial-hero-result.webp) ## Iterate in deltas Follow-ups see the whole conversation and the current canvas, so you speak in changes rather than re-describing the thing: > Rewrite the headline to be punchier and under six words. > Add more breathing room between the headline and the buttons. Describe the problem, not the fix. "The buttons feel cramped" works as well as an exact spacing value. Click an element first and you can just say "this": the agent reads your selection. When the spec is a picture rather than a paragraph, paste it in. Screenshots, competitor shots, and a whiteboard photo all ride along as attachments, and `@` pulls in an element you already have so the new thing matches it. ![The chat panel alongside the design taking shape on the canvas|75%x](/docs/images/chat-step2.webp) ## Hand it over without a handoff Because the output is native elements, a designer picks it up and keeps working instead of rebuilding it. There's no import step and nothing is flattened. The same is true downstream: an engineer's agent can read the canvas directly, so the artifact you make is the one the team builds from. When you want eyes on it, publish the project to brilliant.design and the link is on your clipboard. Public is the default and it's free. Visitors get a view-only editor in the browser: they can pan, zoom, select, inspect properties, and copy elements out, but they can't change your work. For one piece rather than a whole project, publish a **drop**: a single selection with its own clean URL, ready to paste into a thread. ## Mark the versions that mattered A **checkpoint** is a named, saved version of a published project, created on purpose from the bookmark button in the top island or the **Create Checkpoint** command. Autosave keeps your work safe continuously, but autosave never mints a checkpoint: those are the moments you decide are worth marking. Your checkpoints are listed at `brilliant.design/{handle}/{project}/history`, with no cap and no retention limit. Restoring an older one rolls forward rather than erasing, so you can go back, look around, and come forward again without losing anything. If you'd rather reviewers see only polished versions, switch the project's audience to **Checkpoints only** and keep working behind it. ## What this isn't Brilliant is a design canvas, not a doc tool and not a prototyping tool: there are no click-through flows, transitions, or hotspots yet, with prototyping on the roadmap. The built-in AI beyond the Brilliant AI Free includes every month and the 10-a-day cap on your own key is a paid feature: more Brilliant AI usage comes with the monthly plans (Personal, Pro, Max), and your own AI is unlimited on any paid plan. And keeping a project private is a paid feature too: public work is the free path. It also won't tell you what to build. The agent produces professional structure with real auto layout, components, and tokens, but the judgment about which direction is right stays yours. ## Next - **Send your first prompt:** [Your First AI Chat](/docs/getting-started/first-ai-chat). - **Follow a full walkthrough:** [Design tutorials](/docs/ai/design-tutorials). - **Put it in front of people:** [Publish a project](/docs/platform/projects). - **Share one piece:** [Drops](/docs/platform/drops). --- Source: https://brilliant.design/docs/solutions/ship-to-code.mdx # Ship it into the codebase Handoff is a document problem pretending to be a design problem. Brilliant skips the document: your coding agent reads the canvas directly over a local MCP server, and if you'd rather move bytes than context, the selection exports as HTML in three flavors, React JSX, SVG, PDF, or raw CSS properties. Two routes to the same place. Most people use both. ## Route one: your agent reads the design Brilliant runs a local MCP server the whole time the app is open, at `http://127.0.0.1:3333/mcp`. Claude Code, Cursor, Codex, Copilot, Windsurf, Antigravity, and anything else that speaks MCP connect with one toggle, and MCP usage is unlimited on every plan. Once connected, the agent can read structure, search across canvases by name, text, type, color, or component, and export any element for itself. So the prompt is the whole handoff: > "Build the settings panel from the Dashboard canvas in our React components. Match the spacing and type." ![A canvas built and read by Claude Code over MCP|75%x](/docs/images/agent-claude-code-result.webp) This is the route that adapts to your stack. The agent already knows your repo's conventions, so it writes your components, your framework, your constraints, rather than generic markup. It also runs the other way. The same connection lets an agent create and edit designs from your editor, so a component you changed in code can be reflected back on the canvas without anyone opening a design tool by hand. ## Route two: export the markup When you want a concrete artifact, the Export panel in the right toolbar and the right-click **Export as** and **Copy as** menus cover it: | You want | Take | |---|---| | Markup to adapt | **HTML** (snippet, full document, or flex) | | A component to paste into a `.tsx` | **React (JSX)**, the same markup with JSX style objects | | Icons and illustrations | **SVG**, with native filters for drop shadow, outer glow, and layer blur | | Just the values | **CSS** properties: size, position, colors, border, radius, rotation, text | | The design itself | **Blueprint**, full hierarchy, pasteable back into Brilliant or into an AI tool | `Cmd+E` exports a PNG for a ticket or a review thread. Everything else lives in the palette, the panel, or the right-click menu. The `+` button in the Export panel header adds another config row, so PNG at 1x, PNG at 2x, and an SVG can ship in one click. For pixel-clean UI shots, reach for PNG rather than WebP: the by-hand UI exports WebP lossy at quality 90, which leaves faint gray banding on rounded corners and color ramps. ## Tokens are the shared vocabulary The reason generated markup is worth anything is that the design was built on named decisions rather than hex codes. Brilliant writes a resolved artifact at `Styles/.gen/default.gen.yaml` on every save, expanded to concrete values, and that's the file Style Dictionary, Tokens Studio plugins, and build scripts read. Your design tokens and your code tokens can be the same tokens. ## Designs live in Git already `.bl` files are plain, deterministic Blueprint text, so a design change reviews like a code change: branch it, diff it, comment on it in a pull request, `git blame` the line where a color drifted. A published project is also a real Git repository you can clone. ## What this is not Brilliant does not compile your app. It generates HTML and React from vector shapes, so that output is a starting point to adapt, not a build artifact, and it won't carry your state, your routing, or your data layer. Brilliant components are design components: they don't run your app code, and you can't drop a live React component onto the canvas. The agent is the piece that bridges design intent to your actual stack, which is exactly why the MCP route beats the export route for anything real. ## Next - **Every format, both directions:** [Import and export](/docs/workspace/import-and-export). - **Connect your editor:** [Use with AI agents](/docs/agents/overview). - **The language your agent writes:** [The Blueprint DSL](/docs/developers/blueprint). - **Keep design and code on one vocabulary:** [Design system overview](/docs/design-system/overview). # Section: Comparisons --- Source: https://brilliant.design/docs/compare/figma.mdx # Brilliant vs Figma Figma set the standard for the last decade of product design. Canvas in a browser tab, cursors on a link, a component model an entire industry learned by heart. Any honest comparison starts there. Brilliant is a full professional design tool in the same mold. Auto layout, components, instances, vector editing, pen tool, boolean operations, design tokens, design systems, typography controls, frames, groups, all of it. If you sit down and design by hand, you get near-complete feature parity with Figma. Same muscle memory, same primitives, same final output. What Brilliant adds is a team of AI agents that work directly on the canvas alongside you. You're not trading one tool for a smaller one. You're getting the same tool, with superpowers. ## Agents that design alongside you Brilliant gives you a team of AI agents that do real design work on the canvas. Prompt them in plain English and they create elements, rebuild layouts, swap design tokens, try typography, pull in brand assets, and generate variations on what you're already working on. Every element they produce is a real vector you can nudge, recolor, or throw away. You stay the creative director. They handle the work you'd rather not. The output is professional. Auto-layout rules, components and instances, design tokens, effects. Agents use the same primitives you do, so what they hand you fits into the rest of your file. Accuracy is high enough that you're editing their work, not rescuing it. [Read the architecture →](/blog/blueprint) Figma's agent story runs the other way. You configure an external agent in Claude Code or Cursor, it talks to Figma through a partial MCP server (read is free; write is in beta on the paid remote server), and the output lands from outside. Brilliant's agents work from inside the canvas, in the same tool you're already in. No setup needed either way. Click the AI input in the bottom toolbar, bring your own key for Anthropic, OpenAI, Google, or OpenRouter, or sign in with your Claude Code subscription. If you'd rather drive Brilliant from your editor (Claude Code, Cursor, Codex, Windsurf, Antigravity, Lovable, anything that speaks MCP), that works too. You set the vision. They execute. You override. ## Free, all of it Figma is a seat-based SaaS. You pay per editor, you pay extra for developers who want to look at your work through Dev Mode, and the price climbs with headcount. Brilliant is free. The full design tool. Auto layout, components, vector editing, effects, design systems, tokens, animated GPU shaders, full export (PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay), Figma import, unlimited MCP usage. Bring your own AI key. Not a trial, not a stripped-down version. Free. Monetization lives in the integrated AI chat and in keeping your work private. Free gives you 10 AI messages a day and unlimited public projects. Personal lifts the daily cap and unlocks private projects, drops, and share links. Team adds pooled storage, unlimited editors on private work, user management, and one invoice for the group. Same model that's worked for developers on GitHub and VS Code for over a decade. You don't pay rent on the act of writing code, and you shouldn't pay rent on the act of moving elements on a canvas. ## Your designs, your disk, your git Figma files live on Figma's servers in `.fig`, a proprietary closed format you access through a browser tab. Version history is linear and vendor-controlled. Brilliant files are plain-text Blueprint on your local disk. They end in `.bl`, and they diff cleanly in git. Branch a canvas to try a direction. Commit the experiment. Revert it if it doesn't work out. Run `git blame` to find the commit where the hero color drifted off-brand. Every primitive your codebase already uses is now available for your designs: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` "GitHub for design", visual design-file diffs, branching, pull-request review, publishable component libraries, is the next chapter of Brilliant. The primitives (plain-text files, git compatibility) are here today. ## Superpowers that actually render Figma gives you fills, strokes, and a handful of fixed effects. Anything richer, animated gradients, metal, iridescence, glass, shader art, lives outside the tool or behind plugin gymnastics. Brilliant ships a real GPU shader stack as first-class canvas fills. `metaballs()`, `metal()`, `irid()`, `steel()`. They render live while you design, compose with the rest of your effects, and export cleanly to raster or video. A professional 2D vector design tool, with superpowers. ## Feature for feature Every primitive you reach for in Figma has its equivalent here, driven with the same muscle memory: - **Auto layout** with direction, hug, fill, and fixed sizing, gap, padding, wrap, alignment, and layout grids. - **Components and instances**, with component sets, variant properties, slots, override detection, and cross-canvas instances. - **Vector editing** with a pen and pencil, a full node-and-handle edit mode, boolean union, subtract, intersect, and exclude, flatten, and outline text. - **Frames, groups, and masks**, with clip-content and per-corner radii. - **A full design-token system**: eleven-step OKLCH color ramps from one seed, light and dark theming, modes as transforms, and multi-brand overlays, resolved to a file Style Dictionary and Tokens Studio read. - **Typography** down to per-character overrides for mixed-style runs. - **Fills, strokes, gradients, and effects**: linear, radial, and angular gradients, image fills, drop shadow, inner shadow, glows, layer and background blur, and blend modes. - **Ten export formats**: PNG, JPEG, WebP, SVG, PDF, HTML in three flavors, React JSX, MP4, MOV, and Replay. - **Figma import** by URL, page, or node-id subtree, plus Send to Figma for the trip back. Sit down and design by hand and the primitives, the shortcuts, and the final output match. What is new is everything around them. ## What Brilliant has that Figma doesn't - **Agents that work on the canvas alongside you.** Not a prompt box next to it. A team that creates real vectors, applies your design system, and iterates in the same file you're already editing. - **A free, full-featured design tool.** No seat tax, no Dev Mode upsell, no gated primitives. - **Plain-text, git-friendly files.** Blueprint on disk, clean diffs, blameable history. - **Native GPU shaders.** Metaballs, metal, iridescent, steel as first-class fills. - **Ten export formats including code and video.** PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay. - **Unlimited MCP usage on Free.** Your external agent can drive Brilliant without a rate limit. ## The honest gaps The one capability Figma ships that Brilliant hasn't shipped yet is interactive prototyping: the mode that wires frames into clickable flows. Brilliant is a fully editable static canvas today, with prototyping on the roadmap, and everything you'd build a prototype from (frames, components, variants, auto layout, tokens) is already here at parity. If a click-through prototype is the one artifact your week turns on, that single step still lives in Figma; everything around it is already here, with the agents, the plain-text files in git, and the shaders that Figma doesn't have. ## At a glance | | Brilliant | Figma | |---|---|---| | Design tool pricing | Free forever | Paid seats | | Developer access | Free | Separate paid Dev Mode seat | | AI integration | Built-in chat + external MCP, agents work directly on the canvas | External MCP only; write is beta and paid | | File format | Plain-text Blueprint, git-native | Proprietary `.fig`, cloud-only | | Version control | Native git (branch, diff, rollback, blame) | Linear version history | | GPU shaders | Native (metaballs, metal, iridescent, steel) | None | | Export formats | PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay | PNG, PDF, SVG + Dev Mode code | | Vector editing, auto layout, components, tokens | Full | Full | | Component variants | Yes (component sets) | Yes | | Interactive prototypes | Roadmap | Yes | | Live multiplayer | Yes, on published projects | Yes | ## A new dance If Figma is the standard, Brilliant is the question the standard is no longer the right answer to. One designer directing thirty agents. Design files in git. A full professional design tool that doesn't ask for rent. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/framer.mdx # Brilliant vs Framer Framer is one of the most impressive products in this space, and the comparison only works if you start by admitting what it is. Framer is an AI website builder. You design, you publish, and what comes out the other end is a live site running on Framer's own infrastructure, with a CMS, SEO controls, and performance services Framer manages for you. Its own headline calls it "the design agent for every step from idea to launch." If a hosted, published website is the thing you want, Framer is very good at delivering exactly that, and this page is not going to pretend otherwise. Brilliant is a different kind of tool. It's a professional 2D vector design tool, in the Figma mold, whose output is your own files on your own disk. Auto layout, components and instances, vector editing, boolean operations, design tokens, typography, shaders. What you make is a plain-text design file you own, not a site Framer hosts. The two tools point in different directions, and the honest way to choose between them is to know which direction you need. *Framer facts on this page are current as of August 2026. Framer moves fast; where a detail matters, check it against Framer's own docs.* ## Design that publishes a site, versus design that hands you files Framer's great strength is also the line where the two tools part. A Framer project is a hosted website, not a file you can take with you. Framer states this plainly on its own help page about exporting, and the wording is worth reading directly: > "Framer does not offer HTML export for self-hosting." The reason is architectural, and again in Framer's words: many of its "performance and optimization features rely on backend services running on Framer's infrastructure," and "These services cannot be replicated through a simple HTML export." Framer even names the person it's the wrong tool for: > "If you require complete control over hosting infrastructure or need to export source code, Framer may not be the right solution for that workflow." That's a fair and clear statement of a real boundary, not a knock. If you want a hosted site, the boundary never bites. If you want the artifact, it's the whole story. Brilliant is built for the artifact. A workspace is a folder on your disk. Each canvas is a plain-text Blueprint file ending in `.bl`, and Brilliant exports to ten formats, including HTML in three flavors and React JSX, when you want code out. Nothing leaves your machine until you choose to publish. [Your designs live where your code lives →](/docs/workspace/canvases-and-files) ## Your designs, your disk, your git Framer keeps your project in its cloud, versioned by a hosted Branching feature. Branching is a genuine strength and worth naming: you can work a change on a branch and merge it back. But it's a hosted concept, not Git, and there's no plain-text file underneath it to diff. Brilliant files are plain-text Blueprint on your local disk. They end in `.bl`, and they diff cleanly in git. Branch a canvas to try a direction. Commit the experiment. Revert it if it doesn't work out. Run `git blame` to find the commit where the hero color drifted off-brand. Every primitive your codebase already uses is now available for your designs: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` Blueprint is an open, documented, versioned language with a formal spec. And a published Brilliant project is itself a real Git repository with a clone URL, so "GitHub for design" isn't a metaphor here. ## Agents: an open protocol, versus a bridge Framer built on purpose Both tools are built around agents now, and here the designs diverge in a way worth stating precisely rather than scoring. Framer chose not to ship MCP. It built a proprietary bridge instead, the `@framer/agent` package, and it says so directly: > "It works differently from MCP under the hood, but it gives you the same functionality an MCP server would, done better." External agents like Claude Code and Codex connect through that package and reach the same capabilities as Framer's built-in agent, though Framer notes trade-offs in real-time streaming and canvas-context awareness, and its docs steer external agents toward one-off edits rather than sustained design work. Framer's agents also reach well past the canvas, into CMS, analytics, domains, and DNS, which is a wider operational surface than a design tool's agent has any reason to touch. That reach is real, and you should weigh it. Brilliant runs a standard local MCP server at `http://127.0.0.1:3333/mcp` the whole time the app is open, documented for Claude Code, Claude Desktop, Cursor, Codex, Copilot, Windsurf, and Antigravity, plus Gemini CLI, Zed, and OpenCode. It's unlimited on every plan, including Free. An external agent drives the same canvas commands you do, over a hundred of them, each with full undo, and everything it makes lands as real, hand-editable vector elements. Whether you prefer a vendor's bridge or an open protocol is a genuine choice; Brilliant's bet is the open one, stated without calling Framer's the wrong one. And the AI is yours either way. Brilliant AI is included in every plan, and you can bring your own AI instead (Anthropic, OpenAI, Google, OpenRouter, a Claude Code or Codex subscription, or a local endpoint); on your own AI Brilliant takes no markup. Framer meters AI in its own credits. [Bring your own AI →](/docs/ai/providers-and-models) ## A design system, not three Style types Framer's token layer is Styles, and there are three of them: Color, Text, and Link, with a two-value Light and Dark axis on color. It's clean and it covers the common case. What it isn't is a general token system with numeric scales or multiple modes. Brilliant compiles a `Styles/*.ds` source into a full design system: eleven-step OKLCH color ramps from a single seed, light and dark theming, modes as transforms, multi-brand overlays, and a cascade across folders. The resolved output is a `.gen.yaml` that Style Dictionary and Tokens Studio can read, so the tokens your designers set are the tokens your build reads. [How the token system works →](/docs/design-system/overview) ## What Brilliant has that Framer doesn't - **Your files, on your disk.** Plain-text Blueprint that diffs in git, exported to ten formats including HTML and React. No hosted-only lock-in. - **A standard MCP server, unlimited on Free.** An open protocol any MCP client can drive, not a proprietary bridge, and no credit meter on agent access. - **Brilliant AI included, or your own AI with zero markup.** Anthropic, OpenAI, Google, OpenRouter, local endpoints, or a Claude Code and Codex subscription. Framer meters AI in its own credits. - **A full design-token system.** OKLCH ramps, modes, brands, and a resolved file Style Dictionary and Tokens Studio read. Framer stops at three Style types. - **Native GPU shaders as first-class fills.** Metaballs, metal, iridescent, steel, composed with your effects and exported to raster or video. - **A desktop app on macOS, Windows, and Linux** with your work on local disk, not a browser tab over someone else's servers. ## The honest gaps Framer does a set of things Brilliant doesn't, and most of them follow from the one difference this whole page is about: Framer's output is a hosted website, Brilliant's is files you own. So Framer ships a published site with a CMS, SEO controls, and managed performance, where Brilliant hands you design files and code and leaves the hosting to you. It ships production motion and interactive prototyping (hover and drag gestures, scroll effects, parallax, page transitions), where Brilliant is a fully editable static canvas today with prototyping on the roadmap and every frame, component, and token you'd prototype from already in the file. It also has breakpoint-based responsive design across Desktop, Tablet, and Mobile, shaders that accept video, a built-in editor for live React components, and agents that reach past the canvas into CMS, analytics, domains, and DNS. If a hosted site with a CMS, or a clickable prototype, is what you're shipping, that work lives in Framer; if what you want is the artifact (in git, drivable by an open protocol, with the AI bill in your own name), everything for that is already here. ## At a glance | | Brilliant | Framer | |---|---|---| | What it makes | Design files you own | A hosted website on Framer's infrastructure | | Code / self-host export | HTML (3 flavors), React, SVG, PDF | None; "Framer does not offer HTML export for self-hosting" | | File format | Plain-text Blueprint, git-native | Cloud-only, no documented file format | | Version control | Native git (branch, diff, rollback, blame) | Hosted Branching, no git | | AI access | Brilliant AI included, or your own AI; standard MCP, unlimited on Free | Proprietary `@framer/agent` bridge, credit-metered, no bring-your-own-AI | | Design tokens | Full system (OKLCH ramps, modes, brands) | Three Style types (Color, Text, Link) | | GPU shaders | Native (metaballs, metal, iridescent, steel) | Yes, and they accept video | | Interactive prototypes | Roadmap | Yes, with production motion | | Responsive breakpoints | No | Yes (Desktop, Tablet, Mobile) | | Hosted site + CMS + SEO | No | Yes | | Platforms | macOS, Windows, Linux desktop + web | Web + desktop apps (macOS, Windows) | ## A new dance Framer and Brilliant aren't really the same product with different prices. Framer turns a design into a hosted site and manages everything under it. Brilliant turns a design into files you own and hands them to you, agents included. If you're shipping a website and want the hosting handled, Framer is a fine answer. If you want the artifact, in git, drivable by an open protocol, with the AI bill in your own name, that's the tool Brilliant is trying to be. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/lunacy.mdx # Brilliant vs Lunacy Lunacy is the closest thing in this comparison to Brilliant's own shape, and the page only works if it says so up front. It's a free, native desktop vector editor from Icons8 that runs on Windows, macOS, and Linux, opens and saves `.sketch` files natively, and stores its own work in `.free`, an open, MIT-licensed format with a public spec. It has a real pen tool, boolean operations, auto layout with hug, fill, and fixed sizing, components, prototyping, and, unusually for a free tool, a write-capable MCP server it shipped back in mid-2025. If you want a genuinely free, cross-platform desktop editor with a Sketch pipeline, Lunacy is an excellent answer, and this page is going to concede that generously before it draws any lines. Brilliant's editor is free too: the full tool, on the free tier, with paid plans covering AI chat volume and private cloud work rather than editor features. Where it differs is the aim. It's built agent-native from the start: AI that runs on Brilliant AI out of the box or on your own key with a model you choose, a full design-token system rather than color presets, plain-text files that diff in Git without unzipping, and Figma interop that goes both ways. This is a comparison between two tools that respect the same values and split on a few specific ones, so we'll be precise about the splits. *Lunacy facts on this page are current as of August 2026, checked against Lunacy Version 14.* ## Two tools that share a lot Lunacy and Brilliant overlap more than most pairs here: both are free, native desktop editors on Windows, macOS, and Linux, both keep an open file format, and both let an agent drive the canvas. Lunacy's real edge is its Sketch pipeline: it opens, edits, and saves `.sketch` natively on Windows and Linux where Sketch itself won't run, and its `.free` format is MIT-licensed with a public spec and a reference implementation. Brilliant answers with a full design-token system, Brilliant AI or your own, plain-text files that diff in git without unzipping, and Figma interop both ways; the honest gaps get one compact section near the end. On price the two match, and Icons8 says it plainly: "there's no hidden charges, ads, or locked features," and Brilliant's full editor is free the same way, with paid touching only AI chat volume and private work. On cloud collaboration the free tiers tilt Brilliant's way: Lunacy's paid tiers meter cloud storage and team seats, while Brilliant's free tier includes unlimited public cloud projects with unlimited collaborators. ## Agent access: the same idea, a different shape and safety story Lunacy is unusually far ahead of most free tools here, and it earns credit for it. Its local MCP server shipped in June 2025 and became write-capable the next month, with a release note reading "Now LLM can edit Selection over MCP." It also exposes a local HTTP automation API with example plugins in five languages. For a free product, that's a serious agent surface, and it predates most of the competition. The differences are about shape and safety rather than whether the capability exists. Lunacy documents its MCP client setup for Claude Desktop only, and it publishes no exact MCP tool names, so the surface can't be fully audited from the docs. Its HTTP automation API, by its own reference, runs on localhost with no authentication and no rate limiting ("Current implementation places no restrictions on request frequency"). Brilliant documents seven agent clients with step-by-step setup, from Claude Code and Cursor to Codex, Copilot, Windsurf, and Antigravity, publishes exactly what an agent can do, keeps MCP unlimited on every plan including Free, and drives the same hundred-plus canvas commands you use, each with full undo. Both tools let an agent write your canvas; they differ in how much of that surface is documented and gated. [What an agent can do in Brilliant →](/docs/agents/overview) ## Brilliant AI or your own, and a real design-token system Lunacy's built-in AI Chat is a nice convenience with a specific trade-off. It offers Claude, Gemini, OpenAI, and Grok with "No API keys required, free to use," which is friendly, but it means Icons8 sits between your prompt and the model, and the feature currently has no documentation page at all. Brilliant gives you both lanes: on Brilliant AI the request routes through Brilliant to Google, and on your own AI, across six providers plus any OpenAI-compatible or local endpoint with per-chat model and thinking-level selection, nothing sits between your prompt and the model. Brilliant takes no markup on either one. The bigger gap is tokens, and Icons8 is candid about it. Lunacy's variables are "reusable solid color presets," with no number, string, or dimension variables and no modes or theme switching. Icons8 lists the fix as open roadmap: "We'll gradually implement full support for variables, including themes, groups, theme switching, field binding, and more." Brilliant compiles a `Styles/*.ds` source into a full system today: eleven-step OKLCH ramps from one seed, light and dark theming, modes as transforms, multi-brand overlays, a cascade across folders, and a resolved `.gen.yaml` that Style Dictionary and Tokens Studio read. Component reuse differs the same way: Lunacy's "component states" are a single-axis list, where Brilliant ships full component sets with variant properties and slots. [How the token system works →](/docs/design-system/overview) ## Two open formats, one that diffs without unzipping This is the closest call on the page, and it needs careful wording, because Lunacy's format is genuinely open and Brilliant is not the only tool that can say so. `.free` is open source under MIT, with a public spec and a reference implementation, and Icons8 designed it to be "compact, human-readable, back-compatible." That's real, and it's more than most tools offer. But the file as it sits on disk is a ZIP archive: pages, images, fonts, and metadata as JSON inside a compressed container. You can unzip it and read the JSON, but you can't `git diff` it in place, and no Icons8 page documents a Git or plain-text-on-disk workflow. Brilliant's `.bl` files are plain, deterministic Blueprint text, one element per line, stable ordering, inline coordinates, hex colors, with nothing to unzip: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` You can diff two versions and read the change, branch a design like a feature, and `git blame` a stray color, and a published Brilliant project is itself a real Git repository with a clone URL. Both formats are open. The narrow, honest difference is that one diffs in Git as it lives on disk and the other has to be unzipped first. [Your designs live where your code lives →](/docs/workspace/canvases-and-files) ## Figma, and the way back Lunacy's strongest interop is Sketch, which it reads and writes natively, and it offers an escape hatch outward by letting you take a `.sketch` file into Figma or XD. Figma itself is import-only: Lunacy imports `.fig` files, with Icons8's own caveat that component variants and complex vector shapes may display differently on import, and Figma variables that "won't be functional right away." There's no direct `.fig` export; the only way back to Figma is through `.sketch`. Brilliant imports from Figma by URL, by page, or by node-id subtree, with a published carry-over list and a published list of what doesn't come across, and it also has Send to Figma, so a canvas can round-trip while a team is still deciding which tool to standardize on. It imports `.sketch` with page selection and SVG as native editable elements too. [Coming from Figma →](/docs/import/from-figma) ## What Brilliant has that Lunacy doesn't - **Brilliant AI included, or bring your own AI with model choice and zero markup.** Six providers plus local endpoints, no vendor between your prompt and the model. Lunacy's AI Chat is hosted with no key of your own. - **A full design-token system.** OKLCH ramps, modes, brands, and a resolved file external tools read, versus solid-color variables with no modes, which Icons8 lists as roadmap. - **Component sets with variant properties and slots**, where Lunacy's "component states" are a single-axis list. - **Plain-text files that diff in Git without unzipping.** `.bl` is line-oriented text; `.free` is an open but zipped container. - **Figma interop in both directions.** Import by URL, page, or subtree, plus Send to Figma. Lunacy imports `.fig` only, with no export back. - **A documented, unlimited, write-capable agent surface** across seven named clients, versus an MCP documented for Claude Desktop only with no published tool names. ## The honest gaps The places Lunacy is ahead, each with what Brilliant does instead. Lunacy reads and writes native `.sketch` on Windows and Linux where Sketch itself won't run; Brilliant imports Sketch, Figma, and SVG as native editable elements and can save as a Sketch file, without editing the `.sketch` in place. It ships interactive prototyping with ten triggers and seven actions; Brilliant is a fully editable static canvas today with prototyping on the roadmap. It also bundles Icons8's asset libraries in the editor and runs on a deliberately low hardware floor for older machines. If a native Sketch read-write pipeline or a clickable prototype is the piece your team turns on, that piece lives in Lunacy; on a real design-token system, Brilliant AI or your own, and git-diffable files, Brilliant is the fuller tool. ## At a glance | | Brilliant | Lunacy | |---|---|---| | Price | Free editor; paid for more Brilliant AI usage, private work, teams | Free editor; paid assets, cloud, and seats | | Platforms | macOS, Windows, Linux desktop + web | Windows, macOS, Linux desktop + web | | Agent access | MCP + 100+ undoable commands, 7 documented clients, unlimited on Free | Write-capable MCP, Claude Desktop only, no published tool names | | AI model | Brilliant AI included, or bring your own AI with model choice and zero markup | Hosted AI Chat, no key of your own | | Design tokens | Full system (OKLCH ramps, modes, brands) | Solid-color variables; no modes (roadmap) | | Components | Component sets with variant properties + slots | "Component states," single-axis | | File format | Plain-text Blueprint, diffs in Git without unzipping | Open MIT `.free`, a ZIP of JSON (unzip to read) | | Sketch interop | Import with page selection | Native read and write | | Figma interop | Import (URL / page / subtree) + Send to Figma | Import `.fig` only; no export back | | Interactive prototypes | Roadmap | Yes | ## A new dance Lunacy and Brilliant share more than most pairs in this set: native desktop apps, an open file format, real vector editing, and an agent you can point at the canvas. They split on a short, specific list. Lunacy is the free, cross-platform editor with the strongest Sketch pipeline going, unmatched if that pipeline is your center of gravity. Brilliant is the agent-native tool with the model bill in your own name, a real design-token system, and files that diff in your Git history exactly as they sit on disk. The choice comes down to which of those two shapes fits the way you work. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/paper.mdx # Brilliant vs Paper Paper made a bold bet. If the output of design is HTML and CSS, why not skip the translation entirely and design in the medium itself? Real flexbox, real CSS, real React components dropped onto a canvas, an MCP server that reads JSX out and writes HTML back in. A design tool where the file IS the frontend. It's a clean idea and it ships. Brilliant takes the opposite bet. Models got good. Extremely good. So good that the act of compiling a design into production code, the thing Paper folds into the canvas itself, is something agents now deliver reliably. Not just to HTML and React. To any language, any framework, shaped to your specific constraints and your current codebase. And not everyone is building a website to begin with. Once that's true, "we're HTML and CSS under the hood" stops being a feature and starts being a constraint. The data model is HTML, and that's the product, not an implementation detail. Your design is bounded by what HTML can represent. HTML wasn't built for design-tool scale: tens of thousands of nodes, spatial indexing, hit-testing, 60fps bulk transforms. You can build any feature on top of HTML. You can't build out of HTML. ## Models made "design equals code" obsolete Paper's central pitch is no handoff. You design in CSS, the design IS the code, your developer just opens the file. That argument was strong three years ago. It is much weaker now. An agent in Claude Code or Cursor can take a high-quality vector design and produce production React, Tailwind, SwiftUI, Jetpack Compose, or whatever your stack actually is, in seconds. The handoff didn't disappear, it got automated. Once codegen is a solved problem at the AI layer above your design tool, the case for fusing design and code at the canvas level weakens. ## Agents inside the canvas Paper's agent story runs from outside the canvas. You set up the MCP in Claude Code or Cursor and drive Paper from there. Brilliant ships an integrated AI chat in the canvas itself. Copy-paste any element into the prompt as a reference chip. @mention layers by name. Eyedropper a color from anywhere on screen. Drop a position pin to point at a spot. The orchestrator drops old images and collapses aged tool results automatically, so a long conversation costs a fraction of what an external agent burns through. A streaming composition linter catches mistakes mid-generation, before they hit the canvas: auto layout overflow, wrong sizing modes, invisible text. External MCP still works on every tier, with no rate limit. The chat is the optional layer that makes designing with an agent inside Brilliant cost less in tokens and time than driving the canvas from outside. ## Free, all of it What Paper charges $20 a month for, Brilliant gives away. Not a stripped-down version. Not a trial. The whole tool. Every feature in Brilliant is unlimited on Free. Auto layout, components, vector editing, effects, design systems, tokens, GPU shaders, all ten export formats (PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay), Figma import, unlimited MCP usage. No weekly cap on calls. No paywall on video export. No throttle on file count. The design tool itself, end to end, is free forever. Brilliant monetizes one thing: the optional integrated AI chat. We've built an in-house, tailored agentic loop optimized to give you the best possible design results, and we're so confident in it that we don't monetize anything else in the app. Free includes Brilliant AI every month plus 10 built-in chat messages a day on your own AI; bring your own AI on every tier with no markup. [Here's why you should pay for the integrated chat](/docs/getting-started/why-pay). ## Your designs, your disk, your git Paper hosts your files in its cloud. Version history lives where Paper keeps it. Brilliant files are plain-text Blueprint on your local disk. They end in `.bl` and they diff cleanly in git. Branch a canvas to try a direction. Commit the experiment. Revert it if it doesn't work out. Run `git blame` to find the commit where the hero color drifted off-brand. Every primitive your codebase already uses is now available for your designs: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` "GitHub for design", visual design-file diffs, branching, pull-request review, publishable component libraries, is the next chapter of Brilliant. The primitives (plain-text files, git compatibility) are here today. ## What Brilliant has that Paper doesn't - **A general-purpose vector canvas.** Design websites, mobile UI, illustrations, posters, brand marks, dashboards, video frames. Paper is shaped like a webpage because it IS a webpage. - **Mature design primitives.** Pen tool, boolean operations, skew, flip, design tokens, components with overrides. Paper has auto layout and rotation. - **Plain-text, git-friendly files.** Blueprint on disk, clean diffs, blameable history. - **Ten export formats including code and video, on Free.** PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay. Paper exports React, Tailwind, HTML/CSS, WebP, AVIF, with MP4 gated behind Pro. - **Unlimited MCP usage on Free.** Drive Brilliant from Claude Code, Cursor, or any MCP client without a weekly cap. - **Built-in AI chat: Brilliant AI or your own AI.** Brilliant AI is the default with nothing to set up, or use your own Anthropic, OpenAI, Google, OpenRouter, or Claude Code subscription. No separate plan to unlock the agent layer. ## The honest gaps Two things follow from Paper's core bet that the design IS the code. Paper imports your own production React components and drops them onto the canvas as live primitives; Brilliant's components are design components, editable by hand and by agents, and it generates React and HTML from them rather than running your app code. And in Paper the native format is web elements, so it exports as the exact same HTML and CSS with no translation step, where Brilliant produces code from vector shapes on demand, in three HTML flavors plus React JSX, shaped to whatever stack your agent targets. Both are coherent bets; the difference is whether the file is bounded by what HTML can represent or is a general vector canvas that emits code. ## At a glance | | Brilliant | Paper | |---|---|---| | Elements are | Vectors | HTML | | Pricing | Free forever | Free w/ 100 MCP/wk cap; Pro $20/mo for 1M/wk + video export | | File format | Plain-text Blueprint on local disk | Cloud-hosted | | Version control | Native git (branch, diff, rollback, blame) | Paper's hosted history | | AI agents | Built-in chat (Brilliant AI or your own AI) + MCP; agents output vector primitives | MCP-only; agents output HTML and CSS | | MCP usage | Unlimited, free | 100/wk free, 1M/wk on Pro | | Export formats | PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay (all on Free) | React, Tailwind, HTML/CSS, WebP, AVIF; MP4 on Pro | | Vector tools | Pen, boolean ops, skew, flip | Rotation only | | Design system | Tokens, components with overrides | No | ## A new dance Paper bet that AI would make design and code one thing. Brilliant bet that AI would make design and code so easy to convert between that you don't need them fused. Both are coherent positions. The difference is what you give up to take them. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/penpot.mdx # Brilliant vs Penpot Penpot is the strongest open alternative to the big proprietary tools, and this page is going to say so more than once. It's a genuine professional editor: a real pen tool with bezier nodes, all five boolean operations, layout built literally on CSS Flexbox and CSS Grid, components with variants, native design tokens that follow a W3C standard, and shipping prototyping. It's MPL-2.0 licensed, you can self-host the whole thing, and its file format is open by design. If you want an open platform you can run on your own infrastructure and never be locked out of, Penpot is the answer, and nothing below is an attempt to talk you out of that. Brilliant is not open source, and it won't pretend to beat Penpot on the axis Penpot was built to win. What Brilliant offers is a different shape: a native desktop tool whose agent access doesn't need a browser tab, whose files are plain text that diffs in Git without unzipping, and whose export and Figma interop run wider. This is a comparison between two good tools with different centers of gravity, so we'll concede generously and then be precise about the few places they actually differ. *Penpot facts on this page are current as of August 2026, checked against Penpot 2.17.1.* ## Two tools, two centers of gravity Penpot is built to win on openness, and Brilliant won't pretend otherwise: it's open source, you can host the whole platform yourself, and its tokens follow a W3C standard. Brilliant is proprietary, and trades that for a different shape, a native desktop tool whose agent access needs no browser tab, whose files are plain text that diffs in git without unzipping, and whose export and Figma interop run wider. Both file formats are genuinely open. What each tool has that the other doesn't gets stated plainly as this page goes, and gathered into one compact list of honest gaps near the end. ## Agent access without a browser tab Penpot ships an official, free, first-party MCP server, and that deserves to be stated without hedging: it's a real agent story, it lives in the main repo, and it isn't paywalled. Any page that waved it away would be misleading you. Here's the honest distinction, and it's about architecture, not capability. Penpot's MCP server doesn't talk to a backend directly. It drives a plugin running inside an open Penpot browser tab, and it writes to "the currently focused Penpot page." Penpot documents the consequences itself: the plugin UI has to stay open, a browser suspending an inactive tab can interrupt operations, and recent Chromium private-network rules require an explicit permission prompt. The tool surface is `execute_code` plus four helpers. It can absolutely write to your canvas; it just needs that live, focused tab to do it. Brilliant's MCP server runs inside the desktop app itself, for as long as the app is open. There's no browser tab to keep focused and nothing to suspend. The agent drives the same canvas commands you do, over a hundred of them, each with full undo, and it can create, inspect, edit, search across every canvas in the workspace, and export to ten formats. Both tools keep the AI bill in your hands, too: Penpot ships no model and tells you to "manage the cost of your agents," and Brilliant is bring-your-own-key with no hosted inference. On that philosophy the two agree. Where they differ is the tab. [What an agent can do in Brilliant →](/docs/agents/overview) ## Your designs, your disk, your git This is the closest call on the page, so it gets the most careful wording. Penpot's `.penpot` file is open and documented on purpose. It's a ZIP archive of JSON metadata plus binary media, and Penpot makes a point of it: "your design data is never locked in a proprietary format. You can always unzip a .penpot file and read the JSON to understand what's inside." That's true, and it's more than most tools offer. The caution here is real: Brilliant is not the only tool with an open format, and anyone who tells you otherwise is overselling. The difference is narrower than open versus closed. A `.penpot` file is a binary ZIP, so it's not line-diffable as it sits on disk, and Penpot documents no Git integration. Brilliant's `.bl` files are plain, deterministic Blueprint text: one element per line, stable ordering, inline coordinates, hex colors. You can `git diff` two versions and read the change, branch a design the way you branch a feature, and `git blame` a stray color, all without unzipping anything: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` And a published Brilliant project is itself a real Git repository with a clone URL. Both formats are genuinely open. Only one of them diffs in Git without a step in between. ## Code out, and Figma both ways Two smaller but real differences round out the picture. On code export, Penpot states its own gap: its inspect and export are "currently CSS only but more are coming," with SVG and HTML markup alongside, and no React. Brilliant exports HTML in three flavors, React JSX, SVG, and PDF, and Copy As covers CSS, HTML, React, SVG, and Blueprint. On Figma, Penpot imports through a Figma plugin that packages a file into a ZIP, and Penpot is candid that the path is one-shot rather than incremental, needs manual steps, and "may not be 100% reliable" on features that differ between the tools; prototyping doesn't survive the trip, and there's no documented path back out to Figma. Brilliant imports from Figma by URL, by page, or by node-id subtree, with a published carry-over list and a published list of what doesn't come across, and it also has Send to Figma, so a canvas can round-trip while a team is still deciding. ## What Brilliant has that Penpot doesn't - **Agent access with no browser tab to keep focused.** A desktop-resident MCP server driving over a hundred undoable canvas commands, versus a plugin that writes to whichever Penpot tab is currently focused. - **Plain-text files that diff in Git without unzipping.** `.bl` is line-oriented text; `.penpot` is an open but binary ZIP with no documented Git integration. - **Wider code and markup export.** HTML in three flavors plus React JSX, where Penpot is "currently CSS only." - **Figma interop in both directions.** Import by URL, page, or subtree, plus Send to Figma. Penpot imports one-shot through a plugin and documents no export back. - **Native GPU shaders as first-class fills.** Metaballs, metal, iridescent, and steel, composed with effects and exported to raster or video. - **A native desktop app** on macOS, Windows, and Linux with files on local disk. Penpot is web-first, with no official desktop app and no documented offline mode. ## The honest gaps Four things Penpot has that Brilliant doesn't, each with what Brilliant offers in its place. Penpot is open source and self-hostable across a Docker and Kubernetes matrix; Brilliant is a native desktop app on macOS, Windows, and Linux with your files on local disk and no server to run. Its tokens follow the W3C DTCG standard with math, aliases, sets, and themes; Brilliant's are a first-party DSL that compiles OKLCH ramps, modes, and brands to a resolved file Style Dictionary and Tokens Studio read. It ships interactive prototyping; Brilliant is a fully editable static canvas today with prototyping on the roadmap. And its layout is literal CSS Flexbox and Grid with named areas; Brilliant's auto layout covers the same direction, sizing, and spacing model. If open source or a clickable prototype is what your work turns on, that need points to Penpot; if you want a desktop canvas your agents drive tab-free and files your git reads directly, that's here. ## At a glance | | Brilliant | Penpot | |---|---|---| | License | Proprietary | Open source (MPL-2.0) | | Self-hosting | No | Yes (Docker, Kubernetes, more) | | Agent access | Desktop MCP, 100+ undoable commands, no tab needed | Official free MCP via a plugin in a focused browser tab | | File format | Plain-text Blueprint, git-diffable without unzipping | Open `.penpot` ZIP of JSON, inspectable after unzip | | Version control | Native git (branch, diff, rollback, blame) | Hosted version history; no git integration documented | | Design tokens | First-party DSL (OKLCH ramps, modes, brands) | W3C DTCG (math, aliases, sets, themes) | | Code export | HTML (3 flavors), React, SVG, PDF | CSS, plus SVG and HTML markup; no React | | Figma interop | Import (URL / page / subtree) + Send to Figma | Import one-shot via plugin; no export back documented | | GPU shaders | Native (metaballs, metal, iridescent, steel) | None documented | | Interactive prototypes | Roadmap | Yes | | Platforms | macOS, Windows, Linux desktop + web | Web (cloud or self-hosted); no official desktop app | ## A new dance Penpot and Brilliant want different things, and both are worth wanting. Penpot is the open, self-hostable design platform, unmatched on that axis. Brilliant is the design tool an agent can drive from your editor without a browser tab, with files that live in Git the way your code does. The choice comes down to a short, honest question: do you want the open platform you host yourself, or the plain-text canvas your agents and your Git history both reach directly? [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/sketch.mdx # Brilliant vs Sketch Sketch was the original. Before Figma, before the browser tab, before shared cursors on a link, Sketch was the Mac-native design tool that made product designers feel like they had a home. Fifteen-plus years of craft, an open file format, a thoughtful plugin ecosystem, and a deliberate refusal to turn every workflow into a cloud subscription. Brilliant shares the craft-first, desktop-native philosophy. The difference is where it runs: Brilliant is built in Flutter and ships on macOS and Windows. Sketch is Mac-only, and has been for fifteen years. Same canvas on your desktop, fewer people locked out. Feature-wise, Brilliant is a full professional design tool: auto layout, components, instances, vector editing, pen tool, boolean operations, design tokens, design systems, typography controls, frames, groups, all of it. If you sit down and design by hand, you get near-complete feature parity with what you'd expect from Sketch. Same muscle memory, same primitives, same final output. What Brilliant adds is a team of AI agents that work directly on the canvas alongside you, and they don't work the way Sketch's agents do. ## Agents that design alongside you Brilliant gives you a team of AI agents that do real design work on the canvas. Prompt them in plain English and they create elements, rebuild layouts, swap design tokens, try typography, pull in brand assets, and generate variations on what you're already working on. Every element they produce is a real vector you can nudge, recolor, or throw away. You stay the creative director. They handle the work you'd rather not. The output is professional. Auto-layout rules, components and instances, design tokens, effects. Agents use the same primitives you do, so what they hand you fits into the rest of your file. Accuracy is high enough that you're editing their work, not rescuing it. [Read the architecture →](/blog/blueprint) Sketch ships a local MCP server with two tools for agents: capture the current selection as an image, and execute arbitrary SketchAPI code. In practice, agents have to write Sketch plugins on the fly to do anything. Powerful in principle, but it's asking frontier models to author plugin JavaScript against an API designed for humans. What comes back is code the agent wrote, not design work on your file. No setup needed with Brilliant either way. Click the AI input in the bottom toolbar, use Brilliant AI as it comes, or bring your own AI (an API key for Anthropic, OpenAI, Google, or OpenRouter, or your Claude Code subscription). If you'd rather drive Brilliant from your editor (Claude Code, Cursor, Codex, Windsurf, Antigravity, Lovable, anything that speaks MCP), that works too. You set the vision. They execute. You override. ## Free, all of it Sketch is cheaper than Figma, which is a real credit to Sketch. But it's still a per-seat subscription (or a perpetual license with a paid upgrade path), and the cost still climbs with headcount. Brilliant is free. The full design tool. Auto layout, components, vector editing, effects, design systems, tokens, animated GPU shaders, full export (PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay), Figma import, unlimited MCP usage. Bring your own AI key. Not a trial, not a stripped-down version. Free. Monetization lives in the integrated AI chat and in keeping your work private. Free gives you 10 AI messages a day and unlimited public projects. Personal lifts the daily cap and unlocks private projects, drops, and share links. Team adds pooled storage, unlimited editors on private work, user management, and one invoice for the group. Same model that's worked for developers on GitHub and VS Code for over a decade. Designers shouldn't pay rent on the act of moving elements on a canvas, either. ## Your designs, your disk, your git Sketch files are a credit to their era. Before everyone else went cloud, Sketch kept its files on your disk as `.sketch` archives, which are ZIP bundles of JSON and asset files. You can unzip one and read the JSON, which is more than Figma can say. But you can't `git diff` two versions meaningfully, and you can't branch a design the way you'd branch a feature. The format is inspectable, not collaborative-in-git. Brilliant files are plain-text Blueprint on your local disk. They end in `.bl`, and they diff cleanly in git. Branch a canvas to try a direction. Commit the experiment. Revert it if it doesn't work out. Run `git blame` to find the commit where the hero color drifted off-brand. Every primitive your codebase already uses is now available for your designs: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` "GitHub for design", visual design-file diffs, branching, pull-request review, publishable component libraries, is the next chapter of Brilliant. The primitives (plain-text files, git compatibility) are here today. ## Superpowers that actually render Sketch has a strong effects system for a tool of its era. Fills, strokes, blurs, shadows, and a rich plugin marketplace that extends it further. But animated gradients, metal, iridescence, glass, shader art, have always lived outside the tool. Brilliant ships a real GPU shader stack as first-class canvas fills. `metaballs()`, `metal()`, `irid()`, `steel()`. They render live while you design, compose with the rest of your effects, and export cleanly to raster or video. A professional 2D vector design tool, with superpowers. ## Feature for feature Everything Sketch made designers reach for, Brilliant has its own equal to, on macOS and Windows both: - **Auto layout** with direction, hug, fill, and fixed sizing, gap, padding, alignment, and layout grids. - **Components and instances**, with component sets, variant properties, slots, override detection, and cross-canvas instances. - **Vector editing** with a pen and pencil, a full node-and-handle edit mode, boolean union, subtract, intersect, and exclude, flatten, and outline text. - **Frames, groups, and masks**, with clip-content and per-corner radii. - **A full design-token system**: eleven-step OKLCH color ramps from one seed, light and dark theming, modes, and multi-brand overlays, resolved to a file Style Dictionary and Tokens Studio read. - **Typography** down to per-character overrides for mixed-style runs. - **Fills, strokes, gradients, and effects**: linear, radial, and angular gradients, image fills, drop shadow, inner shadow, glows, layer and background blur, and blend modes. - **Ten export formats**: PNG, JPEG, WebP, SVG, PDF, HTML in three flavors, React JSX, MP4, MOV, and Replay. - **Import** from Sketch, Figma, and SVG as native editable elements. Sit down and design by hand and the primitives, the shortcuts, and the final output match. What is new is everything around them. ## What Brilliant has that Sketch doesn't - **Cross-platform desktop support.** macOS and Windows. Sketch has been Mac-only since 2010, which locks out a real chunk of any mixed-OS team. - **Agents that do design work, not plugin work.** Sketch's MCP gives agents one lever: write and run SketchAPI code. Brilliant's agents work through design primitives directly, the same ones you use. What comes back is a file you edit, not scripts you debug. - **A free, full-featured design tool.** No subscription seat, no license purchase, no gated primitives. - **Plain-text, git-friendly files.** Blueprint on disk, clean diffs, blameable history. - **Native GPU shaders.** Metaballs, metal, iridescent, steel as first-class fills. - **Ten export formats including code and video.** PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay. - **An integrated AI chat out of the box.** Zero setup required to start working with agents. ## The honest gaps Three places Sketch is further along, each with what Brilliant does instead. It has a mature 15-year plugin ecosystem (Runner, Sketch Plugins, and the rest); Brilliant's extensibility runs through MCP, so any agent or editor drives the canvas directly, unlimited on Free. It ships interactive prototyping with Smart Animate, overlays, and clickable hotspots, where Brilliant is a fully editable static canvas today with prototyping on the roadmap and every frame, component, and token you'd prototype from already at parity. And it offers a pay-once perpetual license, a genuinely rare model, where Brilliant's answer is a free editor with no license to buy at all. If a mature plugin marketplace or a click-through prototype is the piece your team turns on this week, that piece still lives in Sketch; everything else is here, on macOS and Windows both, in plain-text files your git already reads. ## At a glance | | Brilliant | Sketch | |---|---|---| | Platforms | macOS, Windows | macOS only | | Design tool pricing | Free forever | Paid (subscription or perpetual license) | | AI integration | Built-in chat + external MCP, agents work directly on the canvas | Local MCP with `run_code` (agents write SketchAPI plugin code) | | File format | Plain-text Blueprint, git-native | `.sketch` archive (JSON + assets, inspectable but not diff-native) | | Version control | Native git (branch, diff, rollback, blame) | Native Sketch Cloud history; no git diff | | GPU shaders | Native (metaballs, metal, iridescent, steel) | None | | Export formats | PNG, JPEG, WebP, SVG, PDF, HTML, React, MP4, MOV, Replay | PNG, JPG, HEIC, TIFF, WebP, PDF, EPS, SVG | | Vector editing, auto layout, components, tokens | Full | Full | | Plugin ecosystem | MCP (agents + external editors) | Mature plugin marketplace | | Interactive prototypes | Roadmap | Yes | | Real-time multiplayer | Yes, on published projects | Yes | ## A new dance Brilliant is what a desktop-native design tool looks like when you build it for every platform, and with agents as the other half of the team from day one. Agents that do real design work, not write plugin code. A file format that goes straight into git. A full professional design tool that doesn't ask for rent. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/uizard.mdx # Brilliant vs Uizard Uizard is one of the tools that made "type a prompt, get a mockup" feel normal. It's a browser-based UI design app built for people who aren't designers, and its pitch is speed: describe an app or paste a screenshot, and Autodesigner returns multi-screen mockups you can edit. Its own headline is "UI Design Made Easy, Powered By AI." For a fast first draft, or for a product manager who needs something on screen before a meeting, that is a genuinely useful thing, and this page isn't going to pretend the appeal isn't real. Brilliant is a different kind of tool. It's a professional 2D vector design tool, in the Figma mold, that an AI agent can drive directly, and everything it makes lands as real, hand-editable vector elements rather than a flattened picture. Pen tool, boolean operations, auto layout, components with variants, a full design-token system. Where Uizard optimizes for the first draft, Brilliant optimizes for the work you do after it, and for keeping that work as files you own. *Uizard facts on this page are current as of August 2026, and one of them is that Uizard has stood still. Where a detail matters, check it against Uizard's own docs.* ## The first honest fact: Uizard has been frozen since mid-2024 This one leads because it colors everything else. Uizard was acquired by Miro, announced on Uizard's own blog on 2024-05-27 under the headline "Uizard joins Miro!", with the reassurance that "For now, Uizard will continue to be available and nothing related to your day to day product experience will change." Uizard is indeed still sold standalone, with a live pricing page, signup, and help center. What has not moved is the product's public pulse. Its blog's newest post, "Autodesigner 2.0 is here!", is dated 2024-06-12. Its changelog's newest entry is that same June 2024 release. Its Terms of Service read "Last updated on January the 25th of 2024." Meanwhile Miro, which now owns it, built the same capability natively: Miro sells its own AI UI design generator and prototyping, meters MCP calls as a per-plan line item, and does not mention Uizard on any of it. None of this is a shutdown notice, and there isn't one to cite. It's simpler and more useful than that: the tool you'd be adopting stopped shipping updates over two years ago, while its new parent competes with it directly. ## Editable vectors, versus a tool built to avoid them Uizard is candid that it is not a vector tool, and it means it as a selling point. It calls itself "the perfect alternative to vector-based design tools," one that needs "no complicated vector-based design skills." That's a fair promise for its audience, but it has a data-model consequence you feel the moment you need precision. Uizard's own Figma-plugin guide tells you what its canvas cannot hold: "SVG's, boolean operations on shapes (unions, substracts etc), and rotated elements: Export these elements as PNG's from Figma, and then upload to Uizard instead." Booleans and vectors don't degrade on import; they're turned into flat pixels before they're allowed in. Uizard's docs also state there's no pen tool, no rotation or skew ("There isn't a way to transform or skew images/components at this time"), no rulers or spacing readouts, and "no option for responsive design at this time." Brilliant is built on the opposite premise. A pen and pencil, a full vector edit mode, boolean union, subtract, intersect and exclude, flatten, and outline text all ship in the box, and anything an agent generates arrives as those same editable elements. [How vectors work in Brilliant →](/docs/editor/vectors) ## An agent surface, versus a stated refusal to provide one Uizard's AI lives inside Uizard. There's no way for your own agent to reach it, and Uizard says so plainly: "At this time, we are not able to provide API access." There's no MCP server, no Zapier, no documented automation of any kind, and no way to bring your own AI; AI is hosted and metered as generations per plan, from a handful a month on the free tier into the thousands on paid. Brilliant runs a local MCP server at `http://127.0.0.1:3333/mcp` the whole time the app is open, unlimited on every plan including Free, documented for Claude Code, Claude Desktop, Cursor, Codex, Copilot, Windsurf, and Antigravity. An external agent drives the same canvas commands you do, over a hundred of them, each with full undo, and it can create, inspect, edit, search across every canvas, and export. And the AI is yours either way: Brilliant AI is included in every plan, and you can bring your own AI instead (Anthropic, OpenAI, Google, OpenRouter, a Claude Code or Codex subscription, or a local endpoint); on your own AI Brilliant takes no markup. [What an agent can do in Brilliant →](/docs/agents/overview) ## Files you own, versus a project with no way out Ask where a Uizard design lives and the answer is only "in Uizard." There's no documented file format, no plain-text representation, and no version-control or git story of any kind. Export is images (JPG, PNG, SVG) or PDF, and code export is per-component only, through Handoff Mode, in CSS and React, gated to paid plans, with a hard ceiling Uizard states outright: "Currently, it's not possible to export your entire Uizard project to HTML, Javascript, or script." You also can't bring code back in: "you can't import code or coding files at this time." A Brilliant workspace is a folder on your disk. Each canvas is a plain-text Blueprint file ending in `.bl`: one element per line, stable ordering, inline coordinates, hex colors, so you can `git diff` two versions and read the change, branch a design like a feature, and `git blame` a stray color: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` Brilliant exports to ten formats, including HTML in three flavors and React JSX, and nothing leaves your machine until you choose to publish. [Your designs live where your code lives →](/docs/workspace/canvases-and-files) ## What Brilliant has that Uizard doesn't - **Editable vector output.** Pen tool, boolean ops, and vector edit mode, with agent-generated work landing as real elements, not the flattened PNGs Uizard asks you to make before import. - **A real agent surface.** A standard MCP server, unlimited on Free, versus "we are not able to provide API access." - **Brilliant AI included, or your own AI with zero markup.** Anthropic, OpenAI, Google, OpenRouter, local endpoints, or a Claude Code and Codex subscription. Uizard meters hosted generations per plan. - **Files you own.** Plain-text Blueprint on local disk that diffs in git, exported to ten formats. Uizard has no file format and can't export a whole project to code. - **A full design-token system.** OKLCH ramps, modes, brands, and a resolved file Style Dictionary and Tokens Studio read. - **A tool that's still shipping.** A public changelog with current releases, next to a product whose blog and changelog stopped in June 2024. ## The honest gaps A few things Uizard does that Brilliant doesn't, mostly about the first draft. Its Autodesigner turns a prompt, a screenshot, or a URL into multi-screen mockups in one step, and it scans hand-drawn wireframes; Brilliant generates through an agent, which is more capable but less of a one-click party trick. It's built for people who've never opened a design tool and runs zero-install in the browser; Brilliant has a web editor too, but its home is a professional desktop app that asks a bit more of you. And it has click-through prototyping and live co-editing, where Brilliant is a fully editable static canvas today with prototyping on the roadmap and multiplayer on published projects. Viewer pricing is shared ground, not a Uizard edge: neither tool charges for viewers or guests. If the fastest path from a sentence to a rough screen is all you need, Uizard's front door is pleasant; the moment you edit, own, or extend what comes out, that's where Brilliant starts. ## At a glance | | Brilliant | Uizard | |---|---|---| | Data model | Editable vectors | Non-vector; booleans and SVG flatten to PNG on import | | Vector / pen tool | Yes | No, by design | | Agent access | Standard MCP, unlimited on Free, 100+ undoable commands | None; "not able to provide API access" | | AI model | Brilliant AI included, or your own AI, zero markup | Hosted generations, metered per plan, no bring-your-own-AI | | File format | Plain-text Blueprint, git-native | None documented | | Code export | HTML (3 flavors), React, SVG, PDF | Per-component CSS/React only; no whole-project export | | Figma interop | Import (URL / page / subtree) + Send to Figma | Import only (plugin copy-paste or screenshot scan) | | Interactive prototypes | Roadmap | Yes (navigation + links, paid) | | Product status | Shipping, public changelog | Blog and changelog frozen since June 2024 | | Platforms | macOS, Windows, Linux desktop + web | Browser only | ## A new dance Uizard and Brilliant answer different questions. Uizard answers "how do I get a rough screen out of a sentence, fast, without knowing a design tool," and for that first draft it's a friendly place to start. Brilliant answers "how do I do the real work, with an agent, and keep it as files I own and can extend." One hands you a picture; the other hands you editable vectors, an open protocol your agent can drive, and a plain-text file in your own git history. If you've outgrown the first draft, that's the tool Brilliant is trying to be. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. --- Source: https://brilliant.design/docs/compare/visily.mdx # Brilliant vs Visily Visily is an AI-first UI design tool with real momentum, and it's honest about who it's for. It describes itself as "AI-powered UI design software," and its whole surface is built around describing or pasting rather than drawing: text-to-design, screenshot-to-design, even diagram-to-design, with a big template library and collaboration that doesn't charge you for viewers. If you want non-designers to produce plausible screens quickly, in the browser, Visily is good at that, and this page will say where it's genuinely strong before it says anything else. Brilliant is aimed at a different job. It's a professional 2D vector design tool, in the Figma mold, that an AI agent can drive through a full command surface, with the design landing as real editable elements you own on disk. The two tools overlap on the words "AI" and "UI design" and diverge almost everywhere underneath, so the useful comparison is about depth, about what the agent can actually do, and about where the file lives. *Visily facts on this page are current as of August 2026. Where a detail matters, check it against Visily's own docs.* ## Two tools headed opposite ways Visily is built for non-designers on purpose, and it's good at it: describe or paste your way to a screen, with Plan Mode to shape intent first, a template library of over 1,500 layouts, diagramming (sitemaps, flowcharts, org charts, ERDs, sticky notes) in the same board as your UI, prototyping, and zero-install reach across Chrome, Firefox, Edge, and Safari plus mobile browsers. Brilliant heads the other way, a professional editor an agent can build inside, with the design landing as real editable elements you own on disk. Where the two overlap is the words "AI" and "UI design"; underneath they diverge on depth, on what the agent can actually do, and on where the file lives, which is what the rest of this page is about. The honest gaps get one compact section near the end. Viewer pricing, for the record, is shared ground rather than a Visily edge: Visily doesn't license viewers and guests, and Brilliant doesn't charge for viewers or guests either; in both tools, paid seats are for people who edit. ## Agents that write the canvas, versus agents that read it Visily ships a real MCP server, and that deserves a fair hearing rather than a dismissal. Point your agent at a Visily board and it can find it, turn it into application code, or pull its theme tokens, generating a React, TypeScript, Tailwind, and shadcn/ui stack, with an installer set covering a long list of named agent clients. That's a legitimate design-to-code path, and anyone who told you Visily "has no MCP" would be wrong. The honest distinction is about direction and access, not existence. Visily's documented MCP tools are read-oriented: `get_design_context`, `get_screenshot`, `get_board_theme`, and the `list_*` discovery calls. They read a design and hand it to your agent; none of them is documented to write or mutate elements on the canvas. And the server is gated: by Visily's own rule it "works only on workspaces on a Pro or Business plan where you are the workspace creator or have an editor license. Free workspaces and viewer/guest roles are not supported." Brilliant's agent surface points the other way. Its MCP server lets an agent create designs as native editable elements, read structure, change properties, reparent, rename, delete, and reorder, search across every canvas, and run the same hundred-plus canvas commands you do, each with full undo, and it's unlimited on every plan including Free. Both are real MCP stories. One reads your design out to code; the other builds and edits the design itself. [What an agent can do in Brilliant →](/docs/agents/overview) ## Professional depth, versus a five-shape catalogue Visily publishes its complete shape list, which makes this easy to state without guessing. It's exactly five items: "rectangle, overlay, oval, line, and triangle." There's no pen or bezier path editing in that catalogue, no boolean operations, and no auto layout in the Figma sense (Visily offers alignment, even spacing, and anchor-based constraints instead). Reuse runs through saved templates rather than a master-instance component model with named properties and variant axes, and its design-token story is a per-board Theme plus token output to Tailwind and CSS variables, not an in-editor variables system with modes. Brilliant is a full editor underneath. Pen and pencil with vector edit mode, boolean union, subtract, intersect and exclude, flatten and outline text, auto layout with hug, fill, and fixed sizing and layout grids, and component sets with variants, properties, slots, and cross-canvas instances. On tokens it compiles a `Styles/*.ds` source into eleven-step OKLCH ramps from one seed, with light and dark theming, modes as transforms, multi-brand overlays, and a resolved `.gen.yaml` that Style Dictionary and Tokens Studio read. [How the token system works →](/docs/design-system/overview) ## Your own AI, and your files Visily's AI is hosted and metered in credits that reset each month, it doesn't name the model behind them, and there's no way to bring your own. Brilliant AI is included in every plan, and you can bring your own AI instead (Anthropic, OpenAI, Google, OpenRouter, a Claude Code or Codex subscription, or any OpenAI-compatible endpoint including local LM Studio, Ollama, and vLLM); on your own AI your prompts go to a model you chose, on a key in your own name, and Brilliant takes no markup. [Bring your own AI →](/docs/ai/providers-and-models) The files diverge just as sharply. Visily is cloud-only: the unit of work is a board in a workspace, durability is expressed as recovery windows rather than files, and there's no documented open format or plain-text, version-controllable source. Its interchange `.vis` file exists only to move a design to the Visily plugin in Figma. Image export is PNG, JPG, and WebP with PDF; SVG isn't a design export, only an asset download in Inspect; and code export is paid, screens-only, up to ten at a time. A Brilliant workspace is a folder on your disk, each canvas a plain-text Blueprint `.bl` file that diffs cleanly in git without unzipping anything: ```diff c16fc75741feed85 fr p(80,80) s(480,320) rd(16) "Hero" - 88625e475868c087 r p(24,24) s(160,100) f[(r1,#F8F8F8)] rd(8) "Panel" + 88625e475868c087 r p(24,24) s(160,100) f[(r1,#0080FF)] rd(8) "Panel" ``` Brilliant exports to ten formats including HTML in three flavors and React JSX, imports from Figma by URL, page, or node-id subtree, and has Send to Figma for the trip back. [Your designs live where your code lives →](/docs/workspace/canvases-and-files) ## What Brilliant has that Visily doesn't - **Agents that build and edit the canvas.** Create, reparent, restyle, reorder, and run 100+ undoable commands, unlimited on Free, versus a read-oriented MCP gated to paid editor seats. - **Professional editor depth.** Pen tool, boolean ops, auto layout, and component sets with variants, next to a five-shape catalogue. - **Brilliant AI included, or your own AI with zero markup.** A model you chose on a key in your name, versus hosted credits with the provider unnamed. - **Plain-text files you own.** Blueprint on local disk that diffs in git, versus cloud-only boards with no open format. - **A full design-token system.** OKLCH ramps, modes, and brands, with a resolved file Style Dictionary and Tokens Studio read. - **Figma interop in both directions.** Import by URL, page, or subtree, plus Send to Figma. ## The honest gaps What Visily does that Brilliant doesn't, each with Brilliant's answer. Visily is built for non-designers, with generation, screenshots, and diagrams as first-class inputs; Brilliant is a professional editor that asks more of you and rewards it with real vector depth. It ships interactive prototyping; Brilliant is a fully editable static canvas today with prototyping on the roadmap. And it does diagramming (sitemaps, flowcharts, ERDs, sticky notes) alongside UI and carries a large template library with browser-based, zero-install reach; Brilliant is a design canvas rather than a diagramming surface. If non-designer accessibility, diagramming, and prototypes in one browser tab are what your work turns on, that's Visily; if you'll edit real vectors, own the file, and drive it with your own agent, that's Brilliant. ## At a glance | | Brilliant | Visily | |---|---|---| | Agent access | MCP that writes the canvas, unlimited on Free | MCP that reads to code, Pro/Business editor seats only | | Editor depth | Pen, booleans, auto layout, component sets | Five shapes; no pen, boolean, or auto layout documented | | AI model | Brilliant AI included, or your own AI, zero markup | Hosted credits, provider unnamed, no bring-your-own-AI | | File format | Plain-text Blueprint, git-native | Cloud-only boards, no open format | | Code export | HTML (3 flavors), React, SVG, PDF | React / Vue / HTML, screens only, paid | | Design tokens | Full system (OKLCH ramps, modes, brands) | Per-board Theme + token export to Tailwind/CSS vars | | Figma interop | Import (URL / page / subtree) + Send to Figma | Import and export via a Figma plugin, paid | | Interactive prototypes | Roadmap | Yes | | Diagramming | No | Yes (sitemaps, flowcharts, ERDs) | | Platforms | macOS, Windows, Linux desktop + web | Browser only | ## A new dance Visily and Brilliant both put "AI" and "UI design" on the label and then head in opposite directions. Visily is the accessible front door: describe a screen, get one, prototype it, hand it to a developer as code. Brilliant is the professional editor an agent can build inside, with the depth of a real design tool, the model bill in your own name, and the file in your own git history. If your work stops at a plausible first screen, Visily is a fine home for it. If it starts there, that's the tool Brilliant is trying to be. [Read the full manifesto](/manifesto) to see the rest of the argument, or [install Brilliant](/) and try it for yourself. # Section: For developers --- Source: https://brilliant.design/docs/developers/blueprint.mdx # The Blueprint DSL When an agent designs in Brilliant, it isn't clicking around or emitting giant JSON blobs. It writes Blueprint: a compact, line-oriented text language built for exactly this job. This page explains what Blueprint is, why it exists, and how to read it, so you can follow along with what your agent is actually doing. You don't have to write Blueprint yourself. It's here for the curious and for anyone building on top of Brilliant's MCP server. ## What Blueprint is Blueprint is a tiny domain-specific language for describing 2D design. It has a handful of element types (frames, text, vectors, lines, icons), properties (position, size, fill, stroke, radius, effects, auto layout), and references that let one line point at another. One element per line, children indented under their parent. The whole grammar fits on a page. Agents create and modify a canvas by sending Blueprint to Brilliant's `create_modify_elements` tool. Everything that lands is a real, native, editable element: the same objects you'd get by drawing them by hand, ready to keep editing in the app. ## Why a DSL instead of JSON or raw tool calls A design isn't a static document. It's a live graph with an auto-layout solver, hit testing, selection state, and z-order, all updating on every change. Two obvious representations both fall short for an agent driving that graph: - **Raw JSON** is explicit and verbose, built for machines rather than for an agent's working memory. A single card with a few children runs to hundreds of lines, and the agent spends most of its context on field names instead of design decisions. - **One tool call per operation** (move, resize, fill, group, and a few hundred more) makes even a small card five or six calls, and the agent has to learn which call does what before it can start. Blueprint is the third option, and it plays to what agents are already great at: reading and writing unfamiliar programming languages. It wins on four fronts: - **Density.** A card that's ten lines of JSON is one line of Blueprint, at roughly a twentieth of the tokens. - **Diffs.** Line-oriented syntax means an edit is a line, so changes stay small and legible. - **Streaming.** Elements arrive and render in batches as the agent writes them, instead of after one giant payload. - **Forgiveness.** The parser absorbs near-miss syntax and, when something is genuinely wrong, hands back a specific diagnostic and a suggested fix. The full story behind those tradeoffs is in the [Blueprint blog post](/blog/blueprint). ## Try it live Here's Blueprint running on the actual engine, right in the page. The walkthrough below takes you through four small edits on one live card, each a single line, so density, diffs, and streaming stop being claims and become something you can feel. That was a handful of Blueprint lines doing real work. The next section reads them, one at a time. ## Reading a Blueprint A few real snippets. You don't need to memorize the syntax to get the shape of it. ### A frame with auto layout and children ```blueprint al(v,g($spacing.lg),pad($spacing.xl)) s(360,hug) f[($color.surface)] rd($radius.md) "Card" t("Settings",$font.family,$font.size.lg,sb) f[($color.text.primary)] #title t("Manage your preferences",$font.family,$font.size.sm) s(fill,hug) f[($color.text.secondary)] #desc al(h,x(c),y(c),g($spacing.sm),pad($spacing.md,$spacing.lg)) s(fill,hug) f[($color.primary)] rd($radius.sm) "Button" svg(icon:check) s(16,16) f[($color.on-primary)] t("Save",$font.family,$font.size.sm,sb) f[($color.on-primary)] ``` Line by line: - `al(v,...)` is a vertical auto-layout frame. `g()` sets the gap between children, `pad()` the inner padding, `s(360,hug)` fixes the width at 360 and lets the height hug its content. - Indentation is parentage: the two `t(...)` text lines and the `al(h,...)` button are children of the card. - `f[(...)]` paints a fill, `rd(...)` rounds the corners. The trailing `"Card"` and `"Button"` are display names. - The nested `al(h,...)` is a horizontal row centered with `x(c),y(c)`; inside it sit a `svg(icon:check)` icon and a `t(...)` label. ### Everything is a design token Look again at the values above: `$spacing.lg`, `$color.surface`, `$font.size.sm`. Those aren't literals, they're design tokens resolved through the document's design system. ```blueprint t("Total",$font.family,$font.size.lg,sb) f[($color.text.primary)] ``` The font family, the size, and the color all bind to tokens. Change `$color.text.primary` once and every element that references it updates. That's the same token system you edit by hand in the app, described in the [design system docs](/docs/design-system/overview). ### Refs let you edit what you already made A trailing `#name` assigns a stable reference to an element. Lead a later line with that same `#ref` and you're modifying the existing element, not creating a new one. ```blueprint #desc f[($color.text.primary)] ``` This repaints the `#desc` text from the first example to the primary color. There's no need to restate the rest of the element: a leading `#ref` means "edit this one." To tuck a new element inside an existing frame, an agent adds `parent(#ref)` to the new line. ### Lines double as connectors For diagrams, `line(...)` draws a segment, and its endpoint form anchors to two elements, so an arrow follows them as they move. ```blueprint line(from(#start), to(#end)) intent(flow) ``` `from` and `to` take element refs; `intent(flow)` picks a sensible route, arrowhead, and stroke in one word. This endpoint form is how Brilliant expresses flowcharts, dependency links, and callouts. ## The parser is forgiving by design When an agent sends a line, it's validated before anything touches the canvas. When a line is slightly off, the parser absorbs the common cases, and when it can't, it returns a diagnostic with a stable code, a plain-English message, and a concrete fix rather than failing silently. Every response also carries a rendered image of what landed, so the agent sees exactly what it made next to any feedback. ## Who this is for This page is for developers watching an agent work and wondering what those dense lines mean, and for anyone building an MCP integration on top of Brilliant. Agents don't learn Blueprint from a page like this one: Brilliant serves them the grammar automatically over MCP, so they arrive already fluent. For the complete grammar and the object syntax agents emit, see the public repo at [github.com/brilliant-hq/brilliant](https://github.com/brilliant-hq/brilliant). To point your own agent at the canvas, start with [Connect an Agent](/docs/agents/overview). --- Source: https://brilliant.design/docs/developers/quality-linter.mdx # The quality linter An agent designing on a canvas fails differently than an agent writing code. The syntax is fine and the layout is wrong: text with no fill on a white card, a column that resolved to zero pixels wide, three siblings all named "Row". Brilliant checks for that automatically, in two stages, and hands the agent back a code, a sentence, and a fix. This page is about what those checks are and why they exist. You never run them by hand; they run on every agent edit. ## Two gates, not one Two different checks guard an agent's edits, and they run at different moments. The first is a **validator**. Each line is checked immediately, before anything touches the canvas: wrong property for a type, missing required argument, unresolvable reference, an impossible auto layout rule. A fatal line error stops that line; a single bad property token drops just that property and the rest of the block still applies. These diagnostics carry `B`-prefixed codes. Valid lines apply as real, editable elements, with auto layout, components, and the rest of the graph updating together. The second is a **composition linter**. It runs once elements are on the canvas and inspects the actual geometry for design-intent problems, and its diagnostics carry `C`-prefixed codes. The split matters. A malformed line is caught in the abstract and never lands. A composition problem can only be seen once the layout solver has run, so it's caught on the real result. ## What the composition linter catches These are the failures that look fine line by line and wrong on screen: - **Invisible text.** Text with no fill defaults to white and disappears on a light background. (`C201`) - **Collapsed elements.** Anything that resolves to `0 x N` or `N x 0` after layout, the classic being a `fill` width inside a `hug` parent with nothing to stretch to. (`C301`) - **Duplicate sibling names.** Three elements called "Row" under one parent, usually the sign of an agent stuck in a loop. (`C101`) - **Overflow and spill.** Text that overflows its parent, or content that spills outside a clipping frame. - **Stranded work.** A block dropped far from everything else, off in empty canvas. Each check runs only on the elements the agent just created or modified, so a large existing canvas doesn't slow the loop down or drown the response in noise about work nobody touched. ## A diagnostic is a code, a sentence, and a fix Every diagnostic carries a severity (error, warning, info), a category (syntax, property, layout, reference, composition), a stable code, a plain-English message, and a concrete suggestion. Three beats, meant to be read rather than just obeyed: ``` ✓ 2 lines applied. ⚠ C301: "Image Slot" is 0×120. fill inside a hug parent collapses to 0. Use a fixed size, or give an ancestor a fixed width on that axis. ``` The validator's side looks the same, minus the canvas: ``` ✗ 0 lines applied (1 error). B101 (line 1): spaceBetween is only valid on the main axis (x for al(h)). Move sb to x(): x(sb) y(c). ``` Every response also carries a rendered image of what landed, so the agent sees exactly what it made next to the feedback on what went wrong. Elements arrive in streamed batches, so this fires as the design comes together rather than at the end of a long turn. ![The chat panel, where an agent's diagnostics and the design land together|420x](/docs/images/bank/ai-chat-panel/general.webp) ## Why it changes the output A model fluent in code but new to Blueprint stumbles on an unfamiliar shape, reads the diagnostic, and writes it correctly the next time it comes up, inside the same session. Costs come down and accuracy goes up for the same reason: the agent spends tokens on decisions instead of repeating mistakes nobody named. In practice that means most of these never reach the version you review. The agent corrects them in the same run. ## What it doesn't check The linter is about composition, not taste and not compliance. It has no opinion about whether your hierarchy reads well, whether a headline is any good, or whether a color pairing is on brand. It's not an accessibility audit either: it flags text that would be invisible, not every pair that falls short of a contrast ratio. And it checks what the agent just touched, so it isn't a whole-canvas health report you can run over old work. ## Next - **The language the checks run against:** [The Blueprint DSL](/docs/developers/blueprint). - **The full architecture story:** [Blueprint, the blog post](/blog/blueprint). - **Point your own agent at the canvas:** [Use with AI agents](/docs/agents/overview). # Section: Changelog --- Source: https://brilliant.design/docs/changelog/changelog.mdx # Changelog Every Brilliant release, newest first. The latest build is **0.1.0-beta.76** (September 15, 2026); grab it from [Download & Install](/docs/getting-started/installation), or just keep the app open: it updates itself. ## 0.1.0-beta.76 *Released September 15, 2026* - Sync fixes: your edits stay put when a canvas is open in more than one place, on disk, on the web, or in the app; plus small polish. ## 0.1.0-beta.74 *Released September 15, 2026* - Fixes & polish for agents working in Brilliant: clearer errors, no silent waits, smarter long tasks; plus sync and checkout fixes. ## 0.1.0-beta.73 *Released September 14, 2026* - Fixes & polish: sturdier sync across devices, smoother Brilliant AI chat and checkout, and dozens of smaller fixes ## 0.1.0-beta.71 *Released September 13, 2026* - Brilliant AI: the chat answers the moment you open the app, with nothing to set up. It starts on a free trial (sign in to unlock the rest). If you are on Personal, Pro or Max, your plan now includes monthly Brilliant AI usage at no extra cost; your own AI (your keys and coding agents) works exactly as before, unlimited on every plan - Fixes & polish: frame names always show, right-click Cut works again, sturdier sync with a project open in two places, letter-spacing tokens as fractions of the font size, and dozens of smaller fixes ## 0.1.0-beta.70 *Released September 9, 2026* - Copy element id: right-click any layer and copy its id (next to Copy link), then paste it into your agent's prompt to point it at that exact layer - Fixes & polish: adding layers to a masked frame never changes which layer is the mask (and an agent can hand a mask a new shape on purpose), sturdier sync when the same project is open in two places at once, a smoother canvas while text and masks settle, inner shadows and glows render correctly over strokes, agent exports scale down with a note instead of refusing, Figma imports keep team-library components, GPT-6 Astra is now the default OpenAI model, and dozens of smaller fixes ## 0.1.0-beta.69 *Released September 8, 2026* - Fixes & polish: designs open more reliably, images load on the first open of a project, sync keeps edits you made while the app was closed, and several smaller fixes ## 0.1.0-beta.68 *Released September 8, 2026* - Fixes & polish: designs open more reliably, images load on the first open of a project, and several smaller fixes ## 0.1.0-beta.67 *Released September 7, 2026* - Fixes & polish: a much faster start on Windows, one-click updates, sharper rendering of masks, shadows and tiled fills, quieter and clearer sync notices, and sturdier chat and exports ## 0.1.0-beta.66 *Released September 6, 2026* - Fixes & polish: the inspector now keeps up with cloud projects, rename a project from its tab, GPT-6 Astra in the model picker, sturdier saves, cloud writes and conflict copies, exports that verify what they wrote, and Windows chrome fonts that heal themselves ## 0.1.0-beta.65 *Released September 5, 2026* - **Fixes and polish.** smoother canvas switching, honest export limits, vector and web polish, dozens more ## 0.1.0-beta.64 *Released September 4, 2026* - Fixes & polish: a truly uniform flat chrome, better battery manners on locked Macs, snappier AI interrupts, and sturdier agent tooling ## 0.1.0-beta.63 *Released September 3, 2026* - Fixes & polish: a cleaner flat look for panels and menus, sharper PDF exports, and sturdier cloud sync ## 0.1.0-beta.62 *Released September 3, 2026* - The AI harness got a major upgrade: stopping an agent mid-turn never loses work it already committed, interrupted streams wind down cleanly on every provider, agent-written designs using modern CSS colors (oklch, hsl) now convert cleanly with every conversion announced instead of dropping silently, and Gemini 3.8 Flash joins the model list - A major performance wave: selecting and marquee-dragging across huge canvases is dramatically faster: a 10,000-element marquee dropped from ~450ms to under 50ms, with zero dropped frames - Cloud sync no longer creates duplicate '(local)' copies of a canvas when your work and the cloud's are actually the same: identical content is recognized and adopted silently - Multiplayer polish: dismissing another person's cursor now sticks (no more flicker), and collaborators' profile photos load reliably for everyone - Blend modes on text fills now compose exactly like every other fill - Resizing a plain frame re-flows its fill-width children immediately - Escape closes an open menu without dropping your selection, and Cmd+D now remembers your last option-drag offset and repeats it - Text sizes up to 20,000 for massive display type ## 0.1.0-beta.61 *Released September 1, 2026* - Live sync now heals itself: a dropped or refused connection re-mints its credential and rejoins automatically; 'sign in again' only appears when you truly are signed out - No data loss, mechanized: an empty document can never overwrite real content, stranded offline edits materialize as '(recovered)' pages instead of vanishing, and a canvas renamed while offline keeps its content - The offline banner tells the truth and names the file that isn't syncing; merge notices name exactly what merged - The opacity dropdown can no longer flash-apply a value on hover or strand a change undo can't reach - Double-click into text selects all the text; double-clicking again selects the word: matching mac - Undo after dragging into a nested auto layout puts every sibling back where it was - AI agents without vision get a textual render summary from canvas previews instead of images they cannot read ## 0.1.0-beta.60 *Released September 1, 2026* - Snapping overhauled end to end: engages on the first drag, holds perfectly still while you're snapped, offers fewer and smarter targets, and the guides are pure pink: no gray fringes, no flicker - The pixel grid now works inside frames, and with the grid on, alignment lands on whole pixels: what you snap is what you get - Inner shadows render on vector paths - Files written by AI agents open more forgivingly: a common non-canonical stroke token now reads cleanly instead of locking the file - Live-session save recovery hardened: an interrupted connection can no longer strand an edit on one client - Double-click text entry selects the word: matching mac ## 0.1.0-beta.59 *Released August 30, 2026* - **Snapping, rebuilt to feel right.** the first drag snaps like the re-drag, nothing pops against your hand, closer targets win, and dense scenes offer fewer, smarter snap targets; hold Cmd to place freely, and guides now wear the brand pink - **Vector editing meets the pixel grid.** node drags no longer jitter between an off-grid neighbor and the grid; with the grid on, snapping offers only grid-true targets - **The eyedropper respects gradients.** picking a color onto a gradient recolors the active stop instead of flattening the whole fill - **Fixes across the board.** framing inside a component instance no longer crashes, command-palette panes stay open when you open them, brand-scoped typography tokens resolve correctly on component overrides, and agents can reach a dismissed Scratch again ## 0.1.0-beta.58 *Released August 29, 2026* - **Big files, no more waiting.** huge canvases copy, select, and edit without a hitch, complex file loads that took half a minute open in about a second, and imports, exports, saves, and boot all got major speed passes - **Pasting shows its work.** heavy pastes get a live progress bar with phases and a finished beat; quick pastes stay instant - **Fixes and polish.** your window comes back where you left it on Mac, sync status stays honest when the network blips, icons render true at every zoom, read-only projects respond instead of ignoring clicks, plus dozens of smaller repairs ## 0.1.0-beta.57 *Released August 27, 2026* - **Another Figma wave.** the 1:1 march pushes into the hard stuff: hidden layers, rotated vectors, boolean groups, and photographic color effects all land true, files named in any language import straight to the cloud, and thousand-layer files come in lighter and faster with duplicate images deduped - **Fixes and polish.** the AI chat works before you sign in, the app boots faster and the whole platform got a latency pass, a long-session canvas crash loop is gone, autosave heals a damaged local journal on its own, the OpenType menu only offers what your font carries, PDF exports carry true font metrics, plus dozens of smaller repairs ## 0.1.0-beta.56 *Released August 23, 2026* - **Figma imports, leveled up.** a folder per file, a canvas per page, and almost everything lands looking like the original - **Hosted Cursor sessions.** Cursor joins Claude Code and Codex as a first-class agent. Connect once, pick your model, and it drives your canvas from the built-in chat - **Fixes and polish.** no more paste freezes, publishing self-heals instead of failing mid-upload, canvases can't get lost mid-sync, dozens more ## 0.1.0-beta.55 *Released August 20, 2026* - **Project covers.** give any project a face: set a cover and it shows everywhere your project appears - **Fixes and polish.** Sonnet 5 stops spinning its wheels and actually designs, Figma design systems now import loudly or tell you exactly why not (no more silent drops), sync warnings only appear when something is genuinely wrong, plus dozens of smaller repairs ## 0.1.0-beta.54 *Released August 19, 2026* - **Fixes and polish.** AI chats keep their memory (no more mid-task amnesia), MCP servers connect reliably however node is installed, Figma paste handles duplicate and nested components cleanly, arrows render their true silhouette, shadows and glows got faster and sharper, plus dozens of smaller repairs ## 0.1.0-beta.53 *Released August 18, 2026* - **Figma import, wave two.** multi-page imports got sturdier with a proper progress bar, the updated capture plugin (1.4.1) is live in the Community, and big imports no longer freeze the app mid-pull - **Fixes and polish.** snapping is jitter-free, arrow keys never lose the canvas, Claude Code connects every time however you installed it and the MCP toggle tells the truth, dither patterns anchor to your artwork so exports match the canvas, the Sharing tab is now Project, plus dozens of smaller repairs ## 0.1.0-beta.52 *Released August 17, 2026* - **Move your Figma work over and lose nothing.** paste straight from Figma with components, variables, auto layout and text styles intact; import whole files with every design-system collection preserved as mode axes; element pinning with the new nine-point constraint control; diamond gradients, new blend modes, and boolean frames that hug their visible result - **Connect your coding agent to Brilliant on the web with zero setup.** one npx command pairs Claude Code (or any MCP agent) with your browser session, agent cursors land on the canvas they're actually editing, and the mobile web viewer got a full facelift - **Desktop honors view-only.** shared projects open read-only with an eye icon and fork-in-place when you want your own copy; plus copy and move files or folders between projects, cloud or local, both directions - **Sharper type.** OpenType typography renders on the canvas, and imported text keeps its exact truncation, casing, and styling - **Reliability wave.** arrow keys never lose the canvas, switches are instant and honest, hidden elements render when you copy them, HEIC images import, image-heavy pages render every image, filter-heavy canvases stop hitching, agents refuse loudly instead of failing silently, Cmd+\ hides all chrome, plus dozens of smaller repairs ## 0.1.0-beta.51 *Released August 14, 2026* - Brilliant for Teams is here! Create your team, invite your people, and design together live in shared team projects. Projects now carry proper display names, and the new notifications bell keeps everyone in the loop on follows, stars, and forks - **Polish and fixes across the board.** sync now asks before anything gets overwritten (both sides preserved), image-heavy canvases no longer lose images at export time, Windows saves retry through file locks instead of silently dropping, plus launcher rename fixes and dozens of smaller repairs ## 0.1.0-beta.50 *Released August 11, 2026* - **AI replies start in a fraction of the time, and chats cost less.** the assistant stays warm and reuses its context instead of rebuilding it for every message - **Teams, in early access.** create a team and invite your people - **Dozens of fixes and polish.** imports, components, the inspector, clipboard, cloud sync, and Windows first-launch reliability all got sturdier ## 0.1.0-beta.49 *Released August 8, 2026* - **Fixes & polish.** a deep stability pass: rare crashes hunted down, saving and sync hardened, and your view always lands exactly where you left it ## 0.1.0-beta.48 *Released August 5, 2026* - Smoother and safer file opening - Snappier exports - More reliable sync and conflict resolution - Sharper rendering across the board - **Fixes & polish.** quit without waiting, broader font coverage, and a tail of smaller improvements throughout ## 0.1.0-beta.47 *Released August 3, 2026* - **The right font, every time.** true faces from your OS, real italics for families without one, and text that snaps to size when its font arrives. - **Markdown, first class.** .md files open as source plus live preview, with full CommonMark and GitHub flavor. - **Fixes and polish.** no blank flash on reload, two rare ways to lose good content closed, shader fills export as live WebGL, image fills crop like the canvas, palette scrolling stays off the canvas, drops publish from layer rows, and text measures 18x faster. ## 0.1.0-beta.46 *Released August 2, 2026* - Fixes a layout collapse when moving elements out of auto layout frames. ## 0.1.0-beta.45 *Released August 2, 2026* - A steadier canvas on open, fewer interruptions while you edit, and clearer messages. ## 0.1.0-beta.44 *Released August 2, 2026* - Windows sign-in restored, faster starts and imports, and steadier collaboration. ## 0.1.0-beta.43 *Released August 1, 2026* - Calmer onboarding, steadier editing, and fewer surprises on Windows. ## 0.1.0-beta.42 *Released August 1, 2026* - **Fixes and polish.** Safer, verified app updates. ## 0.1.0-beta.41 *Released August 1, 2026* - **Fixes and polish.** Reliability improvements across editing, importing, and saving. ## 0.1.0-beta.40 *Released August 1, 2026* - **Fixes and polish.** Major stability improvements across the board. ## 0.1.0-beta.39 *Released July 31, 2026* - **Fixes and polish.** Keyboard, camera, and live-session reliability. ## 0.1.0-beta.38 *Released July 31, 2026* - **Fixes and polish.** Smoother startup and steadier previews. ## 0.1.0-beta.37 *Released July 31, 2026* - **Meet the Brilliant platform.** Publish any project to brilliant.design under your handle. Share it with a link, keep it private, or put it on your profile. Your designs get a home on the internet, live. - **Real multiplayer.** Open the same project on desktop and in the browser, or with your whole team. Live cursors, presence avatars, and edits that sync as you make them. When two people touch the same thing, changes merge element by element, and nobody's work is ever lost. - **A new home.** Projects live in a redesigned home with visual tiles, tabs that remember where you were, and your cloud projects one click away. Open a cloud project instantly without downloading it, or make it available offline. - **The full editor, in your browser.** brilliant.design now runs the real editor. View any public project live, edit your own from any machine. - **Sign in only when you want to.** Everything works before you ever sign in. When you publish or share, sign in with Google or an email code without leaving the app. - **Fonts are faithful.** Fonts load the instant a design references them, font collections (.ttc) pick the right face, and the cases where text silently fell back to the wrong font are gone. - **Fixes and polish.** A crash some of you hit during exports and agent sessions is fixed, multi-selection exports save one file per element (with a single-file option), Figma imports resume themselves after you connect, renames are instant everywhere, and dozens of sync and stability hardenings across the board. ## 0.1.0-beta.36 *Released July 17, 2026* - **Hosted Codex sessions.** OpenAI's Codex CLI joins Claude Code as a first-class agent. Sign in once, pick a Codex model, and it drives your canvas with the same tools, settings UI, and connection flow you know from Claude Code. Works on Windows too. - **The smoothest canvas yet.** Panning, zooming, coasting, drag previews, and agent cursors all glide at your display's native rate. - **Layout and strokes.** Per-side strokes (give a card just a bottom border), corner smoothing for squircles, min/max width and height constraints in auto layout, vertical text alignment, and cross-axis gap for wrap layouts. - **Richer image fills.** Tile, stretch, and fit modes with a tile-scale control in the inspector, per-fill opacity, and image fills inside vector regions render correctly everywhere, including exports. - **Deeper Figma round-trip.** Liquid Glass survives both directions, and a big parity wave lands: image-fill opacity, text case, crop modes, tile scaling, stroke miter limits, hidden elements, and instance overrides all import and send faithfully. - **Text files feel native.** Non-canvas files in your repo open in a real editor surface instead of a stale canvas view. - **Fixes and polish.** Undo is faithful in more places (deleting a parent with its child, hug-group operations, auto-layout property changes), text stops jittering while elements move, curved shapes no longer poke out of their selection outlines, crop ghosts rotate with the crop, and thin fills like underlines and dividers never vanish when zoomed out. ## 0.1.0-beta.35 *Released July 15, 2026* - **A far steadier AI chat.** GPT-5.6 (and the whole GPT-5 family) works with tools and reasoning again, and you can watch OpenAI models reason live, just like Claude. Provider hiccups now fix themselves behind the scenes, rate limits retry automatically with a visible countdown, truncated responses pick up where they left off, and your first message can no longer vanish from a session's history. - **Windows AI chat fixes.** Claude Code sessions on Windows no longer stall after a permission block, and the connection indicator tells the truth. - **Agent cursors, round two.** Cursors now perch at the corner of the element being worked on instead of covering it, thinking captions pace honestly with the work, names stop flickering, hover freezes a cursor so you can read it (with an ✕ to dismiss it for the turn), and cursors survive canvas switches, with a separate identity per Claude Code terminal when you run several agents at once. - **Fixes and polish.** Thin borders never vanish at any zoom, a few rare ways to lose changes when switching canvases are gone, font fetching recovers gracefully on flaky networks, liquid glass presets got bolder, and folder-local brands now export with the right palette. ## 0.1.0-beta.34 *Released July 14, 2026* - **Agent cursors.** AI agents now work under live multiplayer-style cursors, with their thoughts streaming right beside the cursor as they design. No more opening the chat just to see why creation paused for a minute, and when you do want the full picture, click the cursor to jump straight into that agent's chat. - **Image filters are back.** All seven filter fills (Halftone, Pixelate, Duotone, Dither, Color Adjust, and more) render again on the new engine, including on text. - **Sharper edges.** Shallow curves get true edge antialiasing, and frame labels and other on-canvas text chrome now match full content-text sharpness. - **A smoother launch.** The app opens straight into a ready canvas: no more blank-canvas flash, no stuck splash screen, and a rare startup crash is now structurally impossible. - **Updates that always land.** In-app update downloads no longer fail with expired links, so stuck-on-an-old-version clients rescue themselves. - **Fixes and polish.** Effect blend modes actually render, auto layout re-flows correctly after undoing a reparent, text style edits show on canvas instantly, exporting no longer disturbs images on the live canvas, steadier hosted AI chats, and a handful of crash fixes. ## 0.1.0-beta.33 *Released July 13, 2026* - **Fixes and polish.** Glass and outlined text render their true silhouette (no more seams inside letters), dropdown hover-previews show up reliably, frame-name labels stay crisp, and deleting your last canvas no longer errors. ## 0.1.0-beta.32 *Released July 10, 2026* - **A brand-new rendering engine.** The entire canvas now draws on a native GPU engine: Metal on macOS, DirectX 12 on Windows. Rendering moved off the UI thread completely, so massive canvases stay smooth while you pan, zoom, drag, and edit. - **Liquid Glass.** A new physically-modeled glass fill with real refraction, dispersion, and edge lensing, plus one-click presets. Works on any shape: rectangles, vectors, booleans, even text. Figma Glass effects import losslessly. - **Text rendering, rebuilt.** Figma-parity sharpness at every size, real color emoji, proper RTL and complex scripts (Thai, Indic, and more), system-installed fonts, and correct variable-font weights. Text stays crisp at any zoom instead of pixelating. - **Way faster everywhere.** Canvas switching up to 20x faster, file opens parse in parallel, saves and auto-saves moved off the UI thread, and dragging, resizing, and snapping stay buttery even with thousands of elements selected. Idle battery use drops too. - **Pixel-perfect exports.** PNG, video, SVG, PDF, HTML, and session replays now all render through the engine, so exports match your canvas exactly. Text never silently drops from an export again. - **MCP Connections.** Wire external AI tools straight into Brilliant. Flip a toggle in Settings and any MCP client (like OpenCode) can drive your canvas. - **Your files, bulletproof.** All user data now writes atomically, so a crash or force-quit can never tear a .design file. Quitting always flushes everything, and older files that could open blank now load correctly. - **GPT-5.6, and a steadier AI chat.** OpenAI's new GPT-5.6 family (Sol, Terra, Luna, plus Pro) lands on both the OpenAI and OpenRouter providers, with Sol as the new OpenAI default. Models you enable in Settings now show up reliably in the picker, cancelled tool calls and oversized images no longer error, and Sonnet 5 gets its full 1M context. - **Fixes and polish.** Snappier vector editing (drags update every frame), a flicker-free eyedropper, pinch-zoom snap to 100%, a richer first-run walkthrough, and hundreds of smaller fixes. ## 0.1.0-beta.31 *Released July 3, 2026* - **Fixes and polish.** Windows cleanups: crisper swatches and eyedropper, corrected icons and shortcuts, and hosted AI chats now get their instructions. ## 0.1.0-beta.30 *Released June 30, 2026* - **Sonnet 5, and configurable models.** The new Sonnet 5 joins the lineup, and a picker lets you enable extra built-in models or bring your own. - **Richer Figma import (beta).** Figma components import as real, editable components and sets, and your design tokens come across too. - **Windows performance across the board.** Crisper strokes, no tile halos or grid artifacts, and far less jank on big canvases. - **Fixes and polish.** Lighter memory use on large canvases, steadier auto-layout and components, and assorted AI chat and canvas fixes. ## 0.1.0-beta.29 *Released June 30, 2026* - **Fixes and polish.** Custom AI providers can now see images, with steadier HTTP connections and more chat and component fixes. ## 0.1.0-beta.28 *Released June 30, 2026* - **Fixes and polish.** Steadier components and variants (reliable add-variant, variant switching, and undo), a smoother first-time provider setup, and a handful of editing fixes. ## 0.1.0-beta.27 *Released June 29, 2026* - **Components, now with variants & properties.** Bundle a component's states (size, state, type...) into one set and flip between them from a dropdown. Build sets by hand in the new Component panel, or just ask the agent. Drag in to make a variant, option-drag out to get an instance, and your overrides stick. - **Connect any AI model.** Any OpenAI-compatible provider now works, with one-tap presets for GLM, Moonshot (Kimi), DeepSeek, and local runners like LM Studio, Ollama, and vLLM. - **Guided setup, right in the chat.** No provider yet? Send a prompt and Brilliant walks you through getting connected and keeps your key encrypted on your device. A new first-run walkthrough shows you the ropes. - **Light, Dark, or System.** New appearance toggle in Settings that sticks across launches. - **Hide per fill and stroke.** The visibility eye now works on individual fills and strokes, and booleans, masks, and instances all respect it. - **Smarter agents.** The agent reuses a component library across canvases instead of redrawing, and auto-corrects common opacity, line-height, and color slips. - **Fixes and polish.** Cleaner toolbar and inspector layout, plus a long tail of component-sync and stability fixes. ## 0.1.0-beta.26 *Released June 22, 2026* - **Fixes and polish.** Refined hide & lock and design-system behavior, smoother auto-layout when dragging elements out of frames, steadier AI agents, and a hang-free startup. ## 0.1.0-beta.25 *Released June 21, 2026* - **Hide & lock layers.** New eye and lock toggles on every layer row, plus Hide/Show and Lock/Unlock in the right-click menu. Hide layers to get them out of the way, lock them so they can't be moved or selected by accident. - **Richer text controls.** Strikethrough, truncate by line count, text case (UPPERCASE, lowercase, Capitalize), and OpenType feature toggles. Apply them by hand or ask the agent. - **Stroke joins & dashes.** Set line join (miter, round, bevel) and turn any stroke dashed with dash, gap, and cap controls. - **Blend modes everywhere.** Fill and stroke blend modes now work on circles, and every blend mode resolves consistently against the canvas background. - **Sharper SVG import.** Blurred filters and alpha/luminance masks now come through correctly, along with dashed and joined strokes. - **More models.** Added Qwen3.7 Max and refreshed the OpenRouter model list. - **Fixes and polish.** Editable corner radius on agent-drawn rectangles, design-system styling preserved on copy/paste and across restarts, steadier vector editing, correct flatten/outline text on macOS, the AI chat no longer redraws elements when you reopen a session, a calmer startup toolbar, and smarter agents. ## 0.1.0-beta.24 *Released June 11, 2026* - **Brilliant is now on Windows.** The full editor, native on Windows, at parity with macOS. Custom title bar with menu bar and Snap Layouts, right-click menus, native pinch + Ctrl+scroll zoom, WebP and MP4 export, image paste, brilliant:// deep links, and in-app auto-update. - **Claude Fable 5.** Anthropic's new Mythos-class model, now the top 'Most powerful' pick across Claude Code CLI, Anthropic, and OpenRouter. - **Jump straight into the AI chat.** Playground first-run polish: a guiding shimmer, a dropdown to re-watch what the agent built, and one-click prompt replays that draw real, editable elements onto your canvas. No setup needed. - **Fixes and polish.** Refreshed social export presets, a smoother eyedropper, sturdier outline/flatten on text, and a handful of AI chat and stability fixes. ## 0.1.0-beta.22 *Released June 1, 2026* - **Claude Opus 4.8.** New 'Best' default across Claude Code CLI, Anthropic HTTP, and OpenRouter. - **Bug fixes.** .design imports no longer drop or swap in wrong image assets, clipping frames no longer export with transparent gutters around overflowing children, undo restores hug-content ancestor frames and their siblings after reparenting and nested deletion. ## 0.1.0-beta.21 *Released May 25, 2026* - **First launch lands in the playground.** New users drop into a playground session instead of an empty chat. New Dev tool hero replay. - **Steadier AI chat.** Fewer false-positive silent-stop cards, no more SVG-finalization hang, empty CLI turns retry instead of giving up. - **Bug fixes.** Design system inspector mode/brand picks stick instead of reverting, arrow heads survive rotation, chat-attachment image IDs no longer collide, export filename derives from the selected element, open-folder picker starts at the repo's parent, canvas stays interactive right after opening a repo. ## 0.1.0-beta.20 *Released May 20, 2026* - **Design system v2.** Rebrand any design in one click. New Design System panel in the right toolbar: pick a brand and watch your entire design re-skin in real time. Switch brands per element, per axis, per canvas, or per folder. Tokens are first class now; agents bind colors and sizes to your design system instead of guessing hex, so swapping palettes feels magical. A new viewer card showcases your palette, type scale, and primitives on a single page. - **Ship designs as real code.** New HTML, CSS, and React (JSX) export. Self-contained documents with web fonts included, auto-layout converted to semantic flexbox, design tokens emitted as CSS variables. Multi-stroke, inner shadows and glows, image crops, rich text spans all render the way you designed them. Drop the export into your codebase and ship. - **Try the AI chat without setting anything up.** Don't have Claude Code installed yet? Open the chat and click a suggested prompt. A real recorded agent session replays onto your canvas, building real elements you can edit. The install-Claude-Code wall is gone. - **Send to Brilliant from Figma.** Bidirectional now. Push any Figma selection straight into your Brilliant canvas over a local connection, no copy-paste. Rotation, masks, image crops, and outside-stroke positions all survive cleanly. - **Multi-page PDF export.** PDFs span multiple pages now. Text renders correctly, arrow and circle stroke caps draw on arcs and vectors, and rasterized effects no longer have those weird gray halos. - **Cleaner SVG export.** Design tokens resolve to their actual hex on export instead of dropping. Stroke positions survive a Figma round-trip. - **Tokens visible everywhere in the inspector.** Every color, size, and effect bound to a design token shows the token name and a small blue diamond indicator. Edit hex live in the fill type field. The eyedropper copies the picked color to your clipboard at the same time it commits. - **Quieter when you're not using it.** When the window is hidden or backgrounded, Brilliant pauses its visual loops and caps its caches. Real battery and CPU savings if you keep it open all day. - **Snappier on heavy canvases.** Hovering elements no longer triggers global rebuilds. Big design files feel more responsive in everyday use. - **Calmer workspace.** The space around your canvases is now a solid soft grey instead of a checkerboard pattern. New canvases also start without a background fill so they're ready to design into immediately. - **Quieter menu bar.** New 'Hide menu bar icon' setting if you don't want Brilliant sitting up there. Clicking a menu-bar item properly brings the app to the front. - **AI vector illustrations with Quiver.** Ask the agent for an icon or illustration and it streams onto your canvas as a real, editable SVG, not a flat raster image. Feed it an existing image and it'll vectorize it back to clean shapes. Connect a Quiver API key in Settings then AI Providers. - **Smarter agents.** Agents bind colors to your design system instead of guessing hex. New blueprint primitives make repeated patterns, instance overrides, and undo checkpoints much easier for them to generate cleanly. Parser is more forgiving about common slips. - **Canvas recovery.** Reopening a canvas that's been deleted now restores it from trash (or creates an empty one) instead of erroring out. Links and breadcrumbs don't go dead. - **And a long tail of fixes.** Figma vector imports, instance text alignment, gradient safety, sub-pixel sizing, repo-switch design system reloads, command palette cursor routing, file reload on external change, vector edge hover, raised file-descriptor limit at startup, plus dozens more. ## 0.1.0-beta.17 *Released April 30, 2026* - **Smarter AI agents.** Huge diagnostics improvements for agents, you should see much better results - **Reliability.** Fonts keep loading and saves keep succeeding through long sessions; first command on a fresh install no longer crashes - **Better Figma paste.** Pastes from Figma/SVG center under your cursor; imported text bounds and mask groups round-trip cleanly - **AI chat polish.** Cleaner mid-stream abort; chat chips no longer resurrect canvases you deleted ## 0.1.0-beta.16 *Released April 28, 2026* - **macOS menubar icon.** Quick access to Brilliant from the system tray - **Window UX polish.** Drag the app from any empty toolbar region; double-click to maximize; side toolbars span full window height - **Overlay mode now opt-in.** Global hotkey only summons the overlay when you turn it on; default off - **Better text input on canvas.** Composition input (CJK, accented characters) routes through the platform IME - **Codex auto-registration.** Brilliant MCP server auto-wires into ~/.codex/config.toml for OpenAI Codex users - **Bug fixes.** Blueprint rotation/shear math for skewed elements ## 0.1.0-beta.15 *Released April 28, 2026* - **Smarter agents.** Tightened blueprint docs (consistent for(...) usage, sparkline area-fill patterns) so AI generates cleaner designs ## 0.1.0-beta.14 *Released April 28, 2026* - **Faster on big canvases.** Hit testing and rendering snappier on dense scenes - **Bug fixes.** Paste with components, circle image fill rendering, SVG-in-slot blueprint race, session replay for for(...) and connect(...) blocks ## 0.1.0-beta.13 *Released April 28, 2026* - **for(...) in blueprint DSL.** AI generates repeated patterns cleanly - **connect() in blueprint DSL.** AI nails flowcharts and diagrams now - **line() in blueprint DSL.** Lines with rotation and length - **Unified lookup MCP tool.** AI explores your canvas faster - **AI chat polish.** Archive All, no focus theft on model switch, smarter silent-stop - **Streaming stability.** Designs don't shift mid-generation; images appear immediately - **Shimmer rework.** Clearer 'AI is generating' feedback - **Folder rename on create.** Matches new-file flow - **Empty frames are clickable.** Tap to select instead of falling through - **Bug fixes.** Pay-before-activate now upgrades correctly, for(...) crash, SVG-in-slot, replay re-centering, more ## 0.1.0-beta.12 *Released April 24, 2026* - **GPT-5.5 and GPT-5.5 Pro.** New OpenAI models across the direct provider and OpenRouter, with gpt-5.5 now the OpenAI default. 1M context, reasoning, Chat Completions + Responses APIs (Pro is Responses-only) - **Spatial references in AI chat.** Drop canvas positions or regions into the chat. Pick a point to insert @point(x,y), rubber-band to mention an area, chips highlight the matching spot on hover - **Settings redesign.** Two-pane layout with a category nav (Account, AI Providers, Shortcuts, About) and drilldowns with back-nav - **Haptic feedback toggle.** New 'Haptic Feedback' toggle in Settings → General. When on, subtle alignment/snap cues fire through the macOS haptic engine. Default off so the app stays quiet out of the box - **Leaner CLI context.** Old screenshots auto-prune from the rolling Claude Code log so long sessions don't bloat. Stale #hexid refs in agent directives auto-heal to the current ID - **/feedback overhaul.** Routes bug/idea/praise to the right channel, asks per-attachment before sending screenshots or logs, surfaces related past feedback as you type - **Bug fixes.** Auto-layout hug resize jitter, backdrop-blur offset during letterboxed export, AI chat crash at tail of long histories, clone-session element/canvas references, rd(0) dropped on blueprint modify ## 0.1.0-beta.11 *Released April 20, 2026* - **AI session recording.** Record any chat session as MP4/MOV by replaying the blueprint DSL. Per-message record button, /export-replay slash command, right-toolbar export section, right click export -> 'export selection as replay' - **Opus 4.7.** New model across Anthropic HTTP, OpenRouter, and Claude CLI providers - **Animated splash screen.** Breathing logo on cold start that collapses into the main UI - **Resume recovery.** When the hosted Claude CLI is interrupted mid-generation, the agent no longer loses its in-progress work on the next turn - **Tile rendering.** Flicker-free display, phantom fixes on move/drag, smoother zoom - **File explorer.** Better drag-and-drop, fixed scroll jump/height, atomic folder + asset moves (single undo step), 'show hidden files' now persists - **Click-to-focus.** Deterministic focus layering, editor stays focused through vim / search dialog, paste routes correctly - **Improved blueprint agent docs.** Better knowledge coverage so agents generate cleaner designs (including ring-shaped UIs via arcs, fractional spacing steps $spacing.1.5) - **Bug fixes.** Repo switch crash, activation screen paste/autofocus, canvas panning (left flicks, Magic Mouse touch-cancel), continuous spacing scale, cleaner install error screen, hosted CLI reconnects, replay polish (overlay scaling, transient canvas leaks, stale canvas recovery) ## 0.1.0-beta.10 *Released April 16, 2026* - **Improved onboarding.** smoother first-launch experience, faster Claude Code install ## 0.1.0-beta.9 *Released April 15, 2026* - **Claude Code onboarding.** One-click install from Brilliant for new users, aggressive CLI detection (nvm/fnm/asdf/Homebrew), /login slash command - **Bug fixes.** shader raster export ticking animation, eyedropper crash in AI input, 2-node pen vector bounds, notification rail auto-shown tracking ## 0.1.0-beta.8 *Released April 14, 2026* - **SVG import quality.** Opacity, gradient coordinate spaces, and degenerate group handling now round-trip correctly - **Bug fixes.** copy-as-SVG crash, tile rendering, vector edit in auto layout, MCP bulk clone hug sizing, image clipboard crop, and more ## 0.1.0-beta.7 *Released April 13, 2026* - **AI chat color picker.** Eyedropper button in the chat input action bar inserts picked colors as hex at the cursor position - **File explorer shows gitignored files.** Items in gitignored paths now appear at muted opacity (matching VS Code) instead of being hidden, so MCP-created canvases in build dirs no longer silently vanish - **WebGL shader code export.** All 13 Brilliant shaders (6 procedural fills + 7 image filters) ported to WebGL 2 / GLSL ES 3.0 as agent knowledge for inlining into exported HTML - **Figma import: image fill crop and reposition.** Repositioned/cropped images from Figma now import with correct framing - **Smarter canvas deletion.** Deleting a canvas now switches to the nearest sibling file of any type, only auto-creating a replacement when the repo is empty - Retry and edit moved to the bottom of AI chat messages - **Bug fixes.** SVG import, canvas deletion crash, blueprint parser, gradient drag handles, ungroup-in-auto-layout undo, and more ## 0.1.0-beta.6 *Released April 11, 2026* - **AI chat shimmer hint.** Animated border trail guides new users to the AI chat input - Auto-scroll file explorer to newly created folders - **Bug fixes.** paste-into-frame positioning, font preview revert, font size on frame descendants, breadcrumb truncation, file explorer paste ## 0.1.0-beta.5 *Released April 10, 2026* - Spacebar hold-to-move selection rectangle during drag - Cmd+drag to target parent frame during reparenting - Figma import via copy plugin - **Bug fixes.** tile rendering, OAuth retry, undo ungroup in auto layout, AI session recovery ## 0.1.0-beta.4 *Released April 9, 2026* - **Fix activation expiry.** Users with expired activation tokens are now prompted to re-activate instead of silently falling back to Free plan - **Top toolbar window drag.** Drag the window from the top toolbar - **Bug fixes.** @mention selection, mixed property fields, vector hit testing, dark mode chat rendering, action bar recovery, and startup stability ## 0.1.0-beta.3 *Released April 9, 2026* - **Scale tool (K).** Proportional content scaling that scales stroke thickness, corner radii, font sizes, and effects with size - **Blazing fast command palette search.** Optimized global search across commands, files, layers, fonts, and AI chats - **Getting Started canvas.** Welcome canvas on first launch to help you get started - **Per-node stroke caps.** Set different start and end caps on individual vector endpoints - **Subgraph drag in vector edit mode.** Click and drag disconnected regions as units - **Device preset dropdown.** Quick frame sizing for common device dimensions - **Resizable AI chat input.** Drag divider to expand the input area - **Hex color detection in AI input.** Inline color chips with color selector integration - **AI context usage indicator.** See how much context your chat session is using - **Save As / Import.** Save designs as native .design YAML and import with auto format detection - **Measurement guides rework.** Edge-center anchoring, overlap support, and Alt strip - **Selection performance.** Select-all on 35K elements: 73s → 14ms - Center vertically/horizontally buttons in alignment row - Auto-archive stale AI chats after 7 days - **Figma import improvements.** 9 additional features now imported from Figma files - Clickable element mentions and attachment chips in AI chat ## 0.1.0-beta.2 *Released March 26, 2026* - **Unified AI chat panel.** Completely redesigned chat experience with collapsible explorer, snap layouts (full/half screen), WhatsApp-style session ordering, drag-and-drop files, @mention styling, and cycling tips - **Global search overhaul.** Unified Cmd+K search across commands, files, layers, fonts, and AI chats - **Batch generate_image.** Generate AI images for multiple elements in parallel with per-target prompts and shimmer progress - **Massive rendering performance boost.** New tile-based engine makes 50-100K+ element canvases smooth and responsive - Auto layout: wrap support - Resolution preset export (replaces scale-based), with PDF and SVG export fixes - Text-to-path: Outline Text and Flatten Text commands (Cmd+Enter) with per-character colors and font weight support - RTL text direction support for Hebrew and Arabic content - **Slash commands.** /usage, /model, /rename, /copy, /archive, /new, /clear, /help, /feedback - **Notification rail.** Hover top toolbar for agent completion notifications and more - Reactive Grid interactive shader with 5-color support and presets - **Major boolean and mask fixes.** VectorPath boolean engine, flatten accuracy, nested boolean/mask support, live drag updates - **HTML import improvements.** Absolute positioning, flex-wrap, margin-auto, per-side borders, rgba opacity - **Nested boolean/mask support.** Boolean parents can now be used as elements inside other boolean ops and masks - **Parent-targeting selection.** Click selects parent, Cmd+click deep selects ## 0.1.0-beta.1 *Released March 13, 2026* - Private beta is finally live! ## 0.1.0-alpha.32 *Released March 11, 2026* - **Image filters.** 6 new filter paint styles (Halftone, Pixelate, Duotone, Dither, Color Adjust, and more) with presets, CMYK mode, editable colors, and color picker support - **Interactive shader fills.** New mouse-reactive shader system (Magnetic Glow POC) with performance-optimized proximity culling - **Shader & image filter support for text elements.** Shader fills/strokes and image filters now work on text - **Nano Banana 2 support.** Generate images directly on canvas via Google's Gemini Image model (try '#image' in the AI chat) - **Math expressions in property fields.** Type math like 200+50, half, double, 2x, round 8 directly in W/H/X/Y fields - **GPT-5.4 and GPT-5.4 Pro.** New model support for AI chat - **Boolean operations improvements.** Major stroke geometry fixes (inside/center/outside position, arc caps, ring sectors, vector shapes) - **Claude Code chat UX.** Prompt history with up/down arrows, reverse search (Ctrl+R), double-click to rename sessions, double-ESC to stop, /feedback command, resizable panels - **Performance.** Eliminated multi-second freeze when dragging heavy elements, paste and duplicate stay fast on large selections, and saves no longer block the UI - **create_html tool.** New HTML-to-canvas creation path for AI agents, supporting full CSS layouts, inline SVG, percentage sizing, and icons - **Mask system (beta).** New mask parent type: top element clips the shape, everything below is masked (Cmd+Option+M) ## 0.1.0-alpha.31 *Released March 5, 2026* - Color Adjust filter for images (photo-editor style sliders: brightness, contrast, saturation, clarity, sharpness, vignette & more) - Comprehensive Figma import upgrade (page/selection filtering, vector paths, region fills and more) - Blend modes for elements, fills, strokes, and effects (with SVG/PDF export) - Viewport culling (big performance boost on large canvases) - Option+drag symmetric resize from center - Design system tokens now support stroke thickness and corner radii - Massive vector editing upgrades and bug fixes (planar face traversal, per-region fills, axis-locking, node duplication) - Stroke-native inner shadow/glow rendering for vector strokes ## 0.1.0-alpha.30 *Released March 3, 2026* - Export to MOV & MP4 (try it with shaders!) - Import from figma (try prompting 'import ') - New recreate from image & website built-in skills - Boolean operations (early beta): Union, Subtract, Intersect, Exclude ## 0.1.0-alpha.29 *Released March 2, 2026* - Export to MP4 (beta) - Radial gradient paint style out of beta - Angular gradient paint style for strokes/fills (beta) - Double click while holding option to show gradient handles - cmd + left/right arrow to move between previous and next AI chat - Significant skill improvements (generative UI results should be much better now) - Improved chat compaction for non-claude-code agents - Optimized chat context management (less tokens, lower cost, better results) ## 0.1.0-alpha.25 *Released February 28, 2026* - Radial gradient paint style for strokes/fills (beta) - Improved claude code integration ## 0.1.0-alpha.22 *Released February 28, 2026* - Improved claude code integration - Agents can now create SVGs with shader fills - Improved fill picker + new image fill option - dotfiles now hidden by default + new 'toggle dotfiles visibility' command ## 0.1.0-alpha.21 *Released February 28, 2026* - Shader performance up to 120fps (dynamic frame rate between 0-120fps based on live performance) - Improved agentic skills, tools and context management ## 0.1.0-alpha.20 *Released February 27, 2026* - Private Alpha is out! # Section: Blog posts --- Source: https://brilliant.design/blog/blueprint.mdx ## The problem Agents are extraordinarily good at code. It's the single largest bucket of training data they have. Give Claude a short syntax overview for any programming language and it will produce thousands of lines of accurate syntax without effort. But design tools are not frontend code. They're not code at all, really. Frontend code is optimized for being written once and running many times. Source goes in, rendered UI comes out, and the round trip from "I changed a color" to "I see the new color" is a build step. Design tools flip that. They're optimized for thousands of tiny interactions per session, each one updating a complex rendering graph in milliseconds, each one undoable, each one immediately reflected back on screen. The data models reflect that difference. A design tool's model has auto-layout solvers that propagate on every property change, hit-test trees, selection state, snap guides, layer ordering that affects both stacking and z-order. A design isn't a static tree like a JSX document. It's a live graph built to support continuous direct manipulation. And there's a second problem: verbosity. To version-control a design, we need an on-disk format that agents and humans can review. Something that diffs cleanly in git. At the time, we had landed on YAML: `.bl` files were plain-text YAML, one property per line, legible at a glance. ```yaml elements: - id: fb61011f9de5d310 type: frame name: "Hero" fills: - id: bac3eb9a0caf3ac6 color: "#F8F8F8" cornerRadius: 16 ``` But the underlying data is heavy. Every element has dozens of properties, many with sub-properties, many referenced by ID across the file. A single card with a few children runs to dozens of lines of YAML. Every line the agent writes costs tokens. Every line of response costs tokens. On a full design the agent ends up spending most of its context on field names rather than on decisions. So: we need a representation that's readable enough for version control, fast enough to keep up with continuous manipulation, and light enough that agents don't burn their context describing what they want. ## What we tried Two attempts. Both taught us something. ### Commands The first try looked obvious from inside the codebase. Brilliant already had a robust command system: every user action the app can do (move, resize, fill, group, align, flatten, and several hundred more) is expressible as a typed Command. Why not let the agent call commands directly? ``` ChangeToolCommand SetColorCommand ResizeCommand RotateCommand GroupCommand AlignCommand ``` The approach explodes fast. Creating one card with a heading is five or six command calls. The codebase carries several hundred typed Command classes, each with its own argument schema, and "which command for which thing" is a lookup the agent has to learn from scratch. Worse, the agent can't express relationships compactly. Every property goes through its own named operation. Token cost climbs linearly with the amount of design, and the agent's attention gets eaten by plumbing. ### YAML diffs The second attempt was the opposite direction. Since `.bl` files already lived as YAML, why not let the agent emit YAML directly and apply it to the canvas? Similar to what agents do with code. They read the contents of a file, make edits, and write it back. The agent can express relationships directly through references. The app can diff the old and new YAML, figure out exactly which properties changed, and apply only those to the canvas. Sounds fantastic, right? But what happens when you want to undo changes? Or when the agent writes something invalid? Does the user have to sit and wait while the whole file is re-parsed and re-applied on every turn? What about auto-layout rules that depend on multiple properties being set together? The agent would have to guess the right order of operations to avoid intermediate invalid states. The YAML would be verbose, and the agent would have to learn the entire schema. Also: YAML that's nice to read is not YAML that's nice to write at scale. The example from the last section, the card with one fill and a corner radius, was eight lines. Add a few children and a couple of variants and you're back to dozens of lines per agent turn. ## The first revelation At some point the framing clicked. We'd been trying to make the same representation work for two completely different audiences. Version control needs structure, labels, and explicitness. Agents want density, regularity, and anything that resembles a programming language they've seen before. Human-readable and agent-readable are not the same thing. We looked at what agents are genuinely good at. Code. Specifically, unfamiliar programming languages. Give Claude a page of syntax for a language it's never touched and it will produce working programs on first try. All programming languages share the same underlying machinery: tokens, expressions, scoping, references, nesting. Any reasonable syntax is just another skin on ideas the model has already internalized very deeply. Here's the thing, though. No programming language exists for 2D vector design. _yet_. ## Brilliant's Blueprint DSL Blueprint is a programming language for drawing, optimized for what agents are good at. It's line-oriented, deeply abbreviated, and structurally regular. It has types (rectangles, circles, text, frames, groups, auto-layout containers, icons, vectors, lines and arrows, arcs and rings, boolean groups, masks, components and instances), properties (position, size, fills that can be solids, gradients, images with filters, live GPU shaders, or refractive glass; strokes, corner radii, effects, auto-layout, styled text ranges down to a single word), and references. The core grammar fits on a page or two, and the full language now covers essentially everything the editor can do by hand. Each element is one line rather than a stanza of YAML. Here's the same card from the YAML example, in Blueprint: ```blueprint fr f[(#F8F8F8)] rd(16) "Hero" ``` One line. Same element. Same semantics. About five times fewer characters, and eight lines of YAML collapsed to one. That ratio only widens as designs grow, because every element in Blueprint stays a single line while its YAML equivalent keeps sprouting nested keys. ## The feedback loop Every time an agent sends a line, it gets checked and answered with feedback designed to be read, not just obeyed. Every line is validated before it touches the canvas. Wrong property for a type, missing required argument, unresolvable reference, an impossible auto-layout rule: each comes back as a diagnostic with a severity (error, warning, info), a category (syntax, property, layout, reference, composition), a stable code (`B101` and friends), a human-readable message, and a concrete suggestion. A fatal line error stops that line; a single bad property token drops just that property and the rest of the block still applies. Valid lines apply to the canvas as real, editable elements, with auto-layout, components, and the rest of the graph updating together. And once elements are on the canvas, a composition linter inspects the real geometry for design-intent issues, with its own `C`-prefixed codes. A validation diagnostic the agent actually receives looks like this: ``` B101: spaceBetween is only valid on the main axis (x for al(h)). Move sb to x(): x(sb) y(c). ``` Code, sentence, fix. Three beats. The agent reads it, adjusts the next line, and moves on. Brilliant has an unfair advantage for this loop. Unlike frontend code, we don't need to spin up a browser to snapshot the result. Every response can also carry an instant visual export of the affected elements. The agent sees exactly what it made, next to the exact feedback on what went wrong. And layers that look fine individually but don't work together? That's the composition linter. It catches things like: - Text with no fill, defaulting to white, rendered on a light background. Invisible text. (`C201`) - Elements that resolve to `0 x N` or `N x 0` after layout. Collapsed. (`C301`) - Sibling elements with the same name under the same parent. Duplicate Row, duplicate Row, duplicate Row, the agent got stuck. (`C101`) - Text that overflows its parent, or content that spills outside a clipping frame. - A block dropped far away from the rest of the work, stranded off in empty canvas. Each maps to a diagnostic code, each runs only on the elements the agent just created or modified, and each comes back with a suggestion the agent can act on without another round trip. ## Examples Three real exchanges, to show how the loop feels. These run with no design system active, so colors are plain hex. ### Invisible title Agent sends: ```blueprint fr f[(#FFFFFF)] rd(16) "Card" t("Welcome",Manrope,24,sb) "Title" #title ``` Brilliant applies both lines. The frame renders, the text renders, and the composition linter catches the problem: ``` ✓ 2 lines applied. ⚠ C201: "Title" (text) has no fill. Defaults to white, may be invisible on light backgrounds. ``` Along with the warning comes a rendered preview showing the empty-looking card. The agent sees both, amends the text line, and resends: ```blueprint #title f[(#09090B)] ``` This time the lint passes. The title renders black on white. ### A malformed alignment Agent sends: ```blueprint al(h,y(sb),x(c),g(12),pad(16)) "Toolbar" ``` The alignment axes in Blueprint are physical: `x` is horizontal, `y` is vertical, and they never flip with layout direction. `sb` (space-between) only makes sense on the main axis, which for a horizontal row is `x`. The validator rejects the line before anything touches the canvas: ``` ✗ 0 lines applied (1 error). B101 (line 1): spaceBetween is only valid on the main axis (x for al(h)). Move sb to x(): x(sb) y(c). ``` The agent applies the fix without guessing: ```blueprint al(h,x(sb),y(c),g(12),pad(16)) "Toolbar" ``` Applied cleanly. ### A hug parent with a fill child Agent sends: ```blueprint al(v,g(8),pad(16)) s(hug,hug) "Card" fr s(fill,120) f[(#FFFFFF)] rd(8) "Image Slot" ``` Brilliant applies both lines. The image-slot frame resolves to `0 x 120`, because a `fill` width inside a `hug`-width parent has nothing to stretch to. Execution succeeds, and the linter fires: ``` ✓ 2 lines applied. ⚠ C301: "Image Slot" is 0×120. fill inside a hug parent collapses to 0. Use a fixed size, or give an ancestor a fixed width on that axis. ``` The agent gives the card a real width so the child has something to fill: ```blueprint al(v,g(8),pad(16)) s(280,hug) "Card" ``` The frame fills to 280 and renders correctly. ### What the loop looks like over a long run Three exchanges isn't much, but extend it. A real design is many Blueprint lines, arriving in a few streamed batches. Somewhere in there the agent will hit a warning it hasn't seen before. It gets the diagnostic, the suggestion, and the preview. Next time that same shape comes up, the agent writes the correct version on the first try. The improvement happens inside the session, with nothing retrained. A model fluent in code but new to Blueprint stumbles on the first unfamiliar shape, reads the diagnostic, and writes it correctly the next time it comes up. The agent stops repeating a mistake the moment the feedback names it, one diagnostic at a time. ## The result A grammar that's extendable, concise, and token-efficient. A compiler that teaches the model which parts of the grammar it hasn't learned yet. A visual feedback loop that collapses the "did my code do what I thought" gap from "rebuild, deploy, reload" to "here's a PNG of what you just made". Costs came down and design accuracy went up, for the same reason in both cases: the agent spends its tokens on decisions instead of field names, and it stops repeating mistakes the moment the compiler names them. Sessions that used to drift off-task now stay on-task, because every turn ends with concrete feedback instead of silence. ## The bigger picture Everything so far has been *creation*. New elements, new properties, new layouts. That's half of what a design tool does. The other half is *manipulation*: moving elements between containers, stretching them, duplicating them, retheming them, restructuring the tree. If the agent can't express those operations densely, all the token efficiency we worked for evaporates at edit time. Blueprint has a compact vocabulary for manipulation, too. **Reparent** in one line: `#title parent(#new_card)`. The element keeps its properties, its overrides, and its ref. Only its place in the tree changes. **Clone** for a standalone duplicate: `clone(#card) p(320,0) "Card 2"`. **Instance** for a linked copy that follows its master: mark a source element as a component, then `inst(#card) p(320,0) #pro`. Instances can **override** specific children without breaking the link: ```blueprint inst(#card) p(320,0) #pro override(#title) t("Pro Plan") override(#desc) t("For growing teams") ``` Edit `#card` later and every instance updates, except the pieces the instance overrode. When the variation is discrete rather than free-form, a component *set* declares its axes up front: `comp "Toggle" axes[state[on,off]]`, one variant frame per state, then `inst(#toggle) at(state(on))` to place a configured copy. **Scale** an element and everything inside it proportionally with `#hero scaleTo(w,1200)`. **Recolor** every fill and stroke under a parent without touching the parent itself with `#card cb(#0F172A)`. **Nudge** properties relatively with `#hero p+(0,40) rd+(4) o+(-0.1)`, useful when the agent is reasoning in "a bit more" terms rather than absolutes. And the everyday structural edits each land in one line: `delete(#old_row)`, `#hero front`, `#divider back(2)`, `ungroup(#button_group)`. Nothing that couldn't be another API call, but the grammar makes them feel native. The language even carries time travel. A `// label` comment on any line snapshots an undo checkpoint as it executes, and a later call can say `undo("structure revised")` or `redo("dark variant added")` to jump between named states. An agent that realizes it took a wrong turn three edits ago rewinds by name instead of reconstructing state by hand, and the user's own undo history stays coherent alongside it. There's one more audience Blueprint has to serve: a design system. When one is active, every color, size, and font slot is a `$token` that resolves through the current brand and mode rather than a bare value. ```blueprint al(v,g($spacing.md),pad($spacing.lg)) s(280,hug) f[($color.surface)] rd($radius.lg) "Card" t("Real-time sync",$font.family,$font.size.lg,b) f[($color.text.primary)] ``` `$color.surface` and `$color.text.primary` aren't fixed colors; they're roles. Flip a subtree to dark with `ds(, theme(dark))` and every token re-resolves: the surface goes dark, the ink goes light, and nothing in the layout changes. Palette stops run a presence ramp from `$primary.hint` to `$primary.intense` (loudness against the surface, not brightness), and spacing, radius, and font size follow a t-shirt scale from `xs` up. Change the brand and every reference in the file tracks the new values. This is what replaced the old ad-hoc seed variables: tokens that bind to a real design system instead of hard-coded hex. And the design system itself is authored in the same family. A brand is a `ds_file` with a modes matrix (light and dark, density, accessibility), generator functions that expand one seed color into a full ramp, and composite tokens like `typography.h1` or `shadow.md`. Inheriting the default catalog, a usable new brand is one to three lines. The agent doesn't just consume tokens; it can author the system they come from, in the same session, in the same language. Each of these goes through the same checked path against the current canvas, so there are no special APIs, no order-of-operations dance between related properties, and no stale references. The agent says "reparent", "clone", "scale", "override", and the canvas updates in one atomic step. That's the point of the language. Not just creating elements, but restructuring and retheming them, at the same density the language was designed for. ## The second revelation Everything above still frames Blueprint the way we originally built it: the agent's language, sitting beside the "real" format. That framing didn't survive contact with reality. We built Blueprint for agents and kept YAML for humans, and then watched what people actually did. When a design came up in review, everyone read the Blueprint. One line per element beats a stanza per element in a git diff, too: a color change is a one-line diff instead of a hunk. The human half of our two-representations split quietly stopped earning its keep, and the "revelation" this post was named for turned out to be scaffolding. So we made it official. Today a `.bl` file *is* Blueprint: a version stamp, a few canvas scalars, and then one line per element as the single source of truth. ``` bp:v1 canvas: backgroundEnabled: false --- 9717d785f6c6ad6b al(v,g(0:$spacing.none),pad(0:$spacing.none)) p(0,0) s(1440,900) f[(1e853251,tok(color.surface,#F8F8F8))] ``` That's a real file off a real disk. Stored lines carry both halves of every token binding (`tok(color.surface,#F8F8F8)` is "the role, and what it resolved to"), so a file opens correctly even before its design system loads, and the diff still shows you the semantic change. The same lines are what the desktop writes to disk, what the cloud stores, what multiplayer sends over the wire, what the web editor edits, what git diffs, what agents write, and what they read back when they inspect a design. Import a Figma file and what lands on disk is Blueprint source. Export doesn't re-derive from a second model, because there is no second model. One grammar, every job. Old YAML files still open, and save forward into the canonical form. What made collapsing to one representation safe is that we treat Blueprint like a programming language, because at this point it simply is one: a versioned grammar (that `bp:v1` stamp), a written spec, a migration policy for grammar changes, and a losslessness law that is proven rather than asserted. The same Blueprint produces the same design, byte-exact, forever, and a standing conformance suite holds that property across every element type and every property. That guarantee is what lets a file format, a wire format, and an agent language be the same artifact without anyone getting hurt. ## Design as source Two audiences read a design language: the agents of today and the models of tomorrow. Today, the language is how everything talks to the canvas: the built-in chat, Claude Code, Cursor, or any MCP client, several of them at once, each with its own labeled cursor on the same canvas. And because reading uses the same language as writing, "review this design", "diff these two versions", and "explain what changed" are text problems now, the kind agents are already great at. Tomorrow's models train on the open web, and Blueprint is there to be learned: the spec is public, the grammar is versioned, and every public Brilliant project is a corpus of real designs written in it. A design stops being a binary blob with an exporter bolted on. It's source. That was the bet in choosing to build a language instead of an API, and the file format was the last thing it had left to win. --- Source: https://brilliant.design/blog/byok.mdx Brilliant has an AI chat built into the canvas. You describe what you want, the agent draws it, and everything it makes is real, editable geometry the moment it lands. The interesting part is what sits behind that chat box: there is no Brilliant model, no Brilliant inference bill, and no Brilliant server in the path between your prompt and a response. You bring your own key, and your traffic goes straight to the provider you picked. That is a deliberate design decision, not a gap we plan to fill later. This post is about why we made it, how the multi-provider plumbing works, where your keys actually live on disk, and the consent rules that govern anything leaving your machine. ## Bring your own key, and what that buys you "Bring your own key" (BYOK) means every request the chat makes is authenticated with your own API key or your own local Claude Code CLI, and it is sent directly to that provider's endpoint. Nothing is proxied through us. You pay your provider, your prompts reach only that provider, and we never sit in the middle holding a copy. We think of this as a trust decision more than a billing one. When an AI feature routes through a vendor's servers, you are trusting that vendor with the contents of every prompt, every canvas snapshot, and every key. BYOK removes us from that equation entirely. The people who can see your traffic are you and the model provider you already chose to trust, and no one else. For a tool that can see your work in progress, that felt like the only honest default. ## One interface, many backends Under the hood, the chat is a multi-provider system. Every backend implements the same small contract: take a conversation, stream a response back in one shared shape the rest of the app understands. A provider's job is translation between that shared shape and whatever the vendor's API actually speaks, so the chat UI, session persistence, and the tool loop all work the same no matter which vendor answered. Today that covers five first-class provider families, each selectable per session from the model picker: - **Anthropic** over the Messages API, authenticated with `x-api-key`. - **OpenAI**, which speaks two dialects. Most models use Chat Completions; the Codex models and the compute-heavy "pro" configurations route through the newer Responses API instead. Same provider, two request shapes, picked automatically from the model's metadata. - **Google Gemini**, keyed by query parameter or by Google OAuth. - **OpenRouter**, which reaches many vendors through one gateway, speaking the same Chat Completions dialect just pointed at a different base URL. - **The local Claude CLI**, wrapped as a provider that delegates to the `claude` binary as a subprocess. It manages its own history and never touches our keychain, because the CLI holds its own credentials. On top of those, you can register any OpenAI-compatible endpoint yourself. That is the path for local runtimes like LM Studio (`http://localhost:1234`) and Ollama (`http://localhost:11434`), or any custom server. Those local providers are keyless by design: the traffic never leaves your machine, so there is nothing to authenticate. Above the providers sits the agentic loop. HTTP backends can emit tool *calls* but cannot run them, so the orchestrator executes each call and feeds the result back as more conversation, turn after turn, until the model stops asking for tools. The tools themselves come in two flavors: file and shell and network tools and the canvas tools that actually draw. The CLI provider skips this loop entirely because it runs its own. The upshot: adding a provider is almost entirely a matter of mapping its stream onto that shared shape. The rest of the app does not change, and neither does the privacy posture, because every backend is BYOK by the same rule. ## Where your keys live A key you paste has to go somewhere. It goes into your operating system's secure credential store, never into a plaintext config file we manage. On macOS, keys are written to the login Keychain, filed per provider, the same OS credential store desktop apps reach for when they hold credentials that matter. On Windows, keys go into the Windows Credential Manager as generic credentials, which the OS encrypts at rest for the current user via DPAPI. Both platforms are kept in lockstep. If you would rather not paste anything into the app at all, the credential store also reads standard environment variables as a fallback: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, and `OPENROUTER_API_KEY`. If one of those is set and the Keychain has no entry, we use it, and it stays wherever you set it. One rule holds across all of this: a key you paste never appears in a chat transcript, in your conversation history, or in a prompt draft. When you set one up from inside the chat, the pasted value goes straight into the credential store and is never echoed back into the conversation. Your secrets and your prompts live in separate places, and they stay that way. ## Nothing leaves without your say-so BYOK settles *where* your traffic goes. The consent model settles *what* travels with it. The rule is simple: outbound context is explicit-consent only, and nothing is attached on its own. We do not quietly ride along a screenshot of your screen, your system information, your app version, a list of your recent files, or any telemetry. Beyond your actual prompt, the default is to send nothing extra. There are exactly three things that can go out with a message, and you control all three: 1. **First-message canvas context.** On the first message of a session, we include a *text* snapshot of the canvas so the model can see the work in progress: a structural outline in Brilliant's Blueprint format, the design-system state, the component catalog, the element count, and the current selection. It is text, not an image, and no screenshot of your screen is taken for it. Later messages send only what changed. Want to send a prompt with no canvas context at all? Start the chat in an empty workspace. 2. **Attachments you add.** Anything attached is something you chose to attach, and each one shows as a removable chip above the input before you send. An element attachment sends that element's Blueprint plus a render of just that element. An image or file attachment sends exactly what you added, nothing more. 3. **The self-review screenshot.** After the agent applies a block of changes, we render a screenshot of *those changed elements* and hand it back to the model so it can check its own spacing, contrast, and alignment. It is a render of the design content only, never your screen or other apps, and text-only models never receive it. And when no provider is connected at all, the chat runs in Playground mode, which replays bundled demo conversations locally with no network call and no model request whatsoever. The full, precise version of this policy, including exactly what is never sent, lives in our [privacy and consent](/docs/ai/privacy-and-consent) docs, and it is the contract the code above enforces. ## The local bridge runs on loopback There is one more piece worth being precise about, because "local AI tool" can mean a lot of things. Brilliant embeds a small MCP server so external agents (Claude Code, OpenAI's Codex, and other MCP clients) can drive the canvas the same way the built-in chat does. That server is deliberately local-only. It binds to `127.0.0.1`, the loopback address, on port 3333, and serves a single `/mcp` endpoint. Loopback means the socket is reachable only from your own machine. A laptop on the same coffee-shop wifi cannot see it, because loopback traffic never touches the network interface. The access boundary is the bind address itself: agents you run locally can connect, and nothing off-box can. That is how an external coding agent ends up editing your Brilliant canvas without any of it going through us. The agent runs on your machine, talks to a loopback port on your machine, and if that agent calls a model, it does so with your key by the same BYOK rule as everything else. ## Why we built it this way We could have shipped a hosted model, put a proxy in the middle, and billed for inference. Plenty of tools do. We chose not to, because the moment your design work and your prompts flow through someone else's servers, you have to trust that someone. BYOK collapses the list of parties who can see your work down to two: you, and the provider you already picked. Multi-provider support keeps that from being a cage. Use Anthropic today, a local Ollama model tomorrow, OpenRouter for something exotic next week, all behind the same chat box and the same consent rules. Your keys sit in the OS vault. Your canvas stays yours until you decide, per item, to share a piece of it. That is the whole idea, and it is wired all the way down. Want the specifics on managing keys and switching models? See [Providers and models](/docs/ai/providers-and-models). Curious what the agent can actually do once it is connected? See [What the AI can do](/docs/ai/what-the-ai-can-do). --- Source: https://brilliant.design/blog/design-tokens-with-modes.mdx Most design-token systems are a spreadsheet wearing a trench coat. A giant table of names mapped to hex values, a second table for dark mode, a third for the "compact" density someone asked for once, and a maintainer whose job is keeping all three in sync by hand. It works right up until you change your brand color, at which point you get to do it forty times. We wanted something else. In Brilliant, a design system is a short program. You author a handful of lines in a small DSL, and a compiler turns them into every stop, every role, and every per-mode variant your designs will ever reference. Change one seed, and the whole thing re-derives. This post is a tour of how that works: the authoring file, the OKLCH color math underneath it, the semantic scales that name the outputs, and the piece we're proudest of, modes that are transforms rather than duplicated palettes. ## A design system is a file Every Brilliant project has a `Styles/` folder with one file you actually edit: `default.styles`. It's plain text, it diffs cleanly in git, and it carries the entire design system for the project. Here's the top of the one we ship by default: ``` modes { theme: [light, dark] density: [comfortable, compact] accessibility: [standard, high-contrast, large-text] } primary: boldness(color(#0080FF)) secondary: boldness(color(#FF3377)) tertiary: boldness(color(#FF9900)) quaternary: boldness(color(#FFDD00)) neutral: boldness(color(oklch(55.6%, 0, 0))) ``` Four brand colors, one neutral, and a declaration of which modes exist. That's a real, working fragment. From those five color lines, the compiler generates hundreds of tokens. The reason it's a file and not a settings panel is the same reason Blueprint (our design DSL) is a language: text is the format both humans and agents are fluent in. An engineer can review a design-system change in a pull request. An AI agent can author a whole brand as a few override lines. And because elements never store resolved colors (they store token references like `color.primary` and re-resolve at paint time), one edit here ripples through every canvas that references it. ## Source in, token table out The `.styles` file is compiled before anything reaches the canvas: each seed expands into its full family of stops, the roles get named, and the per-mode behavior is baked in. Nothing in the design file is a value your elements copy. Elements hold live references, and they re-resolve per token and per mode at paint time. ## OKLCH, and why generated scales need it When you hand us `#0080FF` and ask for an eleven-step ramp from near-white to near-black, the naive approach is to interpolate in sRGB. Don't. sRGB is perceptually lopsided: equal numeric steps produce wildly unequal jumps in apparent lightness, and hues drift and muddy as you darken them. You get a ramp that looks even in a table and terrible on a screen. So the compiler expands seeds into perceptually even OKLCH ramps. OKLCH is a cylindrical form of the Oklab color space, with three axes: L for perceptual lightness, C for chroma, and H for hue. Its defining property is that its lightness axis tracks how light a color actually looks to a human eye, so a ramp built on evenly spaced L values reads as evenly spaced. The ramp itself is eleven steps, numbered `50, 100, 200, ... 900, 950`. Your seed IS the `.500` stop, exactly, byte for byte. That convention is deliberate: it lines up with Tailwind v4's 500 stops, so a brand color you enter as its natural mid tone lands where you'd expect. The light end holds near white, and the dark end is lifted off pure black so the bottom three stops stay distinguishable from each other and from the void. Here is the default `primary` seed expanded into its actual generated ramp: ![The primary seed color expanded into the 11 primitive stops, from a near-white .50 to a near-black .950, with the seed marked at .500](/docs/images/diagrams/ds-oklch-ramp.webp) There's one perceptual subtlety we handle for neutrals. Grays with almost no chroma want to be lifted lighter on the top half of the ramp than a saturated hue would (Tailwind hand-tunes its neutrals the same way), so near-achromatic seeds get an airier light half automatically. Our default `neutral` is `oklch(55.6%, 0, 0)`: zero chroma, pure lightness, the workhorse behind nearly every surface and label. ## Semantics: naming the stops Raw stops like `primary.500` are precise but anonymous. Nobody wants to remember that "the hover state should be step 200." So the generators wrap primitives in a semantic layer that maps role names to stops. There are three vocabularies, and they're used uniformly across every domain they touch. **Boldness** is nine roles for anything with a low-to-high intensity: `hint, faint, subtle, soft, mid, firm, bold, strong, intense`. The center is `mid`. On a color scale, `mid` is step 500, so `$primary` bare resolves to `primary.mid`, the tone you seeded. Boldness drives color tone, font weight, stroke width, and opacity alike, so `font.weight.bold` and `primary.bold` speak the same language. **T-shirt** sizing handles anything on a size ramp: spacing, radius, font size. The vocabulary runs `xs, sm, md, lg` on up through `20xl`, because marketing layouts genuinely want section padding in the hundreds of pixels. Here's spacing and font size from the default file: ``` spacing: tshirt(number([4, 8, 12, 16, 24, 32, 48, 64, 96, 128]), min: { none: 0 }) font.size: tshirt(number([12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 80, 96, 128]), transforms: { accessibility.large-text: shift(+1) }) ``` **Looseness** is six roles for line height and letter spacing: `none, tight, snug, normal, relaxed, loose`. On top of the generated roles sit plain aliases, the chrome layer you actually build interfaces from: ``` color.surface: neutral.hint color.on-surface: neutral.strong color.primary: primary.mid ``` A surface is a surface. Swap the brand seed and `color.primary` follows without anyone touching the alias. ## Modes are transforms, not palettes Here's the part we like. A second palette for dark mode is a maintenance tax and a lie: it claims your dark theme is a genuinely different set of decisions when almost always it's your light theme, reflected. So we don't store a second palette. We store a transform. Each semantic generator carries an ordered list of `(mode → operation)` transforms. At resolve time, the resolver starts at a role's base stop index and applies every operation whose mode is currently active. Three operations exist: - `shift(N)` moves N stops along the scale, and multiple active shifts sum. - `mirror` reflects the index around the middle stop. This is how dark mode flips a color: `primary.hint` (near white in light) mirrors to a near-dark stop in dark, while the center stays put. - `outward(N)` pushes N stops away from center toward the nearer end. Darks get darker, lights get lighter. This is high-contrast mode. Generators come with sensible defaults, so you rarely write transforms by hand. A `boldness(color(...))` seed defaults to `theme.dark: mirror` plus `accessibility.high-contrast: outward(1)`. A `tshirt` scale defaults to `density.compact: shift(-1)` and `accessibility.large-text: shift(+1)`, so compact density quietly tightens every spacing and radius by one stop while large-text loosens it. You override a generator's transforms explicitly when you want to (font size above opts out of the compact shrink, because shrinking text for density is a readability regression), or opt out entirely with `transforms: none`, which is exactly what `radius` does. The mirror in action, resolved by the real engine in each theme: ![The nine boldness roles of the primary blue ramp shown twice, one row resolved in light mode and one in dark mode. The ramp mirrors in dark mode while mid stays the same color in both](/docs/images/diagrams/ds-boldness-mirror.webp) The payoff is that a mode switch is an index-shift program over one palette, not a lookup into a second one. Add a brand and you inherit all of this for free; the brand file is a few seed overrides and every mode keeps working. And because resolution is per token and per mode, a single element can even carry its own mode overrides against the same shared scales. ## Composite tokens Some decisions are bigger than one value. A heading is a size, a weight, and a line height together; a believable shadow is several layered drops. Those are composite tokens, and the DSL has first-class forms for both: ``` typography.h1: { fontSize: font.size.3xl, fontWeight: font.weight.bold, lineHeight: 1.2 } shadow.md: [ drop(y: 2, blur: 4, spread: -1, color: rgba(0, 0, 0, 0.06)), drop(y: 4, blur: 6, spread: -1, color: rgba(0, 0, 0, 0.10)), ] ``` A typography token is a record of font fields; a shadow token is a list of `drop(...)` layers. Elements bind to them by reference, so editing `shadow.md` once restyles every card that uses it. Notice the shadow colors are absolute rather than token roles, and on purpose: a shadow should stay dark whether it falls on a light or dark surface. Point it at a brand role instead and it would cheerfully invert in dark mode and turn into a glowing halo. ## Resolution at paint time Tie it together and the whole system is lazy. Elements store token references, never resolved values. At paint time a render context carries the active canvas, the active modes (say `{theme: dark, density: compact}`), and an optional brand, and every token-bound property resolves through it. Flip a mode in the inspector, or edit one line in `default.styles`, and the change re-resolves through every binding at once, with no baked values to hunt down. That's the whole pitch. A design system small enough to read in a sitting, color scales that are perceptually even because the math is done in the right space, and modes that are a few operations over a single palette instead of a pile of parallel tables you have to keep honest by hand. If you want the full grammar, the [authoring reference](/docs/design-system/authoring) covers every generator and form, [tokens](/docs/design-system/tokens) walks the built-in defaults, and [modes and brands](/docs/design-system/modes-and-brands) goes deep on the transform vocabulary. --- Source: https://brilliant.design/blog/jank-hunting.mdx Jank is the easiest bug to feel and the hardest to catch. A frame lands a few milliseconds late, the canvas hitches under your cursor, and by the time you think "what was that," it's gone. No stack trace, no error, no reproduction. Just a vibe that the tool stuttered. Brilliant renders its canvas through a native Rust wgpu engine (the story of why lives in [our rust engine post](/blog/rust-engine)), and a design tool lives or dies on that canvas staying smooth through thousands of tiny direct-manipulation gestures per session. So we built two things that refuse to let jank hide: a frame profiler that records a real session and hands back a written verdict, and a measurement culture that would rather admit "I don't know" than lie to itself with a green checkmark. ## Record a session, read the verdict The premise is simple. No DevTools, no Instruments, no on-screen HUD to eyeball. You hit start, you use the app, you hit stop, and the profiler writes a report that says, in plain language, what was slow and why. The reason it can say *why* is context. Every frame in the recording knows not just how long it took but what you were doing when it happened: which gesture was live, how deep you were zoomed, what was selected, how heavy the scene was. That context is what turns a number into a sentence. A 40 ms frame is noise. A 40 ms frame that landed mid-resize, at deep zoom, on a 99-element selection is a lead. The report doesn't stop at "this frame was slow." It decides *where* the time went, groups stutters into episodes instead of scattering them across single frames, and names the most expensive elements by name, so the verdict reads like a diagnosis rather than a spreadsheet: what stuttered, when, what dominated the cost, and which content was responsible. Recording costs effectively nothing when it's off and stays cheap while it's on, so profiling a real working session is a thing you actually do, not a thing you schedule. ## A profiler that knows what it doesn't know Here's where the "refuse to lie to itself" part earns its keep. A profiler is an instrument, and an instrument without error analysis is a random-number generator with confidence. GPU timing in particular is full of traps: measurements arrive late, repeat themselves, and go bimodal under load, and a naive analysis will happily fabricate stalls that never happened or bless a drowning GPU as healthy. The profiler treats every measurement's trustworthiness as part of the measurement. When a number can't be trusted, it is excluded from the verdict rather than allowed to win it, and the report says so. When the machine is merely busy rather than stalling, the report says that too, so a healthy heavy scene never gets mistaken for a bottleneck. The same honesty applies to how the numbers were captured. A debug build inflates everything by roughly an order of magnitude and mints phantom spikes out of thin air; we misread three field sessions before learning that lesson, so now every report carries its capture conditions and tells you when they disqualify a judgement. A verdict is only as trustworthy as the honesty about how it was captured. ## Gates that a hostile machine can't flake A profiler tells you what happened on one machine, once. To keep regressions *out*, we need gates in CI, and CI is a hostile place to measure milliseconds. Shared runners throttle, thermals drift, a cold run and a hot run of the same workload can move by 5x uniformly. An absolute "this must finish in N ms" gate on that hardware is a coin flip that pages you at 3 a.m. So our performance gates never assert absolute wall-clock time. Real captured sessions are replayed through the real engine, and every gated cost is measured as a *ratio* against a reference workload running under the same conditions in the same run. Because the reference throttles right alongside the thing under test, the ratio stays put even as the absolute numbers swing. Costs too small to ever be felt by a human don't get to fail a build no matter how much they wobble, and measurements the machine has rendered untrustworthy are recognized and set aside rather than gated on. The rule of thumb behind all of it: gate the *shape* of the cost, not the raw milliseconds, because only the shape is portable across a contended machine. ## Real bytes through the real engine The other half of not lying to yourself is refusing to test a mock. When we test the engine, we feed it the exact bytes the live application produced, captured from real sessions and replayed through the same interface the running app uses. No synthetic stand-in scene, no re-implemented protocol. If a frame renders wrong in the app, the bytes that made it wrong are the bytes under test. The same discipline covers the profiler itself. Observing a frame must not change the frame: we prove, not assume, that a profiled render and an unprofiled render of the same scene produce identical output. And every comparison in the suite has to first prove it's comparing something real, because two renders of *nothing* match perfectly, and a test that can pass vacuously isn't a test. The assertion is always the strongest one the situation can actually support, and never stronger. ## What it all buys Put together, the payoff is that jank stops being a vibe. On the founder's real captured session, the profiler's verdict reads clean: p95 frame time 2.72 ms, zero jank episodes, with liquid glass correctly named as his single most expensive feature class (ablating it cut the frame's driver-side cost by 49%). That's not a benchmark we invented, it's a written verdict from a recorded session, reproduced against an independent ablation matrix. Record a session, read the verdict, and trust it precisely as far as the honesty rules say you can. A tool that knows what it doesn't know is a tool you can actually chase jank with. --- Source: https://brilliant.design/blog/per-parent-selection.mdx Two questions come up whenever someone opens a Brilliant canvas with a serious amount of stuff on it. The first is "why is this still fast?" The second, usually asked with a raised eyebrow, is "why did selecting things in two different frames draw me two rectangles instead of one?" Both answers come from the same place: Brilliant treats the canvas as a hierarchy of coordinate spaces, not a flat pile of shapes. The first question is about finding elements quickly in world space. The second is about operating on them correctly in parent-local space. This post walks through both. ## The world is big and mostly empty The canvas is enormous. World coordinates in Brilliant span from -10,000,000 to 10,000,000 on each axis, which is a lot of room for a design that mostly clusters in a few thousand pixels. A typical large document is a few hundred elements; our internal large-canvas benchmark stresses the system at 13,000 elements at once, with only a slice of them visible in the viewport at any given zoom. You cannot answer "what is under this cursor" or "what falls inside this drag rectangle" by looping over 13,000 elements on every pointer move. So we don't. A spatial index over the canvas answers region queries without walking the whole document, so most of those thirteen thousand elements never get looked at on a given query. The index alone is never the final answer, though, and the next section is why. An index is only useful if it matches reality, and canvas geometry changes constantly: every drag, resize, and reparent moves world bounds around. So the index is kept coherent with the model at all times. Every query reflects every edit that preceded it, including mid-gesture, and bulk operations (a thousand-element delete, a big multi-element drag) keep the index current without paying a per-element toll. Nobody queries a stale tree, and nobody waits for a fresh one. ## From a click to an element The index gives us candidates, never answers. Hit testing runs in two stages, and the split is the whole game. The **broad phase** is the spatial index: given a world point, it returns a small candidate set by bounding-box overlap. Fast, approximate, never precise. The **narrow phase** takes each of those handful of candidates, transforms the point into that element's parent-local space, and tests it against the element's real geometry. Expensive, but it only ever runs on the few candidates the broad phase surfaced, not on the whole canvas. Input arrives in screen space and gets transformed to world space before any of this begins. The precise tests then happen in parent-local space, because that is where an element's points actually live. So a single click threads through three coordinate systems: screen to world for the query, world to parent-local for the precise test. Ordering matters as much as speed. The hit test returns candidates topmost-first, and sorting only the candidate set is cheap, roughly proportional to the number of candidates rather than the number of elements on the canvas, which is exactly what you want on a hot path that fires on every pointer move. Visibility and lock filtering happen here too, so hidden and locked elements never reach the precise test. Click tolerance divides by the zoom factor, keeping the clickable margin a constant few screen pixels whether you are zoomed way out or way in. There is a whole policy layer above this that decides selection feel, like the rule that a top-level frame with children is only grabbable by its label rather than its body. That is its own post. The point here is the shape: a fast broad phase narrows the field, a precise narrow phase confirms the hit, and the transform into each element's own coordinate space is what makes the precise test correct. ## Why two frames means two rectangles Now the eyebrow question. Select two elements inside Frame A and three inside Frame B, and Brilliant draws two selection rectangles, each with its own resize handles. This is deliberate, and it falls straight out of the coordinate hierarchy. An element's points are stored relative to its parent, not in world space. A frame can be rotated, scaled, nested inside another rotated frame. "Align these to the left" only has a meaning inside a single coordinate space. Left in Frame A, if Frame A is rotated 18 degrees, is a different direction than left in Frame B. Collapsing everything into one world-space rectangle and aligning against that would produce visually wrong, and genuinely surprising, results the moment any ancestor is rotated. So selection is not a flat set. It is grouped by parent: ``` selection └── one group per parent ├── the selected ids inside that parent ├── bounds in the parent's own local space └── world bounds, for drawing on screen ``` Every geometric operation iterates those per-parent groups and works inside each parent's local space independently. Align-left with a selection spanning two parents runs twice, once per group, each pass computing its own bounds and moving its own elements within its own frame. Resize and rotate work the same way. Two parents with selected children, two rectangles, two independent operations. The multiple rectangles are not a quirk of the renderer; they are an honest picture of the fact that you are editing in two coordinate spaces at once. A couple of details make this robust. Each parent's world bounds are computed so that a rotated or nested frame still gets a correct on-screen rectangle rather than an inflated one. And a frame can never be co-selected with its own descendants: ancestors always win, Figma-style. Marquee across a frame and its children and you select only the frame. ## Marquee correctness Drag-selection runs the same broad-phase machinery. The rectangle query returns candidates, each candidate's world rectangle is converted into its parent's local space, and then it is precisely intersection-tested. Top-level frames with children require full containment rather than mere intersection, so dragging a marquee across a big frame selects the elements inside it rather than the frame itself. You only grab the frame by enclosing it whole. That is a lot of interacting rules: broad phase, per-candidate transforms, the containment special case, the ancestors-win frame rule, tolerance that shifts with zoom, plus hidden, locked, and mask-shape stripping. All of it is exactly the sort of thing an optimization pass quietly breaks. So the fast marquee is held to a strict correctness gate: standing tests require it to select exactly what the slower, obviously-correct path selects, across every scene shape and every modifier. A shortcut that lies is not a shortcut we ship. That is the throughline. The spatial index makes the canvas fast, the two-phase hit test makes clicks land where you see them, per-parent grouping makes operations correct inside every coordinate space, and the correctness gate keeps any of it from drifting when we make it faster. The extra rectangle is the visible tip of a lot of care about coordinates. --- Source: https://brilliant.design/blog/renderer-math.mdx Every architectural decision in Brilliant's renderer comes down to one piece of arithmetic. A display frame at 60 Hz is 16.7 milliseconds. A modern laptop viewport is somewhere between 2 and 15 million device pixels. Divide, and you get the entire budget for everything the engine does, end to end: **between one and eight nanoseconds per pixel, per frame.** One nanosecond is about three clock cycles. That number is the renderer's whole worldview, and this post is a tour of the thinking that follows from it: what things cost, why some caches are provably a bad idea, and what happens on hardware where the arithmetic stops working. ## The two currencies GPU work is paid in two currencies, and they fail differently. **Bandwidth** is bytes moved. A backdrop-reading effect like frosted glass reads the pixels behind it, blurs them, and writes them back, which is a lot of traffic per pixel. On a discrete GPU that is almost free. On an integrated GPU with a fifth of the bandwidth, it's the first thing that saturates. **Driver overhead** is the sneaky one: fixed per-operation costs that don't care how many pixels you touch. We learned this the expensive way. A user's canvas with a few hundred glass elements produced frames where the GPU work measured fine but the frame took 170 milliseconds anyway. The fix wasn't faster shading; the per-pixel math was already cheap. The overhead lived on the driver's side of the line, and a structural change to how that work was handed off dropped the same content from 170 ms to half a millisecond. Same pixels, same math per pixel, two orders of magnitude difference, because we had been paying in the wrong currency. ## Sharp at every zoom, without a cache Curves in Brilliant stay sharp from 2% zoom to 5300%, and the engine never pre-renders anything per zoom level to get there. When a user once photographed faint waviness on a wide shallow curve, the fix stayed inside that same approach rather than bolting on a special case, and the waviness went away everywhere at once instead of at one hand-picked zoom. Sharpness that holds across the whole range, not a table of pre-rendered levels. That is also the case against zoom-keyed caches, the ones that render something at one zoom and reuse it at another. Stretched reuse blurs in proportion to how far you zoomed, and re-baking on a zoom change costs a full render right when the gesture needs the frame. Live evaluation has neither failure mode, and when we finally benchmarked our most sophisticated zoom-keyed cache against it on real documents, live won on speed too. That story, with its numbers, is in [the rust engine post](/blog/rust-engine). ## The worst case is not the real case Here is the lesson from this year's work that we find genuinely instructive. Our most demanding effect needs to know, for every pixel near a shape's rim, how far that pixel is from the shape's edge. A complex shape (a boolean of a whole word of text, say) can have tens of thousands of edge segments. Budget naively, cost per pixel against total segments, and the arithmetic collapses: we measured the worst constructible cases at hundreds of milliseconds per frame. But worst-constructible is not what real shapes cost. Once we stopped trusting the scary arithmetic and measured what the effect actually does on real "monster" shapes, the honest cost came in orders of magnitude lower than the naive budget implied. A document that used to route to a lower-quality fallback now evaluates exactly, live, in 0.2 to 0.3 milliseconds per frame across the entire zoom range. The general lesson is duller than the scary number and more useful: measure the real work before you believe a worst case, because a global count is how you scare yourself into building caches you don't need. ## What the measured anchors say Numbers, from our reference workstation (an M4-class Mac driving up to 15 megapixels; treat these as one machine's measurements, not universal constants): - Full glass effect, all-in, at total-viewport rim coverage: about **1.2 ns per device pixel**. Of that, only a small slice is the exact geometry evaluation; the bulk is the bandwidth-bound optics tail (backdrop grab plus blur) that any implementation pays. - 1.2 ns/px × 14.7 Mpx ≈ 17.6 ms: right at the frame budget, and that is the *worst constructible case*, a rim covering every pixel on a 5K display. The same scene at a more typical 7 Mpx viewport is ~8.5 ms, comfortable. - Real documents are nowhere near the worst case: a 4,919-element production document measured 0.10 ms of glass evaluation at the median across a 0.02-to-53 zoom sweep. Now the extrapolation, clearly labeled as such. An integrated GPU has roughly a tenth of the compute and a fifth of the bandwidth of that workstation, so both halves of the cost get several times worse. Sounds fatal, except for the law that quietly rescues every budget in this post: **weaker machines drive smaller viewports.** An integrated-GPU laptop pushes 2 to 4 megapixels, not 15. At the small end, even worst-case saturation lands right at budget; partial glass coverage, the actual common case, fits with a wide margin. And at the top of the iGPU range, full-viewport saturation honestly does not close: the arithmetic says tens of milliseconds no matter how clever the code, and the caches we deleted wouldn't have closed it either, since the bandwidth tail dominates. That case is why [our rendering contract](/blog/rendering-contract) has an honest-degradation value: when the math says no, the answer is a deterministic, visible, reported reduction that settles back to exact one frame after rest, never a silent quality trade. ## Measuring without fooling yourself One more family of math matters here: the statistics of measurement, because per-pixel nanoseconds are exactly the kind of number a machine will lie to you about. Thermals move everything. We measured the same workload 5.5x slower hot versus cold, uniformly. So no performance gate in our suite asserts absolute milliseconds; every gated measurement is a ratio against a reference workload measured under the same conditions, and the ratio stays put while the absolute numbers swing. GPU timing has its own failure modes on top (asynchronous readbacks, saturation, debug-build inflation), and every report is stamped with enough context that nobody judges a number captured under conditions that invalidate it. The full philosophy is in [the jank post](/blog/jank-hunting); the one-line version is that a measurement pipeline needs error analysis exactly like any other instrument. ## The point None of this math is exotic. It's division, a cost model, and some care with statistics. What makes it valuable is that it's written down and every renderer decision has to survive it: features get a cost model before they get an implementation, optimizations get an A/B against the thing they claim to beat, and when a beautiful idea loses to arithmetic, the arithmetic wins. The nanosecond budget isn't a limitation we resent. It's the sharpest design tool we have. --- Source: https://brilliant.design/blog/rendering-contract.mdx Performance work has a famous failure mode: you win the benchmark by quietly trading away something the user can feel. The frame rate chart goes up, and the tool got worse. A little blur during zoom. A selection outline that trails the shape it's selecting. An animation that stutters at a lower tick rate instead of stuttering at a higher one. Each trade looks reasonable in isolation, and together they are how fast software ends up feeling cheap. We decided to write the trades down before they tempted us. Brilliant's engine work is governed by a written rendering contract: seven values that every rendering and performance decision gets judged against, in code review and in tests. The one-sentence version: **every presented frame is a sharp, correct, immediate image of your document, regardless of canvas size, gesture, or hardware.** The rest of this post is the seven values, and the moments each one earned its keep. ## 1. Truth Every pixel on screen derives from the current state of your document and the current camera. Stale content never presents. Not for a frame, not during a gesture, not "just until the cache warms up." This sounds too obvious to write down, and that is exactly why it's written down. Almost every rendering optimization is, at its core, a scheme for reusing old pixels. Retained layers, damage tracking, incremental updates: all of them are bets that yesterday's work is still valid. The value doesn't forbid the bets. It forbids losing them silently. Reuse is held to a strict correctness gate against the true current state, and the moment reuse and truth disagree, reuse loses. ## 2. Sharpness Every pixel is correctly sampled at the current camera. No under-sampling, no stretched reuse of something rendered at another zoom, no upscaled stand-ins while the real thing loads. (Blending many tiny elements into one pixel when you zoom far out is not a violation; that's what correct sampling means at that scale.) Sharpness is the value that keeps catching real bugs, because the tempting way to make zoom fast is to render once and stretch. We shipped a version of that idea for our most expensive effect, and at deep zoom a user photographed the result: a silhouette with visible stair steps, magnified texels pretending to be geometry. The fix wasn't a bigger bake. The fix was evaluating the effect against the live camera, exactly, every frame, and then proving with measurements that exact was also faster. Where a bounded approximation genuinely must exist for pathological content, it's resolution-matched, documented, and scheduled for deletion, not celebrated as cleverness. ## 3. Smoothness Every frame fits the display budget, and per-frame cost is a function of the viewport, never of document size. If some work scales with the number of elements in your file and runs every frame, that is a bug by definition, even when the file is small enough that nobody has noticed yet. This is the value behind the engine architecture we described in [the engine post](/blog/rust-engine): keep per-frame work proportional to the pixels actually on screen rather than to the size of the file. A 13,000-element document and a 200-element document should cost the same to pan, because panning shows you the same number of pixels either way. ## 4. Immediacy Input-to-photon within one display frame of the compositor's floor. The camera you're zooming and the elements you're dragging are sampled at render time, from the freshest input, not relayed through pipeline stages that each add a frame. We measured our own drag path and did not love the answer: input was being relayed through the UI pipeline with about two frames of depth before it reached pixels. So we rebuilt the input path so the freshest input reaches the renderer directly at frame start, and the measured depth dropped to under a tenth of a frame. The chrome came along too: selection rectangles and handles ride the same live transform the content rides, so during a fast drag the outline stays glued to the shape instead of chasing it. Immediacy also covers feedback, and this one is a standing rule with a veto behind it: we never gate, pace, or debounce live UI to buy frame time. If the inspector updates live during a marquee sweep, it keeps updating live; an optimization once tried to suppress that and got reverted with a test pinning the behavior so it can't quietly return. The legitimate fix for expensive feedback is making the feedback path cheap, never making it less alive. ## 5. Uniformity The contract holds identically across every gesture and every device class at its native resolution. Zoom, pan, drag, resize, rotate, text editing: none of them gets a worse deal than idle. There is no "it's fine, that only happens during zoom." Uniformity is why we distrust special-case caches keyed on gesture state. A path that only runs during a particular gesture is a path with its own bugs, its own latency, and its own slightly different pixels, which means the contract now has an asterisk. The same reasoning applies across zoom levels: routing decisions in the engine are pure functions of geometry, not of zoom bands, so nothing pops or swaps representations as you move through scale. ## 6. Honest degradation Users can construct content that exceeds any hardware: stack enough backdrop-reading effects, animate enough unbounded shaders, and the budget loses. The contract doesn't pretend otherwise. It demands that degradation be deterministic, least visible first, settled back to exact within one frame of rest, and reported. Degradation is a measured event, never a silent one. Correctness is never on the degradation ladder. The hardest ruling here was about animated shader fills on a machine that's hitting a wall. The obvious move is lowering the animation's tick rate, and we built exactly that before admitting it fails the test that matters: when a single re-render exceeds the frame budget, a lower tick rate doesn't remove the jank, it re-times it into a stutter metronome. So the policy is binary. An animation is either playing at full rate and full resolution, or frozen, byte-exact, at the last rendered instant. Freezing engages only on real measured evidence of a wall, and resuming is an honest retry that backs off so a struggling machine sees a decaying trickle of attempts instead of a flapping loop. And we never pixelate: no resolution downscale ships on the default path, because a soft frame is a broken promise from value number two. ## 7. Enforceability A rule isn't real until a gate enforces it. The gap between what a team claims and what is true is exactly the set of rules without tests, so every value above maps to specific automated checks, and a new rule lands together with its gate, in the same change. This is the value that makes the other six more than a poster. Each value above has its own automated gate: correctness has strict oracles, sharpness has checks that a frame drawn mid-gesture matches the same frame at rest, smoothness is measured as ratios over real captured documents so a noisy CI machine can't fake a regression or hide one ([the jank post](/blog/jank-hunting) covers why), immediacy has a probe that fails if input latency creeps back up, and degradation is counted so every degraded frame shows up in the stats. When we retire an old behavior, the old path often stays available behind a switch for one release with its own tests pinning it, so the field can confirm the new way before the old one is deleted. ## The math that makes it possible A skeptical reader should ask: isn't this contract just expensive idealism? The reason it's affordable comes down mostly to one idea: the relationship between world space and screen space is simple, and worth trusting rather than caching around. Caches keyed on zoom fight that simplicity: by construction they either stretch stale pixels (trading sharpness) or re-bake mid-gesture (trading smoothness). When we finally measured our most sophisticated zoom-keyed cache against live evaluation on real user documents, live was faster and exact. The full story, with numbers, is in [the engine post](/blog/rust-engine). The other half is the per-frame floor: work proportional to the pixels on screen and to how much you changed, never to the size of the file. Budgets then hold on weak hardware for an unglamorous reason: smaller machines drive smaller viewports. ## What this buys you You never think about any of this, which is the point. You zoom to 5000% and edges stay edges. You drag fast and the selection stays welded to the shape. You open a huge file and panning costs what panning costs. And when your content genuinely exceeds your hardware, the tool degrades in a way that is predictable, visible in its own diagnostics, and gone one frame after you let go. Values are cheap to publish and expensive to keep. This set is enforced by tests we run on every change, and it has already cost us real work we were proud of: caches we measured and deleted, a tick-rate ladder we built and retired, latency we re-architected input handling to remove. That's the standard we intend to keep. If you ever catch a Brilliant frame breaking one of these, we'd genuinely like to hear about it. --- Source: https://brilliant.design/blog/rust-engine.mdx Brilliant is a Flutter app, and for a long time the canvas was too. Every rectangle, every glyph, every gradient you saw was drawn by a Flutter `CustomPainter` walking the element tree on the UI thread. That painter served us well until it didn't, and the ceiling it hit was not a bug we could fix. It was the architecture. So we replaced it. Canvas content in Brilliant now renders through a native Rust engine built on wgpu. This post is about what that swap bought, and about the one optimization we were sure we needed and then measured away. ## Why the painter had to go A design tool is a rendering loop wearing a trench coat. Every drag, every nudge, every zoom tick is a full re-evaluation of a live graph, and the round trip from "the model changed" to "the pixels changed" has to fit inside a display frame or the whole thing feels like mud. The Flutter painter did this on the same thread that runs your gestures, your layout, and your UI. On small documents that is fine. On a 13,000-element canvas at a deep zoom, it is a slideshow. We could have chased that with caching and tiling forever. Instead we moved the pixels to where the pixels belong: a GPU renderer written in Rust. Dart still owns everything that makes Brilliant *Brilliant*, the whole model and its meaning; the engine is a deliberately dumb, extremely fast renderer of primitives. Dart decides *what*; the engine decides *how fast*. The important part, the part we want to be honest about: there is no Flutter fallback anymore. The legacy widget and tile pipeline is deleted, not disabled. There is no per-canvas opt-out, no "classic renderer" toggle hiding in settings. If the native library fails to load, or the engine faults at runtime, the canvas does not quietly fall back to a slower path that renders something slightly different. It shows an explicit failure screen: Retry if the library never loaded, Restart Rendering if the engine died at runtime. That is the whole contract. We found it clarifying to have exactly one renderer. A fallback is a second rendering path you have to keep pixel-honest forever, and the cheapest way to keep two renderers in agreement is to have one. One renderer also means exports stop being a separate story. A PNG, a PDF, a copy-as-image: they come out of the same engine that painted your screen, so what you see is exactly what you export. There is no second rasterizer to drift a hairline or fatten a glyph on the way out. ## Only what changed The obvious way to feed a renderer is to hand it the whole scene every frame. The obvious way is also the slow way. Re-describing a 13K-element document sixty times a second is exactly the O(document) per-frame work we were trying to escape. So the engine doesn't work that way. The cost of an edit is designed to track the size of the edit, not the size of the document, and the payoff shows up at the extremes: dragging a selection across a 13,000-element document costs roughly what it would on a ten-element one, and typing into a text element never triggers a document walk. The common interactions were built to cost almost nothing to describe, so the frame budget gets spent on pixels instead of bookkeeping. ## Content and chrome never disagree Your toolbars, panels, and menus are still Flutter widgets. The canvas underneath them is Rust. The requirement that makes that split feel like one application is subtle: content and chrome must never disagree, even for a single frame. If the engine drew on its own schedule while Flutter painted the selection rectangle on Flutter's schedule, then during a fast drag your selection outline would visibly lag the thing it is selecting. So the two are locked together and present as one. Grab a shape and throw it across the screen: the outline stays glued to it at full speed, under the same camera, with no gap you could photograph. The selection logic itself never left Dart. Only the drawing moved. ## Retiring the painter without losing its judgement Deleting a renderer is easy. Deleting a renderer without shipping a thousand tiny visual regressions is not. The painter had years of accumulated correctness baked into it, most of it undocumented, all of it load-bearing. How do you prove the new engine matches it when you are about to delete the thing you would compare against? Carefully, and behind strict correctness gates. The migration didn't land until the engine cleared a hard bar for matching the old output across everything the painter knew how to draw: shapes, radii, gradients, strokes, blends, flips, arcs. The painter did not leave until the engine could stand in front of its life's work and match it. From here on the rules change, and we like the new rules better. There is no painter left to produce a "correct" reference, so the engine is now its own source of truth for how a pixel looks, held to strict correctness gates of its own: the same scene must render identically every time, everywhere it renders, screen and export alike. ## The zoom cache we measured away We want to tell one optimization story straight, because it went the opposite of how we expected. Liquid glass in Brilliant refracts its backdrop, and the optics depend on the shape's silhouette evaluated against the camera. The received wisdom, and our own instinct, was that you cannot afford to evaluate that live every frame during a zoom gesture. You bake it into a zoom-keyed cache and reuse it. We built exactly that, in two versions. Then we sat down to validate it properly, and the cache lost. We keep a written rendering contract, a north star every engine decision gets held against, and one of its notes we had been quietly violating. A zoom-keyed cache, by construction, has to either stretch a stale bake (blurry) or re-bake mid-gesture (janky). It trades away a rule we care about no matter which way it fails, and glass evaluated live against the camera doesn't have to make that trade at all. So we spiked it: live evaluation versus the cache, A/B, on real content. On a founder's actual 4,919-element capture at his real zoom range (0.02 through 53), the numbers came back like this: ```text leg gpu_content p50 p95 max LIVE 0.10 ms 0.63 1.45 CACHED 0.58 ms 1.90 2.19 ``` Live evaluation was not a tolerable regression we would accept for sharper pixels. It was *faster*, roughly 5x faster at the median. The per-tick re-bake the cache performed during a zoom gesture cost more than the evaluation it was supposedly saving. And the artifacts the founder had reported, simple vector geometry crawling past a zoom threshold, pixelation at extreme zoom, were the cache's re-bake stutter and its stretched reuse. The optimization was the bug. We deleted both cache versions. Glass now evaluates live, exact at every zoom. For genuinely pathological content, the kind no cache would have rescued either, the behavior is the one our rendering contract demands: a deterministic, visible reduction that settles back to exact the moment the pressure lifts, never a silent quality trade. The verdict is written down, with the measurement, because "we measured, the cache lost" is a better artifact than a clever cache nobody ever questioned. ## What we got The canvas is a Rust engine now. Dart still holds the model and the meaning; the engine holds the milliseconds. The engine's cost tracks what you changed rather than how big your document is, the chrome never lags the content it decorates, exports are pixel-identical to the canvas, and the renderer we deleted set the correctness bar its replacement had to clear. We got one renderer instead of two, which is one contract to keep honest instead of a permanent negotiation between them. And we got a habit we intend to keep: when an optimization feels obviously necessary, measure it against the thing it is supposed to beat, and be willing to find out it doesn't. If the language Brilliant's agents use to build all of this is interesting to you, the [Blueprint post](/blog/blueprint) is the other half of the story.