Transform any recipe into a guided, step-by-step cooking experience with AI-generated images and voice narration.
- Recipe Import: Paste any recipe URL with multi-fallback scraping (direct fetch β Firecrawl β Perplexity)
- Recipe Search: Search recipes from TheMealDB (free) or Spoonacular (API key required)
- AI Step Images: Beautiful AI-generated images for each cooking step using Runware's official SDK (FLUX.2 models)
- Voice Narration: On-demand text-to-speech using ElevenLabs with audio preloading
- Cooking Mode: Immersive full-screen experience with full-bleed images and overlaid instruction cards
- Progress Tracking: Pick up where you left off, track completed recipes
- Recipe Library: Save, organize, and manage your recipe collection
- Celebration: Confetti party when you complete a dish! π
- Frontend: Next.js 16+ (App Router), React 19, TypeScript, TailwindCSS 4
- Auth & Database: Supabase (Auth + Postgres with Row Level Security)
- Image Generation: Runware SDK (
@runware/sdk-js) with FLUX.2 models - Text-to-Speech: ElevenLabs API
- LLM: OpenAI GPT-4o (for recipe normalization and parsing)
- Recipe Scraping: Firecrawl + Perplexity AI as fallbacks for protected sites
- Storage: Pluggable backend (Local filesystem / Supabase Storage / S3-compatible)
cook-dis/
βββ src/
β βββ app/ # Next.js App Router pages
β β βββ api/ # API routes
β β β βββ jobs/ # Background job worker + trigger
β β β βββ media/ # Local file serving
β β β βββ recipes/ # Recipe CRUD + import
β β β βββ steps/ # Step media retry
β β β βββ tts/ # On-demand text-to-speech
β β β βββ search/ # Recipe search API
β β βββ auth/ # Auth callback
β β βββ cook/[id]/ # Cooking mode (full-screen guided experience)
β β βββ import/ # Recipe import page
β β βββ library/ # Recipe library
β β βββ login/ # Authentication
β β βββ profile/ # User profile & settings
β β βββ search/ # Recipe search
β βββ components/ # React components
β β βββ layout/ # Navbar, etc.
β β βββ ui/ # Button, Card, Input, Toaster, etc.
β βββ lib/
β β βββ services/ # External API integrations
β β β βββ elevenlabs.ts # TTS service
β β β βββ openai.ts # Recipe normalization
β β β βββ recipe-parser.ts # Multi-fallback URL scraping
β β β βββ recipe-search.ts # TheMealDB + Spoonacular
β β β βββ runware.ts # Image generation (official SDK)
β β βββ storage/ # Media storage abstraction
β β β βββ base.ts # Abstract base class
β β β βββ local.ts # Local filesystem
β β β βββ s3.ts # S3-compatible (AWS/R2/MinIO)
β β β βββ supabase.ts # Supabase Storage
β β β βββ index.ts # Factory function
β β βββ supabase/ # Supabase client utilities
β βββ types/ # TypeScript types
βββ supabase/
β βββ migrations/ # Database migrations
βββ data/
βββ media/ # Local storage directory (gitignored)
- Node.js 18+
- npm
- Supabase account (free tier works)
- API keys for:
- OpenAI (required - recipe normalization)
- Runware (required - image generation)
- ElevenLabs (required - voice narration)
- Firecrawl (optional - enhanced recipe scraping)
- Perplexity (optional - AI-powered scraping fallback)
- Spoonacular (optional - recipe search)
git clone https://github.com/keanucz/cook-dis.git
cd cook-dis
npm install- Create a new Supabase project at supabase.com
- Go to SQL Editor and run the migration file:
supabase/migrations/001_initial_schema.sql
- Enable Email Auth in Authentication β Providers
- (Optional) Enable Google OAuth for social login
- If using Supabase Storage, create two private buckets:
recipe-step-imagesrecipe-step-audio
Create .env.local with your values:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Media Storage (choose: local, supabase, or s3)
MEDIA_STORAGE_BACKEND=local
LOCAL_STORAGE_PATH=./data/media
# OpenAI (required)
OPENAI_API_KEY=sk-...
# Runware (required)
RUNWARE_API_KEY=...
# ElevenLabs (required)
ELEVENLABS_API_KEY=...
# Firecrawl (optional - for protected recipe sites)
FIRECRAWL_API_KEY=...
# Perplexity (optional - AI scraping fallback)
PERPLEXITY_API_KEY=...
# Spoonacular (optional - recipe search)
SPOONACULAR_API_KEY=...
# Worker Auth
CRON_SECRET=your-random-secret
# App URL
NEXT_PUBLIC_APP_URL=http://localhost:3000npm run devCook-Dis uses Runware's official SDK (@runware/sdk-js) for AI image generation:
- WebSocket connection with auto-reconnect for reliability
- Consistent visual style - all images use the same cookware (stainless steel pan, marble countertop) for continuity
- Professional food photography prompts optimized for appetizing results
- Base64 output for direct storage without additional downloads
| Model | ID | Description |
|---|---|---|
| FLUX.2 Dev | flux2dev |
Fast generation, great quality (default) |
| FLUX.2 Max | flux_2_max |
Highest quality, slower |
Model preference is set per-user in Profile settings and persisted with each recipe.
Text-to-speech is generated on-demand when the user taps play:
- First tap generates audio via ElevenLabs API
- Audio is cached in browser memory
- After all images complete, audio is preloaded for all steps in the background
- Subsequent plays are instant
This approach minimizes API costs while providing a smooth experience.
MEDIA_STORAGE_BACKEND=local
LOCAL_STORAGE_PATH=./data/media- Files stored in
./data/media/directory - Served via authenticated API route (
/api/media/[...path]) - Best for development and self-hosted deployments
MEDIA_STORAGE_BACKEND=supabase- Create private buckets:
recipe-step-images,recipe-step-audio - Files served via signed URLs (1 hour expiry)
MEDIA_STORAGE_BACKEND=s3
S3_ENDPOINT=https://your-account.r2.cloudflarestorage.com
S3_REGION=auto
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...
S3_BUCKET=cook-dis-media
S3_PUBLIC_BASE_URL=https://media.yourdomain.com # Optional CDNMedia generation runs asynchronously via a Postgres-based job queue:
- Recipe Import β Creates
generate_step_imagejobs for each step - Cook Page β Triggers worker automatically while waiting
- Worker processes jobs with retry logic (up to 3 attempts)
- Failed media can be manually retried from the cooking interface
The cook page automatically triggers the worker every 15 seconds while images are generating.
{
"crons": [{
"path": "/api/jobs/worker",
"schedule": "* * * * *"
}]
}* * * * * curl -X POST https://your-domain.com/api/jobs/worker -H "Authorization: Bearer $CRON_SECRET"- All API keys are server-side only (never exposed to client)
- Row Level Security (RLS) on all Supabase tables
- Users can only access their own recipes and media
- Signed URLs for private media access
- CRON_SECRET protects the worker endpoint
- Direct Fetch - Standard HTTP request with cheerio parsing
- Firecrawl - Handles Cloudflare, JavaScript rendering
- Perplexity AI - Uses AI to extract recipe from any page
Supports most recipe websites via:
- Schema.org JSON-LD (preferred)
- Microdata markup
- OpenAI-powered content extraction
- TheMealDB: Free, no API key required
- Spoonacular: Requires API key, more comprehensive database
- Push to GitHub
- Import project in Vercel
- Add environment variables
- Deploy!
Vercel automatically handles the cron job for background processing.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]docker build -t cook-dis .
docker run -p 3000:3000 --env-file .env.local cook-dis| Service | URL | Notes |
|---|---|---|
| OpenAI | platform.openai.com | Required - GPT-4o for recipe parsing |
| Runware | runware.ai | Required - FLUX.2 image generation |
| ElevenLabs | elevenlabs.io | Required - Free tier available |
| Firecrawl | firecrawl.dev | Optional - Enhanced scraping |
| Perplexity | perplexity.ai | Optional - AI fallback |
| Spoonacular | spoonacular.com/food-api | Optional - Recipe search |
Contributions are welcome! Please open an issue or PR.
MIT License - see LICENSE
Built with β€οΈ for home cooks everywhere. Happy cooking! π³