A modern web application for AI-powered image generation, editing, and composition. Supports two AI providers — Nano Banana 2 (Google Gemini) and GPT Image 2 (via Fal.AI) — switchable via a global toggle in the header.
Switch between AI providers at any time using the header toggle:
- Nano Banana 2 — Google's
gemini-3.1-flash-image, fast and high-quality - GPT Image 2 — OpenAI's latest image model served through Fal.AI, with jpeg/png/webp output format control
Create images from text prompts:
- Supports 10 aspect ratios and 3 output resolutions (1K / 2K / 4K)
- Prompts passed directly to the API for strict adherence to user input
- Output format selection (jpeg/png/webp) available with GPT Image 2
Modify existing images with AI-powered edits. Upload via drag-and-drop, URL, or send from another tab:
- Apply natural edits while maintaining photorealism
- Works with both uploaded files and previously generated images (URL or base64)
- Output resolution, aspect ratio, and format (GPT Image 2) configurable
Combine multiple images into new, cohesive compositions:
- Select up to 5 images from the pool
- Nano Banana: passes images as multimodal input to Gemini
- GPT Image 2: uploads images to Fal.AI storage and calls the edit endpoint with multiple image URLs
- Control aspect ratio, resolution, and output format
- Dual AI Provider Support: Switch between Nano Banana 2 and GPT Image 2 globally
- Flexible Resolution: 1K, 2K, 4K output for both providers
- Output Format Control: jpeg / png / webp (GPT Image 2 only; Gemini auto-selects format)
- Image Reuse: Send generated images between tabs — handles both base64 (Nano Banana) and hosted URLs (Fal.AI)
- Strict Prompt Adherence: User prompts passed directly to the API without modification
- Modern UI: React 18, shadcn/ui components, Tailwind CSS, dark mode support
- Drag-and-Drop Upload: Intuitive file upload with visual feedback
- Docker Ready: Fully containerized, runs behind Nginx Proxy Manager
- React 18 with hooks
- Vite — build tool and dev server
- shadcn/ui — accessible React components
- Tailwind CSS — utility-first styling
- Lucide React — icon library
- React Dropzone — drag-and-drop uploads
- FastAPI — Python web framework
- Google GenAI SDK (
google-genai) — Nano Banana / Gemini API - HTTPx — async HTTP client for Fal.AI REST API calls
- Python 3
| Provider | Model ID | Used For |
|---|---|---|
| Nano Banana 2 | gemini-3.1-flash-image |
Default Gemini model |
| Nano Banana Pro | gemini-3-pro-image |
Higher quality Gemini |
| GPT Image 2 | openai/gpt-image-2 (via Fal.AI) |
OpenAI model via Fal.AI |
- Docker with multi-stage builds
- Docker Compose + external
shared_netnetwork - Uvicorn / Gunicorn — ASGI server
- Docker & Docker Compose
- Google Gemini API Key — Google AI Studio
- Fal.AI API Key — fal.ai (required only for GPT Image 2)
git clone <repository-url>
cd GemFlashcp .env.example .env
# Edit .env and fill in your API keys| Variable | Required | Description |
|---|---|---|
GOOGLE_API_KEY |
Yes | Google Gemini API key for Nano Banana |
GEMINI_API_KEY |
Alternative | Alternative name for Google API key |
GEMINI_MODEL |
No | Gemini model ID (default: gemini-3.1-flash-image) |
FAL_KEY |
For GPT Image 2 | Fal.AI API key |
APP_PASSWORD |
Yes | Password required on the login screen |
SECRET_KEY |
Yes | Long random string used to sign JWT tokens — must be kept secret |
NODE_ENV |
No | Node environment (default: development) |
PORT |
No | Backend port (default: 8000) |
NETWORK_NAME |
No | Docker network name (default: shared_net) |
Example .env (see .env.example for a copy-paste template):
# Google Gemini API (Nano Banana)
GOOGLE_API_KEY="your_google_api_key_here"
GEMINI_MODEL="gemini-3.1-flash-image"
# Fal.AI (GPT Image 2)
FAL_KEY="your_fal_api_key_here"
# Authentication
APP_PASSWORD="choose-a-strong-password"
SECRET_KEY="replace-with-a-long-random-secret-string"
# Application
NODE_ENV="development"
PORT=8000
NETWORK_NAME="shared_net"docker compose up --build -d- Web Interface: http://localhost:8000 (or your configured domain via Nginx Proxy Manager)
- API Docs: http://localhost:8000/api/docs
GemFlash/
├── frontend/
│ ├── src/
│ │ ├── components/ # Reusable React components
│ │ ├── App.jsx # Main application (provider state, all tab logic)
│ │ ├── index.css
│ │ └── main.jsx
│ ├── public/
│ ├── dist/ # Built assets (generated inside Docker)
│ ├── package.json
│ └── vite.config.js
│
├── backend/
│ ├── main.py # FastAPI app — Nano Banana + Fal.AI endpoints
│ └── requirements.txt
│
├── .env # Your local environment variables (not in git)
├── .env.example # Template
├── Dockerfile
├── docker-compose.yml
└── README.md
| Ratio | Format | Use Case |
|---|---|---|
1:1 |
Square | Profile pictures, social media tiles |
2:3 |
Portrait | Photography, print/poster |
3:2 |
Landscape | Photography |
3:4 |
Portrait Classic | Social media, print |
4:3 |
Classic | Traditional photography |
4:5 |
Instagram Portrait | Social media vertical |
5:4 |
Landscape | Horizontal wide shots |
9:16 |
Vertical | Mobile video (Reels/Shorts) |
16:9 |
Widescreen | Video, web |
21:9 |
Ultrawide | Cinematic |
| Label | Nano Banana (1:1 example) | GPT Image 2 (1:1 example) |
|---|---|---|
| 1K | 1024×1024 | 1024×1024 |
| 2K | 2048×2048 | 2048×2048 |
| 4K | 4096×4096 | 2880×2880 (8.29M px ceiling) |
GPT Image 2 via Fal.AI has a maximum of 8,294,400 total pixels and a 3840 px max edge, so 4K square is capped at 2880×2880. Widescreen (16:9) at 4K = 3840×2160.
- PNG — Lossless (default)
- JPEG — Compressed, smaller file size
- WebP — Modern format, good compression
Nano Banana's output format is determined automatically by the Gemini API.
All endpoints (except /api/auth/login) require an Authorization: Bearer <token> header.
POST /api/auth/login
Content-Type: application/json
{ "password": "your_app_password" }
→ { "access_token": "...", "token_type": "bearer" }POST /api/generate_image
Content-Type: application/json
Authorization: Bearer <token>
{ "prompt": "...", "aspect_ratio": "16:9", "output_resolution": "2K", "output_format": "png" }
POST /api/edit_image (multipart/form-data)
POST /api/compose_images (multipart/form-data)POST /api/fal/generate_image
Content-Type: application/json
Authorization: Bearer <token>
{ "prompt": "...", "aspect_ratio": "16:9", "output_resolution": "2K", "output_format": "png" }
POST /api/fal/edit_image (multipart/form-data — accepts image_url or image_file)
POST /api/fal/compose_images (multipart/form-data — accepts image_urls JSON array + image_files)Fal.AI responses return image_url (hosted CDN URL). Nano Banana responses return image (base64) + mime_type.
- Timeouts: 120s read / 60s connect for both providers
- Image uploads to Fal.AI: Base64 images and file uploads are automatically uploaded to Fal.AI storage before being passed to the edit/compose API
- URL images: Images generated by GPT Image 2 are passed directly by URL to subsequent edit/compose calls — no re-upload needed
| Provider | Resolution | Approximate Time |
|---|---|---|
| Nano Banana 2 | 1K | 10–20 s |
| Nano Banana 2 | 2K | 20–40 s |
| Nano Banana 2 | 4K | 30–60 s |
| GPT Image 2 | 1K–4K | 15–45 s |
- Login required: All API endpoints are protected by JWT authentication. A password must be entered on the login screen before any image operations are available.
- JWT tokens: Issued on successful login, stored in
localStorage, and sent asAuthorization: Bearerheaders on every API request. Tokens expire after 24 hours. - Password & secret:
APP_PASSWORDandSECRET_KEYare read from.envat startup — never hardcoded. Use a strong, unique value for each. - API keys stored in
.env: Excluded via.gitignore— never committed to the repository. - Keys are server-side only: Google and Fal.AI API keys are never sent to the browser.
- All external API calls made from the backend container.
See CONDUCTOR.md for full security guidelines.
docker compose logs
docker compose ps- Verify
.envis in the project root with correct values - Restart after editing
.env:docker compose restart
- Confirm
FAL_KEYis set in.env - Check Fal.AI account has credits
- Check logs:
docker compose logs -f
- Try a lower resolution first
- Check provider API status
- Timeout values are set in
backend/main.py(httpx.Timeout(120.0, connect=60.0))
docker compose logs -f # Follow all logs
docker compose logs --since 10m # Last 10 minutesThe app is designed for the external shared_net Docker network. The docker-compose.yml uses that network with no exposed ports — Nginx Proxy Manager handles ingress.
docker compose up --build -d # Build and start
docker compose down # Stop
docker compose up --build -d # Rebuild after code changes- JWT Authentication: Password-protected login screen gates all API access.
APP_PASSWORDandSECRET_KEYconfigured via.env. Tokens expire after 24 hours; "Sign out" button in the header clears the session.
- Fal.AI / GPT Image 2 integration: Added global provider toggle; new
/api/fal/*endpoints; image size computation for Fal.AI constraints; automatic Fal.AI storage upload for edit/compose - Output format: jpeg/png/webp selector added for GPT Image 2 provider
- Resolution: 1K/2K/4K exposed in UI for both providers
- Mime type fix: Nano Banana responses now return actual
mime_typefrom Gemini instead of hardcodingimage/png - Cross-tab transfer: URL-based images (Fal.AI) handled correctly without CORS-prone blob conversion
- Gemini Model Refresh: Updated to Nano Banana 2
gemini-3.1-flash-image - Strict Prompt Adherence: Disabled prompt enhancement
- Timeout Configuration: Extended read timeout to 120 s
- Path Alias Fix: Resolved jsconfig.json/vite.config.js
@alias inconsistency