OtakuVersus is a manga-styled anime guessing game with both singleplayer and real-time multiplayer modes.
Players identify anime titles from scene images, earn points, and compete in rankings.
- Core Features
- Tech Stack
- Project Structure
- Product Highlights
- Technical Decisions
- How to Add Anime (Contributor Workflow)
- Database Configuration
- Migrate Supabase Postgres to Neon
- Architecture Notes
- Roadmap
- ✅ JWT authentication (register, login, current user)
- ✅ Guest play support (no account required)
- ✅ Singleplayer sessions (score-based)
- ✅ Multiplayer matchmaking with shared rounds
- ✅ Pre-match countdown and synchronized round timer
- ✅ Result comparison vs opponent
- ✅ ELO ranking for multiplayer accounts
- ✅ Separate leaderboards:
- ✅ Singleplayer score leaderboard
- ✅ Multiplayer ELO leaderboard
- ✅ User match history with mode filtering
- ✅ Anime titles and scenes seeded into PostgreSQL via Prisma
- ⚛️ React 19
- 🔷 TypeScript
- ⚡ Vite
- 🎨 Tailwind CSS
- 🧭 React Router
- 📦 TanStack Query
- 🟢 Node.js
- 🚏 Express
- 🔷 TypeScript
- 🔺 Prisma ORM
- 🐘 PostgreSQL
- 🔐 JWT auth
- 🗂️ Storage abstraction layer (
noop/ Cloudinary / Supabase Storage) - ▲ Frontend deploy-ready for Vercel
- 🚄 Backend deploy-ready for Railway/Render
OtakuVersus/
client/
src/
api/
app/
components/
game/
ui/
features/
auth/
game/
history/
leaderboard/
layouts/
pages/
routes/
styles/
types/
utils/
public/
.env.example
package.json
server/
prisma/
migrations/
schema.prisma
seed.ts
src/
app/
config/
lib/
middleware/
modules/
auth/
users/
game/
leaderboard/
anime-scenes/
storage/
types/
utils/
.env.example
package.json
package.json
README.md
- 🎮 End-to-end gameplay loop with persisted sessions and post-match breakdown.
- 🏆 Two ranking systems: score-based singleplayer and ELO-based multiplayer.
- 👤 Guest flow and authenticated flow coexisting in one codebase.
- 🧠 Server-authoritative multiplayer scoring and ELO calculation.
- 🎨 Manga-styled UI system kept consistent across pages and game states.
- 🧩 Domain-oriented backend modules (
auth,game,leaderboard,users,anime-scenes) for maintainability. - 🗃 Prisma + PostgreSQL with migrations and seed data to keep schema/content reproducible.
- ⚡ TanStack Query for predictable async state, caching, and refetch patterns.
- 🧠 In-memory caching for external anime metadata to reduce API calls and improve response time.
- 🧱 Reusable UI primitives (
Button,Card,Modal,Loading) to avoid style drift.
This section describes how to add new anime content as part of an official release contribution.
Put exactly 3 images per anime in:
client/public/images/scenes
Use naming:
<Anime Title>_1.png<Anime Title>_2.png<Anime Title>_3.png
Supported extensions: .png, .jpg, .jpeg, .webp.
Edit:
server/prisma/seed.ts
Changes:
- Add title to
animeCatalogoradditionalAnimeTitles. - Add scene entry to
scenes:anime: '<Anime Title>'difficulty: DifficultyLevel.EASY | MEDIUM | HARD
Important:
animeinscenesmust exactly match image filename title.- Keep enough unique anime for session generation.
npm run prisma:seed --workspace serverIf schema changed:
npm run prisma:migrate --workspace server
npm run prisma:seed --workspace server- Start app and play multiple sessions.
- Confirm title appears in round pool and answer suggestions.
- Confirm slider loads all 3 images for the added title.
- Confirm no seed/runtime errors in backend logs.
Commit:
- new files in
client/public/images/scenes server/prisma/seed.tsupdates
In PR description include:
- list of added anime
- selected difficulty per anime
- quick gameplay proof (screenshots/video)
- Domain-based Express modules keep backend features isolated and maintainable.
- Prisma schema is the single source of truth for data shape and relations.
- TanStack Query handles async state and cache on the frontend.
- Auth context keeps JWT flow simple and explicit.
- Multiplayer and ELO are implemented server-side to keep scoring authoritative.
The server uses Prisma with standard PostgreSQL connections:
DATABASE_URLis the pooled runtime connection used by the API.DIRECT_URLis the direct connection used by Prisma Migrate and administrative commands.
For Neon, copy the pooled connection string into DATABASE_URL and the direct connection string into DIRECT_URL. Both connections must use SSL. Keep JWT_SECRET unchanged when changing database providers so existing access tokens remain valid.
The application does not use Supabase Auth. Registered accounts, bcrypt password hashes, guest accounts, and game history live in the Prisma-managed public schema and are migrated with the rest of the database.
- Create an empty Neon project in the same or a nearby region and, when possible, use the same PostgreSQL major version as Supabase.
- Install
pg_dumpandpg_restoreand make them available inPATH. - Use direct, non-pooled connection strings for both the source and target migration connections.
- Schedule a maintenance window and stop the API before the final dump so no writes occur during migration.
From PowerShell at the repository root, set the two temporary migration variables. Do not commit either value:
$env:SOURCE_DATABASE_URL = '<supabase-direct-connection-string>'
$env:TARGET_DATABASE_URL = '<neon-direct-connection-string>'Run the migration script. The explicit switch is required because restore removes existing objects from the target public schema:
& ".\server\scripts\migrate-supabase-to-neon.ps1" `
-BackupPath "$env:TEMP\otakuversus-supabase.dump" `
-ConfirmTargetResetThe script uses a custom-format dump and restores it with --no-owner and --no-acl, preventing Supabase-specific ownership and privileges from being copied to Neon.
Keep the API stopped and compare all application record counts:
npm run db:verify-migration --workspace serverThe verification command reads SOURCE_DATABASE_URL and TARGET_DATABASE_URL. It compares users, registered and guest account totals, sessions, rounds, guesses, anime titles, and scenes.
Update the deployed server environment only after verification succeeds:
DATABASE_URL=<neon-pooled-connection-string>
DIRECT_URL=<neon-direct-connection-string>
Do not change JWT_SECRET and do not run prisma:seed, because the seed deletes existing application data. Check migration state and then build the application:
npm exec --workspace server -- prisma migrate status
npm run buildBefore ending maintenance mode, verify login, registration, /api/auth/me, guest play, completed games, ELO, history, and leaderboards. Keep Supabase unchanged as a short-lived rollback database. If rollback is required after writes have been enabled on Neon, those new writes must be reconciled manually.
Supabase Storage is an optional, currently inactive provider and is not migrated by this database procedure. Neon does not provide object storage.
- WebSocket/SSE multiplayer events instead of polling
- Admin scene management panel with upload/moderation workflow
- Background image optimization pipeline (format conversion + resizing)
- Seasonal multiplayer ladder with soft reset and reward tiers
- User profile avatars with upload + preset avatar pack
- In-game achievements and account progression badges
- Activate difficulty-based score multipliers for
EASY,MEDIUM, andHARD
This project is proprietary and licensed as All Rights Reserved.
Commercial use is not permitted without explicit written permission.
