A multiplayer card game server using Colyseus framework with TypeScript, Docker, and comprehensive observability.
- TypeScript-based Colyseus game server
- Matchmaking lobby system with FIFO algorithm
- Full frontend UI with real-time synchronization
- Vanilla JavaScript with ES6 modules (no build step)
- Comprehensive E2E testing with Playwright
- Docker-first development environment
- Prometheus metrics and Grafana dashboards
- Test-driven development with comprehensive test coverage
- Card and Deck utilities for building card games
- Professional animations and responsive design
Access the game UI at http://localhost:2567
/- Landing page with game selection/lobby.html- Join matchmaking queue/game.html?matchId=<id>- Play Snap card game
- Real-time multiplayer: Colyseus WebSocket synchronization
- Responsive design: Mobile, tablet, and desktop support
- Animations: Smooth card dealing, snap effects, victory confetti
- Accessibility: Keyboard navigation, ARIA labels, screen reader support
- No build step: Vanilla JavaScript with ES6 modules
# Start server (frontend served at root)
docker compose up
# Run E2E tests
docker compose run e2e
# Run E2E tests in headed mode
docker compose run e2e npx playwright test --headed
# Run E2E tests in debug mode
docker compose run e2e npx playwright test --debugSee docs/FRONTEND.md for complete frontend architecture guide.
- Docker >= 20.x
- Docker Compose >= 2.x
- Node.js >= 18.x (for local development)
# Clone the repository
git clone https://github.com/agileguy/cards.git
cd cards
# Start the entire system (server + observability)
docker compose up
# The following services will be available:
# - Game Server: http://localhost:2567
# - Prometheus: http://localhost:9090
# - Grafana: http://localhost:3000 (admin/admin)
# - Colyseus Monitor: http://localhost:2567/colyseus
# - Metrics: http://localhost:2567/metrics
# - Health Check: http://localhost:2567/health# Run all tests in Docker
docker compose run test
# Run tests in watch mode
docker compose run test npm run test:watch
# Run tests with coverage
docker compose run test npm run test:coverageThe application uses the debug package for structured logging. Logs are disabled by default in production. Enable them using the DEBUG environment variable:
# Enable all logs
DEBUG=cards:* docker compose up
# Enable specific namespace logs
DEBUG=cards:lobby docker compose up
DEBUG=cards:server docker compose up
# Enable multiple namespaces
DEBUG=cards:lobby,cards:server docker compose upAvailable namespaces:
cards:server- Server startup, shutdown, configurationcards:lobby- Lobby room operations, matchmaking
The logger is production-safe - no logs are emitted unless explicitly enabled.
npm install
npm run start:dev # Start with hot reload
npm test # Run tests
npm run build # Build TypeScriptcards/
├── src/ # Backend (TypeScript)
│ ├── server/
│ │ ├── index.ts # Server entry point
│ │ └── config.ts # Configuration
│ ├── rooms/
│ │ ├── LobbyRoom.ts # Matchmaking lobby
│ │ ├── GameRoom.ts # Abstract game base class
│ │ └── SnapRoom.ts # Snap game implementation
│ ├── games/
│ │ ├── IGameEngine.ts # Game engine interface
│ │ └── snap/
│ │ └── SnapEngine.ts # Snap game logic
│ ├── schemas/
│ │ ├── Player.ts # Player schema
│ │ ├── LobbyState.ts # Lobby state schema
│ │ ├── BaseGameState.ts # Base game state
│ │ └── SnapGameState.ts # Snap game state
│ └── utils/
│ ├── Card.ts # Card class
│ ├── Deck.ts # Deck class
│ ├── Matchmaker.ts # Matchmaking logic
│ └── metrics.ts # Prometheus metrics
│
├── public/ # Frontend (Vanilla JS)
│ ├── index.html # Landing page
│ ├── lobby.html # Matchmaking page
│ ├── game.html # Game board
│ ├── css/
│ │ ├── style.css # Main styles
│ │ ├── cards.css # Card rendering
│ │ └── animations.css # Animation library
│ └── js/
│ ├── client.js # Colyseus client wrapper
│ ├── lobby.js # Lobby page logic
│ ├── game.js # Game page logic
│ ├── lib/ # External libraries
│ ├── components/ # UI components
│ └── utils/ # Utilities
│
├── tests/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── e2e/ # End-to-end tests (Playwright)
│
├── docs/
│ ├── API.md # WebSocket API documentation
│ ├── FRONTEND.md # Frontend architecture guide
│ ├── PLAN.md # Project roadmap
│ └── TESTING.md # Testing philosophy
│
├── docker-compose.yml # Multi-service orchestration
├── Dockerfile # Multi-stage build
├── playwright.config.ts # E2E test configuration
├── prometheus.yml # Prometheus config
└── grafana/ # Grafana provisioning
The lobby system provides automatic matchmaking for players using a FIFO (First-In-First-Out) algorithm.
Connect to the lobby room via WebSocket:
const client = new Colyseus.Client('ws://localhost:2567');
const lobby = await client.joinOrCreate('lobby', { name: 'PlayerName' });Client → Server:
join_lobby- Join the matchmaking queue (sent after connecting to room)leave_lobby- Leave the matchmaking queue
Server → Client:
joined_lobby- Acknowledgment with{ sessionId, waitingCount }left_lobby- Acknowledgment of leavematched- Match found! Contains{ matchId, opponentSessionId, matchedAt }timeout- No match found within 30 seconds, being disconnected
// Join the lobby
lobby.send('join_lobby', { name: 'Alice' });
// Listen for match
lobby.onMessage('matched', (message) => {
console.log('Matched!', message.matchId, message.opponentSessionId);
// Join game room with matchId
});
// Listen for timeout
lobby.onMessage('timeout', (message) => {
console.log('No match found:', message.reason);
});Lobby behavior can be configured via environment variables:
LOBBY_TIMEOUT_MS- Maximum wait time before timeout (default: 30000ms)LOBBY_CHECK_INTERVAL_MS- How often to check for timeouts (default: 5000ms)
The server exposes Prometheus metrics at /metrics:
Connection Metrics:
cards_connections_total- Total connectionscards_connections_active- Active connections
Room Metrics:
cards_rooms_total- Total rooms createdcards_rooms_active- Active roomscards_room_players- Players per room (histogram)
Lobby Metrics:
cards_lobby_players_waiting- Current number of players waiting in lobbycards_lobby_matches_total- Total successful matchescards_lobby_match_duration_seconds- Time to find match (histogram)cards_lobby_timeouts_total- Total lobby timeouts
Game Metrics:
cards_games_started_total- Games startedcards_games_completed_total- Games completedcards_game_duration_seconds- Game duration (histogram)
Performance Metrics:
cards_message_latency_seconds- Message latency (histogram)
Access Grafana at http://localhost:3000 (admin/admin) to visualize metrics.
GET /health- Health checkGET /metrics- Prometheus metricsGET /colyseus- Colyseus monitor dashboardGET /api/lobby/count- Get current number of waiting players in lobbyGET /api/games- List available games
ws://localhost:2567/lobby- Matchmaking lobby roomws://localhost:2567/snap- Snap card game room
See docs/API.md for complete WebSocket API documentation.
This project follows strict Test-Driven Development (TDD). See TESTING.md for details.
- Write tests first (TDD approach)
- Ensure all tests pass in Docker
- Follow existing code style
- Update documentation
ISC