A full-stack real-time multiplayer drawing and guessing game built with React, Node.js, Socket.IO, and Redis. Players take turns drawing words while others guess, with real-time synchronization, scoring, and AI-powered drawing recognition.
- Real-Time Multiplayer Gameplay: Up to 6 players per room with live drawing synchronization
- Interactive Drawing Board: Canvas-based drawing with color selection, undo functionality, and eraser tool
- Word Guessing System: Players guess the word being drawn with real-time feedback
- Scoring System: Dynamic scoring based on how quickly players guess correctly
- Room Management: Automatic room creation and player matching
- Game States: State machine pattern with Waiting, Drawing, and Finished states
- Persistent State: Redis-based game state persistence with automatic TTL management
- Responsive UI: Modern, responsive design built with React and Tailwind CSS
- Node.js with Express - RESTful API server
- TypeScript - Type-safe development
- Socket.IO - Real-time bidirectional communication
- Redis (ioredis) - State persistence and room management
- Docker - Containerization
- React 19 - UI framework
- TypeScript - Type-safe development
- Vite - Build tool and dev server
- React Router - Client-side routing
- Tailwind CSS - Utility-first CSS framework
- Socket.IO Client - Real-time communication
- Canvas API - Drawing functionality
scribble/
├── backend/
│ ├── src/
│ │ ├── app.ts # Express app configuration
│ │ ├── server.ts # Server entry point
│ │ ├── controllers/ # API controllers
│ │ │ ├── apiController.ts
│ │ │ └── drawingGuesser.ts # AI drawing recognition
│ │ ├── game/ # Game logic
│ │ │ ├── game.ts # Core game class
│ │ │ ├── gameManager.ts # Game instance management
│ │ │ ├── roomManager.ts # Room and player management
│ │ │ └── states/ # Game state machine
│ │ │ ├── gameState.ts
│ │ │ ├── waitingState.ts
│ │ │ ├── drawingState.ts
│ │ │ └── finishedState.ts
│ │ ├── sockets/ # Socket.IO handlers
│ │ │ ├── socketManager.ts
│ │ │ ├── handleSocketConnection.ts
│ │ │ └── connectToRedis.ts
│ │ ├── routes/ # API routes
│ │ └── middleware/ # Express middleware
│ ├── config/ # Configuration
│ ├── Dockerfile
│ └── docker-compose.yml
│
└── frontend/
├── src/
│ ├── App.tsx # Main app component
│ ├── pages/ # Page components
│ │ ├── Home.tsx
│ │ └── Game.tsx
│ ├── components/ # React components
│ │ ├── composed/ # Composite components
│ │ └── game/ # Game-specific components
│ │ ├── DrawingBoard.tsx
│ │ ├── ChatContainer.tsx
│ │ ├── Players.tsx
│ │ └── GameLayout.tsx
│ ├── context/ # React context providers
│ ├── utils/ # Utility functions
│ └── config/ # Frontend configuration
├── Dockerfile
└── docker-compose.yml
- Node.js (v20 or higher)
- npm or yarn
- Redis (for local development) or Docker
- Docker and Docker Compose (optional, for containerized deployment)
-
Clone the repository
git clone <repository-url> cd scribble
-
Install backend dependencies
cd backend npm install -
Install frontend dependencies
cd ../frontend npm install
-
Backend Environment Variables
Create a
.envfile in thebackend/directory:PORT=5000 NODE_ENV=development CORS_ORIGIN=http://localhost:5173 REDIS_URL=redis://localhost:6379
-
Frontend Configuration
Update the socket connection URL in
frontend/src/utils/socket.tsif needed:SocketManager.instance = io("http://localhost:5000/");
Backend:
cd backend
docker-compose upFrontend:
cd frontend
docker-compose up-
Start Redis (if not using Docker)
redis-server
-
Start Backend Server
cd backend npm run devServer will run on
http://localhost:5000 -
Start Frontend Development Server
cd frontend npm run devFrontend will run on
http://localhost:5173 -
Open your browser Navigate to
http://localhost:5173
Backend:
cd backend
npm run build
npm startFrontend:
cd frontend
npm run build
npm run preview- Join a Room: Enter your name and avatar, then join or create a room
- Wait for Players: Game starts automatically when 2+ players join
- Take Turns Drawing: Each player gets a turn to draw a word
- Guess the Word: Other players try to guess what's being drawn
- Score Points: Faster guesses earn more points
- Win: Player with the highest score wins!
The game uses a state machine pattern with three main states:
- WaitingState: Waiting for players to join or between rounds
- DrawingState: Active drawing and guessing phase
- FinishedState: Game completion
- Socket.IO Events:
join-room: Player joins a game roomdraw-command: Drawing strokes synchronized in real-timeguess: Player submits a guessroom-update: Room player list updatesword-update: New word/round startscorrect-guess: Player guessed correctly
-
Redis stores:
- Game state (serialized game objects)
- Room player lists
- Player-to-room mappings
- Active rooms set
-
TTL Management: Automatic expiration (1-2 hours) for cleanup
- Command Pattern: Drawing operations use command pattern for undo functionality
- Debounced Emission: Drawing commands are debounced before sending to reduce network traffic
- Canvas Rendering: HTML5 Canvas API for drawing operations
Edit backend/config/index.ts:
gameTime: 20, // Drawing time per round (seconds)
waitTime: 10, // Wait time between rounds (seconds)Configure allowed origins in backend/config/index.ts:
corsOrigin: process.env.CORS_ORIGIN || "*";GET /api/sample- Sample data endpointPOST /api/guess- Submit drawing image for AI recognition- Body:
multipart/form-datawithimagefile - Response:
{ message: "recognized word" }
- Body:
See the "Real-Time Communication" section above for event details.
Backend:
npm run dev- Start development server with nodemonnpm run build- Compile TypeScriptnpm run watch- Watch mode for TypeScript compilationnpm start- Start production server
Frontend:
npm run dev- Start Vite dev servernpm run build- Build for productionnpm run preview- Preview production buildnpm run lint- Run ESLint
cd backend
docker-compose up -dIncludes:
- Node.js application container
- Redis container
- Automatic dependency management
cd frontend
docker-compose up -dIncludes:
- Multi-stage build (Node.js build + Nginx serve)
- Nginx configuration for static file serving
- Production-optimized build
- API Keys: The Google Gemini API key is currently hardcoded in
drawingGuesser.ts. Move this to environment variables in production. - CORS: Configure appropriate CORS origins for production
- Redis: Secure Redis instance in production (password, network isolation)
- User authentication and profiles
- Custom word lists and categories
- Private rooms with passwords
- Spectator mode
- Drawing history and replays
- Mobile app support
- Enhanced AI drawing analysis
- Leaderboards and statistics
- Custom avatars and themes
ISC
Contributions are welcome! Please feel free to submit a Pull Request.
For questions or support, please open an issue on the repository.