Ilham is the Arabic word for inspiration. It is a local-first, infinite mood board for arranging references and finding new inspiration in the collection you have built.
Drop images onto the board, arrange them freely, extract palettes, add labels, and optionally use semantic captions and embeddings to search or organize visual ideas. Your board stays in the browser; AI is an opt-in enhancement rather than a dependency of the core experience.
Prerequisites: a recent Node.js release and pnpm.
pnpm install
pnpm devOpen the Vite address shown in the terminal. Ilham is desktop-first; the compact viewport shows a desktop-only message instead of a reduced board.
pnpm test
pnpm lint
pnpm build- Drop image files onto the canvas or paste an image from the clipboard.
- Use the toolbar to select, marquee-select, zoom, pan, extract colors, add a label, search, organize, undo, redo, or change layer order.
- Drag nodes to move them. The selection transformer handles resize and rotation.
- Open the right-side panel to inspect selected nodes and monitor processing activity.
- Use Export in the header to preview and download the board or current selection.
The footer shows the board position, zoom level, organizer progress, and local storage status.
| Shortcut | Action |
|---|---|
V |
Select |
M |
Area select |
H |
Pan |
Z |
Zoom |
T |
Add label |
⌘ Z |
Undo |
⇧ ⌘ Z |
Redo |
Delete |
Delete selection |
⌘ ] |
Bring forward |
⌘ [ |
Send backward |
Ilham has no application backend. IndexedDB is the local source of truth for:
- Image blobs.
- Assets and generated metadata.
- Nodes, transforms, and stacking order.
The UI updates immediately. A small write queue persists board changes in order and combines rapid updates to the same node before they reach IndexedDB. The footer reports whether the queue is saved, saving, or failed; a failed write can be retried.
Browser storage is scoped to the current browser profile and origin. Clearing site data removes the local board. Live synchronization between tabs and remote backup are not implemented.
AI features require your own Gemini API key:
- Open Settings.
- Paste the key under Gemini.
- Select Use for this tab.
The key remains only in module memory for the current tab. It is not saved to IndexedDB, localStorage, Zustand, or the board. Closing the tab or choosing Clear removes it. The selected model IDs and request limits are ordinary non-secret preferences and may be saved locally in production.
With a key, newly added images are processed in the background:
image file
-> image embedding
-> concise visual caption
-> caption embedding
-> ready for semantic search and organization
Caption and embedding requests are independently serialized and rate-limited. Configure the limits to match your Gemini tier. If processing fails, the image remains on the board and the activity panel offers feedback and removal.
For development-only defaults, copy .env.example to .env.local and fill the values you want:
cp .env.example .env.localVITE_GOOGLE_GENAI_API_KEY=
VITE_GOOGLE_CAPTION_MODEL=gemini-3.6-flash
VITE_GOOGLE_CAPTION_REQUESTS_PER_MINUTE=5
VITE_GOOGLE_CAPTION_REQUESTS_PER_DAY=20
VITE_GOOGLE_EMBEDDING_MODEL=gemini-embedding-2
VITE_GOOGLE_EMBEDDING_REQUESTS_PER_MINUTE=5
VITE_GOOGLE_EMBEDDING_REQUESTS_PER_DAY=100
VITE_BOARD_HISTORY_LIMIT=16All VITE_* values are included in a Vite development build. Do not use this mechanism for a shared production key. Production ignores these defaults and asks each user to provide their own key for the active tab.
React components -> interface and browser gestures
board commands -> state, history, persistence, and async coordination
Zustand -> reactive board and interface state
IndexedDB -> local assets, image blobs, and nodes
React Konva -> infinite board rendering and direct manipulation
Gemini -> optional captioning and embeddings
Web Worker -> bounded organization calculation
The core models are validated with Zod:
Assetis reusable content: an image, palette, or text label.Nodeplaces an asset on the board and owns stacking order and transform data.Transformstores centre-basedx,y, width, height, and rotation.Boarddescribes ordered node IDs and durable groups.
The project keeps rendering, board behavior, storage, semantic work, and schemas as separate boundaries:
src/
├── board/ board commands, history, layout, persistence, and state helpers
├── components/ board, layout, search, toolbar, and shadcn/Base UI components
├── schemas/ Zod asset, board, and node models
├── semantic/ Gemini integration, rate limiting, cosine scoring, and search
├── storage/ IndexedDB access and the local write queue
├── workers/ off-main-thread layout calculation
└── store.ts small reactive Zustand store
For a detailed account of semantic search and the layout algorithm, see Layout and Search.
- TypeScript is strict and React Compiler is enabled.
- Tailwind CSS v4 provides styling; shadcn components use Base UI primitives.
- Tests are deliberately offline and do not require an API key, IndexedDB, or network access.
pnpm buildruns the TypeScript build before Vite’s production build.
- This is a desktop-first web application, not a packaged desktop app.
- The board is local to one browser profile and does not sync between devices or tabs.
- The Gemini key is a user-supplied browser key for a local experience. A hosted multi-user deployment should use a server-side proxy or another appropriate credential boundary.
- Organization is applied immediately as one undoable operation; a preview-and-apply flow is not yet available.
- Use a local or remote vector database for higher-quality similarity search as boards grow.
- Support additional AI providers and local models alongside Gemini.
- Add a chat surface with safe, user-approved board tool calls.
- Add image generation for creating new visual references directly on the board.
Codex was used as an iterative engineering collaborator throughout the project. It helped turn ideas into small implementation steps, review and simplify the React/Konva architecture, add offline tests, audit performance and persistence behavior, and write project documentation. Product decisions, interaction design, and final direction remained human-led.
Created by reuteelf.
I enjoy finding unexpected usecases for familiar tools and turning that curiosity into something tangible. This MVP is largely vibe-coded, made possible by OpenAI Build Weeks free Codex credits. I may not win and will probably die broke ... but I hope it inspires someone to build an even better product.
