Real-time multiplayer word guessing game built with Go and React
Features • Demo • Quick Start • API • Contributing
Hat Game is a digital version of the classic party game where players guess words/names across three rounds with different explanation rules. Built with real-time WebSocket communication, it supports 2-8 players with spectator mode, game history, and mobile-friendly responsive design.
Inspired by: The traditional "Hat" party game (also known as "Celebrities" or "Fishbowl")
- 3 Rounds with Different Rules
- Round 1: Explain with words (unlimited)
- Round 2: Explain with gestures only
- Round 3: Explain with one word only
- 2-8 Players with automatic team rotation
- 60-Second Turns with live countdown timer
- Seating Arrangement — host can reorder players before game starts
- Animated Table — toggle between round/square visualization
- Live Scoreboard — per-round and total scores
- Real-time WebSocket communication with auto-reconnect
- Spectator Mode — watch games and send emoji reactions (👏 😂 😮 🔥)
- Quick Join Links — shareable
/join/{roomId}URLs - Password-Protected Rooms — private games
- Presence Detection — see who's online
- Guest Mode — play without registration
- JWT Authentication — optional account creation for stats tracking
- Game History — track personal stats and recent games
- Sound Effects — countdown beeps and turn notifications
- Mobile-Friendly — fully responsive design
- Dark Mode Ready — UI prepared for theme switching
Note: Add screenshots/GIFs of gameplay here when available
# Typical game flow:
Lobby → Enter Cards → Seating → Rules → Round 1 → Round 2 → Round 3 → Final Scores
Live Demo: (Coming soon)
| Layer | Technology |
|---|---|
| Backend | Go 1.24, gorilla/websocket, zerolog |
| Database | PostgreSQL (pgx/v5, sqlc), Redis (go-redis) |
| Frontend | React 18, TypeScript, Vite, TailwindCSS |
| State | Zustand (client), Redis (server) |
| Testing | Go testing, Playwright E2E |
| Auth | JWT (golang-jwt), bcrypt |
hat-game/
├── cmd/server/ # Application entry point
├── internal/
│ ├── app/ # Server initialization
│ ├── config/ # Environment configuration
│ ├── domain/ # Business logic (DDD)
│ │ ├── auth/ # Authentication service
│ │ ├── game/ # Game mechanics & state machine
│ │ └── room/ # Room management
│ ├── http/ # Middleware (auth, rate limiting)
│ ├── storage/
│ │ ├── postgres/ # User persistence, game history
│ │ └── redis/ # Active game state, sessions
│ └── transport/
│ ├── rest/ # HTTP handlers, DTOs
│ └── ws/ # WebSocket hub & connections
├── frontend/ # React SPA (Feature-Sliced Design)
│ ├── src/
│ │ ├── app/ # Router, providers
│ │ ├── entities/ # Game, Player models
│ │ ├── features/ # Auth, Room logic
│ │ ├── pages/ # Landing, Room, History, Spectator
│ │ ├── widgets/ # Lobby, Game, Scoreboard, Seating
│ │ └── shared/ # API clients, UI components
│ └── e2e/ # Playwright tests
└── pkg/ # Shared utilities (clock, randid, validator)
LOBBY → ENTERING_CARDS → SEATING → READING_RULES → ROUND_IN_PROGRESS → ROUND_FINISHED
↑ ↓
└────────────── (repeat 3x) ─────────┘
↓
GAME_FINISHED
- Go 1.24+
- Node.js 18+ & npm
- Docker & Docker Compose
- Make (optional, for convenience)
# Clone repository
git clone https://github.com/iteplenky/hat-game.git
cd hat-game
# Install dependencies and setup infrastructure
make setup
# Start development server
make devThe application will be available at http://localhost:8080
# Install Go tools
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
go install github.com/pressly/goose/v3/cmd/goose@latest
# Start databases
docker-compose up -d postgres redis
# Run migrations
cd internal/storage/postgres/migrations
goose postgres "postgres://user:pass@localhost:5432/hat_game" up
# Install frontend dependencies
cd frontend
npm install
# Build frontend
npm run build
# Start backend
go run cmd/server/main.gomake dev # Hot reload mode (air + vite)
make test # Run all tests
make lint # Run linters
make db-up # Start PostgreSQL + Redis
make db-down # Stop databases
make db-migrate # Run database migrations
make sqlc-generate # Regenerate SQL code from queries
make docker-build # Build Docker image| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
POST |
/api/auth/guest |
Create guest session | ❌ |
POST |
/api/auth/register |
Register new account | ❌ |
POST |
/api/auth/login |
Login with credentials | ❌ |
GET |
/api/auth/me |
Get current user | ✅ |
POST |
/api/rooms |
Create new room | ✅ |
GET |
/api/rooms |
List public rooms | ❌ |
GET |
/api/rooms/:id |
Get room info | ❌ |
POST |
/api/rooms/:id/join |
Get WebSocket join token | ✅ |
DELETE |
/api/rooms/:id |
Close room (host only) | ✅ |
GET |
/api/users/me/stats |
Get user statistics | ✅ |
GET |
/api/users/me/history |
Get game history | ✅ |
GET |
/join/:id |
Quick join page | ❌ |
Connect: ws://localhost:8080/ws?roomId={roomId}&playerId={playerId}
Client → Server Messages
// Submit cards for the round
{ type: "submit_cards", cards: ["Name1", "Name2", ...] }
// Start the game (host only)
{ type: "start_game" }
// Start next round (host only)
{ type: "start_round", round: 1|2|3 }
// Confirm rules have been read
{ type: "confirm_rules" }
// Start your turn
{ type: "start_turn" }
// Mark card as guessed
{ type: "card_guessed" }
// Skip current card
{ type: "skip_card" }
// End your turn early
{ type: "end_turn" }
// Reorder players before game starts (host only)
{ type: "reorder_players", playerOrder: ["id1", "id2", ...] }
// Toggle rules confirmation requirement (host only)
{ type: "set_skip_rules", skipRules: true|false }Server → Client Messages
{ type: "room_state", ... } // Full game state
{ type: "player_joined", player } // New player joined
{ type: "player_left", playerId } // Player disconnected
{ type: "game_started" } // Game has begun
{ type: "round_started", round } // New round started
{ type: "turn_started", turn } // Turn started
{ type: "card_drawn", card } // New card drawn
{ type: "card_guessed", cardId } // Card was guessed
{ type: "turn_ended", scores } // Turn finished
{ type: "round_finished", scores } // Round finished
{ type: "game_finished", winner } // Game completed
{ type: "timer_tick", remaining } // Countdown update
{ type: "players_reordered", ... } // Player order changed
{ type: "spectator_joined", ... } // Spectator connected
{ type: "reaction", emoji, ... } // Spectator reactionCopy .env.example to .env and configure:
# Server
PORT=8080
ENV=development
# PostgreSQL
DATABASE_URL=postgres://hatgame:hatgame@localhost:5432/hat_game
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# Security (generate strong secrets!)
SESSION_SECRET=your-32-char-secret-minimum-length
ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173
# Frontend (frontend/.env)
VITE_API_URL=http://localhost:8080
VITE_WS_URL=ws://localhost:8080View Schema
-- Users (persistent storage)
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR UNIQUE,
password_hash VARCHAR,
last_name VARCHAR,
is_guest BOOLEAN DEFAULT false,
total_games INTEGER DEFAULT 0,
total_wins INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
last_seen_at TIMESTAMPTZ DEFAULT NOW()
);
-- Game History
CREATE TABLE game_history (
id UUID PRIMARY KEY,
room_id VARCHAR NOT NULL,
user_id UUID REFERENCES users(id),
player_name VARCHAR NOT NULL,
is_host BOOLEAN DEFAULT false,
started_at TIMESTAMPTZ NOT NULL,
finished_at TIMESTAMPTZ,
final_score INTEGER DEFAULT 0,
place INTEGER,
won BOOLEAN DEFAULT false
);
-- Indexes
CREATE INDEX idx_game_history_user_id ON game_history(user_id);
CREATE INDEX idx_game_history_finished_at ON game_history(finished_at);Package Coverage
auth 80.6%
game 67.3%
room 92.3%
validator 66.7%
randid 84.6%
# Backend tests
make test # All tests with coverage
go test -v ./... # Verbose mode
go test -cover ./... # Coverage report
# Frontend E2E tests
cd frontend
npm test # Playwright tests
npm run test:ui # Interactive mode- Backend: ~10,600 lines of Go
- Frontend: ~6,200 lines of TypeScript
- Total Files: 100+
- Dependencies: 15 Go modules, 20 npm packages
- Voice chat integration
- Custom card packs
- Tournament mode
- Mobile apps (React Native)
- Analytics dashboard
- Internationalization (i18n)
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please read CONTRIBUTING.md for details on our code of conduct and development process.
- Follow Go best practices and
golangci-lintrules - Write tests for new features
- Update documentation for API changes
- Keep commits atomic and descriptive
- Use conventional commit messages
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by the traditional "Hat" party game
- Built with amazing open-source libraries:
- gorilla/websocket - WebSocket implementation
- sqlc - Type-safe SQL
- React - UI framework
- TailwindCSS - Styling
- Zustand - State management
Author: Denis Teplenky (@iteplenky)
Issues: GitHub Issues
Made with ❤️ and Go