Vibrant Wave is an AI-powered canvas editor where what you see on the canvas is the main conditioning signal for generation.
Every time you hit Generate, the app takes the visible composition inside the Generation Area, optionally mixes in your extra reference images, adds your text prompt, sends it to the image LLM — and returns the result as a new layer on top.
Mental model: WYSIWYP — What You See Is Your Prompt. You “author” the prompt with layers, masks and framing — not only with text.
Model stack (current): Requests go through OpenRouter and are executed by nano-banana (our image-LLM relay). The model is swappable later; today we default to
google/gemini-2.5-flash-image-previewvia OpenRouter, executed through nano-banana.
- Outpaint empty borders — expand the frame, hit Generate, the white areas fill in contextually.
- Local fix (Inpaint) — paint a mask over a region and regenerate just that area.
- Style lift — drop 1–2 style refs, adjust prompt, get a tasteful new layer.
- Quick variants — set
variantCount: 3–4, keep the best, hide the rest.
Standard AI image generation from text prompts
Filling a grid with variations based on reference examples
Combining elements on canvas - dressing a person in a suit and creating a photo
- Go to OpenRouter.ai
- Sign up/login and go to API Keys
- Create a new API key (starts with
sk-or-...) - Copy the key for the next step
Create .env.local in project root:
# Required: paste your OpenRouter API key here
OPENROUTER_API_KEY=sk-or-your-key-here
# Optional: override default model (default: google/gemini-2.5-flash-image-preview)
OPENROUTER_IMAGE_MODEL=google/gemini-2.5-flash-image-previewChoose your platform:
Windows:
run_win.batmacOS:
./run_mac.shLinux:
./run_linux.shManual (any platform):
bun run devApp runs on http://localhost:3000 by default.
bun run build
bun run start{
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint"
}Run with Bun:
bun run dev
bun run build
bun run startEndpoint: POST /api/generate
Body (JSON):
{
"prompt": "cinematic portrait, warm lighting",
"canvas": "data:image/png;base64,....",
"attachments": ["data:image/png;base64,...."],
"model": "google/gemini-2.5-flash-image-preview",
"variantCount": 3
}Behavior:
- Server resizes input images to ~1MP using
sharp(keeps aspect) for efficient LLM input. - Calls OpenRouter chat completions with image+text modalities.
- Returns up to
variantCountimage URLs and combined textual output.
Response (shape):
{
"variants": [
{ "image": "https://...", "text": "...optional text..." }
],
"text": "Combined text across variants",
"image": "https://..." // first image for convenience
}- UI Components: shadcn/ui primitives in
src/components/ui - Canvas Tools: Konva-based components in
src/components/canvasandsrc/components/Canvas* - State Management: Zustand stores in
src/libfor settings, history, and canvas state - Command Pattern: undo/redo system with commands in
src/lib/commands - Hotkeys: global keyboard shortcuts in
src/lib/useGlobalHotkeys - Theme: CSS custom properties with dark/light mode support
Any Next.js-compatible host works (Vercel recommended).
- Set environment variables (
OPENROUTER_API_KEY, optionalOPENROUTER_IMAGE_MODEL). - Build with
bun run buildor via the host's build step. - Ensure serverless runtime can fetch
https://openrouter.ai/api/v1/chat/completions.
The app supports optional authentication via NextAuth with two modes:
When OIDC variables are configured, the app uses OIDC authentication and automatically redirects to the identity provider. Credentials authentication is disabled in this mode.
Required variables:
OIDC_ISSUER_URL- Your OIDC provider's issuer URL (e.g.,https://idp.lofters.ru)OIDC_CLIENT_ID- OIDC client IDOIDC_CLIENT_SECRET- OIDC client secret (plain text, not the hashed value from Authelia config)OIDC_LOGOUT_URI- OIDC logout endpoint (e.g.,https://idp.lofters.ru/logout)
Optional variables:
OIDC_TOKEN_ENDPOINT_AUTH_METHOD- Token endpoint authentication method. Default:client_secret_basic(recommended for Authelia). Alternative:client_secret_post. Must match thetoken_endpoint_auth_methodsetting in your Authelia client configuration.
When OIDC is not configured, the app falls back to simple username/password authentication:
- Set
AUTH_ENABLED=trueto enable authentication - Configure
AUTH_USERandAUTH_PASSWORDfor login credentials
NEXTAUTH_URL- Your application URL (e.g.,https://your-domain.comorhttp://localhost:3000)NEXTAUTH_SECRET- Generate a secure secret (e.g.,openssl rand -base64 32)
Example production .env with OIDC:
OPENROUTER_API_KEY=sk-or-your-key-here
NEXTAUTH_URL=https://your-domain.com
NEXTAUTH_SECRET=your-generated-secret
OIDC_ISSUER_URL=https://idp.your-domain.ru
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-plain-text-client-secret
OIDC_LOGOUT_URI=https://idp.your-domain.ru/logout
# OIDC_TOKEN_ENDPOINT_AUTH_METHOD=client_secret_basic # optional, default is client_secret_basicImportant: The OIDC_CLIENT_SECRET must be the plain text secret (the original value before hashing), not the Argon2id hash shown in Authelia's configuration file. Authelia will verify the plain text secret against the stored hash.
Example production .env with credentials:
OPENROUTER_API_KEY=sk-or-your-key-here
AUTH_ENABLED=true
AUTH_USER=admin
AUTH_PASSWORD=your-secure-password
NEXTAUTH_URL=https://your-domain.com
NEXTAUTH_SECRET=your-generated-secretWhen using Docker Compose, all authentication variables are automatically passed from your .env file to the container. See docker-compose.yml for the complete list of supported environment variables.
- Missing API key:
OPENROUTER_API_KEYnot set → API returns 500 with explicit error - Sharp issues: on Windows, update Node/Bun and reinstall deps (
rm -rf node_modules && bun install) - Empty variants: check model quota and ensure input images are valid data URLs
- Canvas not loading: check browser console for Konva/React-Konva errors
- Hotkeys not working: ensure focus is on canvas, not input fields
- OIDC authentication failed:
- Verify
OIDC_CLIENT_SECRETis the plain text secret (not the Argon2id hash) - Ensure
OIDC_TOKEN_ENDPOINT_AUTH_METHODmatches your Authelia client configuration (client_secret_basicorclient_secret_post) - Check that
OIDC_CLIENT_IDmatches exactly (case-sensitive) - Verify the callback URL in Authelia:
https://your-domain.com/api/auth/callback/oidc
- Verify
MIT