multiplayer-prompting
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data β all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v10View latest version
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Val Town β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Frontend (React) β β
β β βββββββββββ ββββββββββββ ββββββββββββββ β β
β β β CardDeckβ βPromptStackβ βVibeOutput β β β
β β ββββββ¬βββββ βββββββ¬βββββ ββββββββ¬ββββββ β β
β β β β β β β
β β βββββββββββββββ΄ββββββββββββββββ β β
β β β β β
β β ββββββββΌβββββββ β β
β β β App.tsx β β β
β β ββββββββ¬βββββββ β β
β βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ β
β β β
β SSE β HTTP β
β β β
β βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ β
β β Backend (Hono) β β
β β β β
β β βββββββββββ ββββββββββββ βββββββββββββββββ β β
β β βSSE Hub β β API Routesβ β Vibe Engine β β β
β β βbroadcastβ β /api/* β β (OpenAI) β β β
β β ββββββ¬βββββ βββββββ¬ββββββ βββββββββ¬ββββββββ β β
β β β β β β β
β β βββββββββββββββ΄ββββββββββββββββββ β β
β β β β β
β β ββββββββΌβββββββββ β β
β β β Database β β β
β β β (SQLite) β β β
β β ββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Browser β POST /api/session/:id/join
β
Create/Find Session in SQLite
β
Add Player with Turn Order
β
Broadcast "player-joined" via SSE
β
Return Session + Player Data
Browser β POST /api/session/:id/play
β
Insert Card into prompt_stack Table
β
Update current_turn in Session
β
Broadcast "card-played" via SSE
β
All Clients Update UI
Browser β POST /api/session/:id/generate
β
Fetch All Cards from prompt_stack
β
Vibe Engine Synthesizes Prompt
β
Call OpenAI API
β
Save Result to vibe_results Table
β
Broadcast "vibe-update" via SSE
β
All Clients Show Response
ββββββββββββ ββββββββββββ ββββββββββββ
β Client 1 β β Client 2 β β Client 3 β
ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ
β β β
β EventSource β EventSource β EventSource
ββββββββββββββββββΌβββββββββββββββββ€
β β β
ββββββββββββββββββ΄βββββββββββββββββ
β
βββββββββΌβββββββββ
β SSE Hub β
β (Maintains β
β connections) β
βββββββββ¬βββββββββ
β
broadcast(message)
β
ββββββββββββββββββ΄βββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
β card-playedβ β turn-changedβ
β vibe-update β βplayer-joinedβ
β player-left β β etc. β
βββββββββββββββ βββββββββββββββ
sessions_v1
βββ id (PK)
βββ created_at
βββ current_turn
βββ max_players
βββ status
players_v1
βββ id (PK)
βββ session_id (FK)
βββ name
βββ avatar
βββ is_active
βββ turn_order
βββ last_seen
prompt_stack_v1
βββ id (PK)
βββ session_id (FK)
βββ player_name
βββ card_type
βββ content
βββ image_url
βββ timestamp
vibe_results_v1
βββ id (PK)
βββ session_id (FK)
βββ prompt
βββ response
βββ token_count
βββ timestamp
Session: current_turn = 0
Players: [Alice, Bob, Charlie] (turn_order: 0, 1, 2)
Turn 0: Alice plays β current_turn = 1
Turn 1: Bob plays β current_turn = 2
Turn 2: Charlie plays β current_turn = 0 (wraps around)
Turn 0: Alice again β ...
Stack: [
{type: "role", content: "Act as Rust expert"},
{type: "tone", content: "ELI5"},
{type: "token-limit", content: "50 tokens"}
]
Vibe Engine:
1. Extract role β "Act as Rust expert"
2. Extract tone β "ELI5"
3. Extract token limit β 50
4. Build prompt:
"""
Act as Rust expert
ELI5
IMPORTANT: Keep your response under 50 tokens.
"""
5. Call OpenAI with prompt + user query
6. Return response
App
βββ PlayerList
β βββ Player Cards (active + spectators)
βββ PromptStack
β βββ Played Card History
βββ VibeOutput
β βββ Query Input
β βββ Generate Button
β βββ AI Response Display
βββ CardDeck
βββ Card Grid (5 cards)
βββ Custom Card Modal
App Component State:
βββ sessionId: string
βββ playerName: string
βββ session: Session
βββ players: Player[]
βββ stack: PlayedCard[]
βββ hand: Card[]
βββ currentPlayer: Player
βββ isMyTurn: boolean
βββ latestResult: PromptResult
βββ generating: boolean
Effects:
βββ SSE Connection (updates on message)
βββ fetchSessionState (on join/updates)
- No Authentication: Sessions are public by ID
- Input Validation: Sanitize all user inputs
- Rate Limiting: Consider adding to /generate endpoint
- XSS Protection: React handles by default
- CSRF: Not applicable (no cookies/sessions)
- Cold Start: ~2-3 seconds for database init
- SSE Overhead: ~1KB per client per 30s (heartbeat)
- Database: SQLite in-memory for active sessions
- OpenAI: 2-5 seconds per generation (gpt-4o-mini)
- Concurrent Players: Tested with 10 simultaneous users
- Session Cleanup: Cron job to archive old sessions
- Connection Pooling: Val Town handles automatically
- CDN for Static Assets: Val Town edge caching
- Database Sharding: Separate tables per date range
- Rate Limiting: Add Redis-like counter for /generate
Legend:
- β : Data flow
- β : Sequential step
- βββ : Component/structure hierarchy
- (FK) : Foreign key
- (PK) : Primary key