A collaborative web game where users can create game objects using OpenAI's Sora2 video model and place them on an infinite grid canvas.
English | 简体中文
- 🎮 Infinite Grid Canvas: Drag and navigate an unlimited game world
- 🎨 AI-Powered Object Creation: Generate game objects using text prompts or reference images
- 🖼️ Animated Objects: All objects are converted to GIFs with transparent backgrounds
- 🔄 Real-time Collaboration: All users share the same world via WebSocket
- 📦 Object Library: Browse, search, and reuse created objects
- ✏️ Flexible Placement: Resize objects from 1x1 to 10x10 grid cells
- 🎯 Zelda-Inspired UI: Retro game aesthetic
- React 18 + TypeScript
- Konva.js for canvas rendering
- Zustand for state management
- Socket.IO for real-time updates
- TailwindCSS for styling
- Node.js + Express + TypeScript
- MongoDB for data persistence
- Socket.IO for real-time communication
- Bull queue for async video processing
- FFmpeg for video-to-GIF conversion with chroma key
- OpenAI Sora2 API for video generation
- Node.js 18+
- MongoDB
- Redis
- FFmpeg
- OpenAI API key with Sora2 access
git clone <your-repo-url>
cd sora-universeEdit config.yaml in the root directory:
openai:
apiKey: "your-openai-api-key-here"
baseURL: "https://api.openai.com/v1"
server:
port: 3001
mongoUri: "mongodb://localhost:27017/sora-universe"
redis:
host: "localhost"
port: 6379
sora:
model: "sora-2"
size: "720x720"
seconds: 4
promptTemplate: "Generate a game object for a video game. The object should be: {prompt}. IMPORTANT: Place the object on a solid green screen background (#00FF00). The object should be centered and loop seamlessly. Keep the camera static."cd server
npm installcd client
npm install# MongoDB
mongod
# Redis
redis-servernpm run devnpm run devThe client will be available at http://localhost:5173
The server API will be at http://localhost:3001
- Click on an empty grid cell
- The object library drawer opens on the right
- Click the + button to create a new object
- Enter a prompt (e.g., "A magical glowing sword")
- Optionally upload a reference image
- Click Create
- A placeholder appears on the grid while Sora2 generates the video
- Once complete, the animated GIF replaces the placeholder
- Click an empty grid cell
- In the opened drawer, click any existing object
- The object is immediately placed at that position
- Select: Click on a placed object to select it
- Copy: Select an object, then click "Copy" in the bottom control panel
- Delete: Select an object, then click "Delete"
- Search: Use the search bar in the drawer to find objects by name
- Pan: Click and drag the canvas to move around
- Zoom: (Future feature - mouse wheel zoom)
- User submits prompt → Creates GameObject in DB (status:
pending) - Job added to Bull queue
- Worker calls Sora2 API with green screen prompt template
- Polls Sora2 API until video generation completes
- Downloads MP4 video
- FFmpeg processes video:
- Applies chroma key filter to remove green background
- Converts to GIF at 15 fps
- Scales to 192x192px (default 3x3 grid size)
- Sets infinite loop
- Saves GIF to
/public/gifs/ - Updates GameObject (status:
completed, gifUrl:/gifs/{id}.gif) - Broadcasts update via WebSocket to all clients
- Grid cell size: 64x64 pixels
- Object default size: 3x3 cells (192x192px)
- Object size range: 1x1 to 10x10 cells
- Canvas: Infinite (virtualized grid rendering)
- Model:
sora-2(fast) orsora-2-pro(high quality) - Resolution: 720x720 (1:1 aspect ratio)
- Duration: 4 seconds
- Background: Green screen (#00FF00) for chroma keying
sora-universe/
├── client/ # React frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── GameCanvas.tsx
│ │ │ ├── ObjectDrawer.tsx
│ │ │ ├── CreateObjectModal.tsx
│ │ │ └── ObjectControls.tsx
│ │ ├── store/ # Zustand state
│ │ ├── types/ # TypeScript types
│ │ └── App.tsx
│ └── package.json
│
├── server/ # Node.js backend
│ ├── src/
│ │ ├── routes/ # API routes
│ │ ├── services/ # Sora2, FFmpeg services
│ │ ├── models/ # MongoDB models
│ │ ├── workers/ # Bull queue workers
│ │ ├── socket/ # WebSocket handlers
│ │ └── index.ts
│ └── package.json
│
└── config.yaml # Configuration file
POST /api/objects- Create new game objectGET /api/objects- Get all objects (paginated, searchable)GET /api/objects/:id- Get single object
POST /api/placed-objects- Place object on gridGET /api/placed-objects- Get all placed objectsPATCH /api/placed-objects/:id- Update placed object (move/resize)DELETE /api/placed-objects/:id- Delete placed objectPOST /api/placed-objects/:id/copy- Copy placed object
object:updated- GameObject status changedobject:progress- Generation progress update
Install FFmpeg:
# macOS
brew install ffmpeg
# Ubuntu
sudo apt install ffmpegEnsure MongoDB is running:
mongod --dbpath /path/to/dataEnsure Redis is running:
redis-server- Check your OpenAI API key has Sora2 access
- Verify the API key in
config.yaml - Check API rate limits
- Object resize handles (drag corners to resize)
- Mouse wheel zoom
- Layer management (bring to front/back)
- User accounts and private worlds
- Object animations and interactions
- Multiplayer cursors
- Export world as image/video
MIT
Built with OpenAI's Sora2 API