Naruto Hand Sign Battle is a two-player browser game where players speak a jutsu, perform webcam-detected hand signs, and resolve attacks in real time. The project combines LLM-driven jutsu generation, computer vision sign validation, live game state sync, and WebRTC video into a playable anime-style battle loop.
The core idea is simple:
- Two players join the same match in the browser.
- A player speaks a jutsu name or asks for chakra recovery.
- Gemini turns that voice input into a structured action.
- If it is a jutsu, the game generates a hand-sign sequence and spell animation.
- The player performs the signs on camera.
- Roboflow validates the signs.
- The backend resolves damage, chakra cost, and the next state of the match.
This gives the game three layers at once:
- Fantasy layer: Naruto-inspired jutsu, chakra, animations, announcer voice, themed UI.
- Interaction layer: webcam, microphone, live voice, hand-sign performance.
- Systems layer: turn logic, validation, animation timing, multiplayer sync.
- Voice-first gameplay: players do not just click abilities, they say them out loud.
- Physical input: attacks depend on actually performing hand signs in front of the camera.
- AI as game master: Gemini acts like a dramatic announcer and generates structured game actions.
- Live PvP loop: state updates, video, and casting all happen during the same browser session.
- React 18
- TypeScript
- Vite
- Zustand for client state
- Framer Motion for UI and battle motion
- Sonner for toasts
@google/genaifor Gemini Live in the browser@fishjam-dev/ts-clientfor WebRTC media signaling@mediapipe/tasks-visionfor headband / face overlay features
- FastAPI
- Uvicorn
- Pydantic
- httpx
- python-dotenv
- google-genai
- inference-sdk for Roboflow
- fishjam-server-sdk
- Docker Compose for local multi-service orchestration
- Fishjam for WebRTC media
- Caddy for production reverse proxy / TLS
flowchart LR
A[React Frontend] -->|REST| B[FastAPI Backend]
A -->|WebSocket game state| B
A -->|WebRTC signaling| C[Fishjam]
B -->|Management API| C
B -->|LLM requests| D[Gemini]
B -->|Vision inference| E[Roboflow]
- The backend creates a game ID and stores match state in memory.
- Players join from separate browser tabs or devices.
- Game state is pushed through WebSocket and polling-assisted presence logic.
- The match automatically transitions from
waitingtoactiveonce both players are present.
- The frontend opens a Gemini Live session using an ephemeral token from the backend.
- Gemini receives microphone audio and returns tool calls.
- It can call:
declare_jutsuchakra_recovery
- For jutsu casting, Gemini returns:
- jutsu name
- hand-sign sequence
- creativity score
- description
- spell animation type
- During casting, the frontend captures frames from the local camera at intervals.
- The backend sends frames to Roboflow.
- Roboflow predicts the sign class.
- The backend compares the predicted sign to the expected next sign.
- Once all signs are validated, the turn can resolve successfully.
- Successful casts deal damage based on:
- sign count
- creativity score
- completion speed
- Failed casts still cost chakra.
- Chakra recovery restores
+20chakra. - The backend broadcasts the updated state to both players.
- Fishjam handles player video/audio transport through WebRTC.
- The game UI renders local and opponent feeds side by side.
- Additional overlays such as headbands and spell animations are rendered on top of the gameplay scene.
The repo is designed to run as:
- Frontend: Vite dev server
- Backend: FastAPI / Uvicorn
- Media server: Fishjam
Recommended local setup:
docker compose up --buildThis starts:
backendonhttp://localhost:8000fishjamonhttp://localhost:5002
Then run the frontend separately:
cd frontend
npm install
npm run devOpen:
http://localhost:5173for frontend devhttp://localhost:8000for backend
Vite proxies /api and /ws to the backend in development.
The production stack in this repo is:
- Caddy in front
- FastAPI backend
- Fishjam media server
docker-compose.prod.yml runs:
caddybackendfishjam
Caddy proxies:
/socket/*to Fishjam/ws/*to the backend- everything else to the backend
This keeps the public surface relatively simple while still supporting:
- browser app/API traffic
- websocket state updates
- WebRTC signaling/media bootstrap
backend/ FastAPI app, game logic, Gemini, Roboflow integration
frontend/ React app, UI, hooks, overlays, casting flow
frontend/public/ Static themed assets
docker-compose.yml Local backend + Fishjam
docker-compose.prod.yml Production stack with Caddy
Caddyfile Reverse proxy configuration
requirements.txt Python dependencies
Create a root .env file from the example:
cp .env.example .envImportant variables:
| Variable | Required | Purpose |
|---|---|---|
ROBOFLOW_API_KEY |
Yes | Hand-sign detection |
ROBOFLOW_API_URL |
No | Roboflow endpoint |
ROBOFLOW_MODEL_ID |
No | Model version |
GEMINI_API_KEY |
Yes | Gemini Live + fallback text/audio flows |
GEMINI_MODEL |
No | REST model |
GEMINI_LIVE_MODEL |
No | Live model |
FISHJAM_URL |
No | Fishjam backend URL |
FISHJAM_BEARER_TOKEN |
No | Fishjam management token |
For WebRTC in Docker, you will usually also need:
export FJ_WEBRTC_TURN_IP=<your-lan-or-public-ip>Then start compose:
docker compose up --buildFor production builds with a split frontend/API host, copy:
cp frontend/env.production.example frontend/.env.productionThen set:
VITE_API_BASE_URLVITE_WS_BASE_URLVITE_FISHJAM_HOSTVITE_FISHJAM_PORTif needed
Do not commit frontend/.env.production.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn backend.main:app --reload --host 127.0.0.1 --port 8000cd frontend
npm install
npm run dev- A game starts in
waitingstate. - Once both players are connected, it becomes
active. - Each player has:
- HP
- chakra
- optional pending jutsu state
- Completed jutsu trigger a spell animation before turn resolution.
- The backend persists runtime game state in
games_state.json.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/game/create |
Create a game |
POST |
/api/game/join |
Join a game and get Fishjam peer/token info |
GET |
/api/game/{game_id}/state |
Get public state |
POST |
/api/start-turn |
Start jutsu casting |
POST |
/api/validate-sign |
Validate the current sign from a camera frame |
POST |
/api/resolve-turn |
Resolve successful/failed casting |
POST |
/api/chakra-recovery |
Restore 20 chakra |
POST |
/api/gemini-token |
Create Gemini ephemeral token for browser Live API |
POST |
/api/transcribe-audio |
Fallback server-side transcription |
POST |
/api/infer-frame |
Debug inference endpoint |
WS |
/ws/game/{game_id} |
Real-time game state updates |
This project explores what happens when you combine:
- anime-inspired game design
- LLM tool calling
- real-time browser voice
- computer vision
- multiplayer state sync
- WebRTC media
It is part game prototype, part AI interaction experiment, and part real-time systems playground.
games_state.jsonis local runtime state and should not be committed.- Gemini is used through server-issued ephemeral tokens, so the frontend does not need a permanent Gemini API key in normal operation.
- Fishjam WebRTC setup depends on a reachable IP or hostname; loopback often does not work for real device-to-device media.
- Chrome is the safest default browser for microphone, camera, and WebRTC behavior during development.