A modern full-stack web application for selecting and managing AI-generated game entity images. Built with Next.js 16, React 19, Express, TypeScript, and Firebase.
Live Demo: https://image-cook-frontend.vercel.app
Image Cook helps game developers streamline the process of selecting AI-generated images for their game assets. Upload multiple AI-generated image variants for each entity (enemies, items, avatars), visually compare them, and batch-confirm your favorites (in user testing sessions). The app automatically compresses and optimizes selected images for game use.
- Visual Image Selection - Browse AI-generated image variants in an intuitive card-based interface
- Batch Operations - Select multiple entities and confirm all at once for efficient workflow
- Automatic Optimization - Selected images are automatically compressed from 1024x1024 PNG to 396x396 JPEG (90% quality) - can be extended to have different configs per category (e.g. avatar, enemy, item)
- State Persistence - Track selections with Firebase Firestore
- Real-time Updates - See selection changes instantly across the interface
- Production Ready - Deployed with enterprise-grade security and performance
- Enhanced Configuration - Choose between multiple AI models (FLUX, SDXL, etc.) and adjust image counts per entity (via project config)
The live demo showcases 21 game enemies with 2 AI-generated image variants each (powered by fal.ai, using FLUX-2 model). You can:
- Browse and compare 42 pre-generated enemy images
- Select your favorite version for each enemy
- Batch-confirm multiple selections
- Change your mind and reselect different variants
- Bulk Image Generation - Integrate with fal.ai API to generate images directly from the app
- Regeneration Support - Regenerate images for specific entities without manual re-upload
- Multiple Categories - Extend beyond enemies to support items, avatars, and custom categories
- Authentication Layer - This is an admin tool, and should be behind a login. Temporarily open to public for demo purposes
- Better Compression - Integration with Tinify API for better image compression and resizing
- Next.js 16 (App Router) - React framework
- TypeScript
- Tailwind CSS - Modern utility-first styling
- Playwright - End-to-end testing across browsers
- Node.js 24
- Express - Node.js framework
- TypeScript
- Firebase Admin SDK - Firestore database + Cloud Storage
- Sharp - High-performance image processing
- Jest + Supertest - Unit and integration testing
- Helmet - Security headers (CSP, XSS protection, etc.)
- CORS - Origin whitelist and credential handling
- express-validator - Input validation
- Request size limits - DoS prevention
- Turborepo - Monorepo build system with intelligent caching
- Firebase Firestore - NoSQL database for entity metadata
- Firebase Storage - Object storage for image files
- Vercel - Frontend hosting with global CDN
- Render - Backend hosting with auto-scaling
Image Cook uses a monorepo architecture with three main packages:
image-cook/
├── apps/
│ ├── backend/ # Express REST API
│ └── frontend/ # Next.js web application
├── packages/
│ └── shared/ # Shared TypeScript types and utilities
└── data/
└── enemies-init.json # Entity initialization data
- Monorepo with Turborepo - Share types between frontend/backend while maintaining independent deployment
- Firebase Admin SDK - All database/storage operations through secure backend API (no client-side Firebase)
- Server-side image processing - Consistent, high-quality compression with Sharp (vs browser-based solutions)
- Batch confirmation UX - Reduces API calls and provides better workflow for bulk operations
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /entities |
Get all entities (with optional category filter) |
| POST | /entities/init |
Initialize entities from Storage |
| POST | /images/approve |
Approve and compress an image selection |
| POST | /images/deselect |
Deselect current winner image |
User selects image → Local state (pending)
↓
User clicks "Confirm" → POST /images/approve
↓
Backend downloads images from Firebase Storage → Compress with Sharp → Upload JPEG to Storage
↓
Update Firestore → Response to frontend
↓
Frontend refreshes → Display updated state
- Node.js 24+ and npm 10+
- Firebase project (free tier works)
- Git
git clone <repository-url>
cd image-cooknpm installThis installs dependencies for all workspaces (backend, frontend, shared).
- Go to https://console.firebase.google.com
- Create new project named "image-cook"
- Enable Firestore Database (production mode)
- Enable Firebase Storage
- Go to Project Settings → Service Accounts
- Click "Generate new private key"
- Save the JSON file as
apps/backend/firebase-service-account.json
Important: This file contains sensitive credentials and is .gitignored. Never commit it to Git.
If you have pre-generated images:
- Go to Firebase Console → Storage
- Create folder
generated/enemy/ - Upload your images following the naming pattern:
{entity-name}-{model}-v{version}.png
Create apps/backend/.env:
GOOGLE_APPLICATION_CREDENTIALS=./firebase-service-account.json
STORAGE_BUCKET=your-project.firebasestorage.app
PORT=8531
FRONTEND_URL=http://localhost:8530Replace STORAGE_BUCKET with your actual Firebase Storage bucket name (found in Firebase Console).
Create apps/frontend/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:8531
API_BASE_URL=http://localhost:8531The shared package must be built before the apps can import it:
npm run build --workspace @image-cook/sharednpm run devThis starts:
- Backend: http://localhost:8531
- Frontend: http://localhost:8530
Or start individually:
# Backend only
cd apps/backend
npm run dev
# Frontend only
cd apps/frontend
npm run devWith backend running, initialize entities:
curl -X POST http://localhost:8531/entities/init \
-H "Content-Type: application/json" \
-d @data/enemies-init.jsonThis scans Firebase Storage and creates database entries for all entities.
Navigate to http://localhost:8530
You should see the Image Cook interface with a grid of entity cards!
# Start all apps
npm run dev
# Build all packages and apps
npm run build
# Run all tests
npm run test
# Format code
npm run format
# Lint code
npm run lintcd apps/backend
# Start server - This is what Render uses
npm run startcd apps/frontend
# Run E2E tests
npm run test:e2e
# Run E2E tests in UI mode
npm run test:e2e -- --uinpm run test - unit & integration tests
apps/backend/src/api-tests.http.example - API tests with REST Client extension in VS Code or IntelliJ
End-to-End Tests - Full user workflow with Playwright
cd apps/frontend
npm run test:e2eTests cover:
- Page loading and rendering
- Image selection interaction
- Batch confirmation workflow
- State updates and UI feedback
- Multi-browser compatibility (Chromium, Firefox, WebKit)
Both Vercel and Render are configured to be deployed automatically when there is a push.
image-cook/
├── apps/
│ ├── backend/ # Express REST API
│ │ ├── src/
│ │ │ ├── config/ # Firebase initialization
│ │ │ ├── routes/ # API endpoints with validation
│ │ │ ├── services/ # Business logic (Firestore, Storage, Selection)
│ │ │ └── utils/ # Image processing
│ │ └── package.json
│ │
│ └── frontend/ # Next.js web app
│ ├── app/ # Pages and layouts
│ ├── components/ # React components
│ ├── hooks/ # Custom hooks
│ ├── lib/ # API client
│ ├── e2e/ # Playwright tests
│ └── package.json
│
├── packages/
│ └── shared/ # Shared TypeScript types and utilities
│ ├── src/
│ │ ├── types/ # Entity type definitions
│ │ └── utils/ # Name normalization
│ └── package.json
│
├── data/
│ └── enemies-init.json # Initial entity data
│
├── turbo.json # Turborepo configuration
└── package.json # Root workspace
All rights reserved.