Brainstorming canvas that expands your ideas with AI.
Type a seed. Branch it. Steer each branch with a lens — scrappy indie, VC pitch, pessimist, whatever. Merge two ideas into one. Compose the whole board into a summary.
henro.space · No accounts · Your OpenRouter key · Everything stays in your browser.
AI chats end up as linear walls of text. You scroll forever, half of it is fluff, and the idea you wanted is gone.
Ideas aren't linear. Chats are. That's the whole problem.
Henro puts brainstorming on a canvas where branches fan out, ideas merge, and context stays visible.
- Seed — type an idea
- Branch — AI expands a node into N sub-ideas (configurable, 1–10)
- Steer — every expansion takes an optional lens so branches go where you want
- Merge — drag two nodes together; AI writes the combined idea
- Compose — synthesize everything active into a summary
- Context-aware prompts — AI sees what's directly connected to each node, not just a flat bag of text
- Vite + React 19 + TypeScript
- Zustand for state (
persistmiddleware, per-project localStorage keys) - Tailwind 4
- Framer Motion
- OpenRouter for inference (BYOK)
No backend. No accounts. No database. Everything is localStorage.
pnpm install
pnpm devOpen the app, paste your OpenRouter key into Settings, and start branching. Get a key at openrouter.ai/keys.
Copy .env.example to .env.local if you want any of these.
| Flag | Effect |
|---|---|
VITE_OPENROUTER_API_KEY=sk-or-… |
Sets the BYOK key for local dev — skips the welcome screen and is used as a fallback when nothing is saved in localStorage. Never set this in a hosted build: Vite inlines VITE_* vars into the public JS bundle, exposing the key to anyone who opens DevTools. |
VITE_HENRO_DEBUG=true |
Logs every AI prompt + response to the browser console (collapsed groups) |
In production, the app is BYOK-only — every visitor pastes their own key, which lives in their localStorage and never leaves the browser.
Static build, any host. Includes config for Cloudflare Pages:
pnpm builddist/is the deploy outputpublic/_headers→ security headers (HSTS, nosniff, frame-ancestors, referrer-policy)public/robots.txt+public/sitemap.xml→ search-engine basics
src/
├── App.tsx – tri-state gate: WelcomeScreen → SeedInput → Canvas
├── main.tsx – ErrorBoundary wrapper
├── store.ts – single Zustand store, persist middleware, project slice
├── components/
│ ├── Canvas.tsx – pan/zoom, marquee select
│ ├── BubbleNode.tsx – draggable node, merge animation, expand
│ ├── Connections.tsx – SVG edges
│ ├── SeedInput.tsx – initial seed prompt (pre-canvas)
│ ├── NodeInput.tsx – inline rename / per-node prompt
│ ├── SidePanel.tsx – node detail (on selection)
│ ├── ComposeButton.tsx– summary modal
│ ├── Settings.tsx – API key + model + branch count + system prompt (presets + editor)
│ ├── ProjectSwitcher.tsx
│ ├── HenroMenu.tsx – top-left brand menu
│ ├── HelpButton.tsx – bottom-left shortcuts overlay
│ ├── SelectionCount.tsx – selection count chip
│ ├── WelcomeScreen.tsx
│ ├── Toaster.tsx / ErrorBoundary.tsx
│ └── icons.tsx
├── lib/
│ ├── ai.ts – OpenRouter chat + retry + debug logging
│ ├── prompts.ts – system-prompt presets
│ ├── errors.ts – AiError class + classifier
│ ├── toast.ts – tiny toast store
│ ├── persistence.ts – custom Zustand StateStorage adapter (split per-project keys)
│ ├── config.ts – BYOK config read/write + useHasApiKey
│ ├── tokens.ts – design tokens (z-index, etc.)
│ ├── motion.ts – framer-motion duration / easing presets
│ ├── uid.ts – RFC4122 v4 UUID helper (works in non-secure contexts)
│ ├── layout.ts – child-node position math
│ ├── physics.ts – overlap resolution
│ └── usePhysics.ts – settle-after-drag hook
└── index.css – Tailwind theme + prose-compose + scrollbar-soft
Persistence schema:
henro:projects:index— project metadata list + current IDhenro:project:<id>— one key per project (nodes, connections, viewport, seed, composeResult)openrouter-config— BYOK config (apiKey, model, branchCount, systemPrompt)
PRs welcome. See CONTRIBUTING.md.
Small fixes and UI polish: open a PR.
Bigger changes (new features, architectural shifts): open an issue first so we can align before you spend time.
MIT — see LICENSE.
Built by @vldsvrk. Follow along for updates.