Welcome to CrowdGame, a real-time, mobile-controlled multiplayer game platform built with Node.js, Express, and Socket.io.
CrowdGame is designed to run in a hybrid environment: a Big Screen View (e.g., a TV or projector) displays the shared game board, while players connect using their Mobile Controllers via a simple 4-character room code. The current built-in game is a cooperative jigsaw puzzle where players drag, drop, and snap puzzle pieces into place in real time.
We are using this repository as a short intern hiring exercise. Fork this repo and add a new crowd game or meaningfully improve an existing one. The result should be fun, easy to understand, and playable without special setup beyond the normal local development flow below.
You may use any coding tools or AI coding agents you like. We care about the final result, playability, practical judgment, and code clarity.
Good 1-2 day submissions include:
- Adding a new mini-game or game mode
- Improving controls, scoring, or game flow
- Adding better visuals, animations, or feedback
- Improving multiplayer/crowd interaction mechanics
- Polishing an existing game so it feels more complete
Submission requirements:
- Fork this repository.
- Make your changes in your fork.
- Add a short README note explaining what you built, how to run or test it, and any tools or coding agents you used.
- Submit your fork or pull request link.
Submit by end of day Friday, 5 June 2026. We will evaluate submissions by running the game, reviewing the implementation, and using LLM-based analysis for clarity, structure, and maintainability.
flowchart TD
subgraph Clients [Frontend Clients]
Screen["๐ฅ๏ธ Big Screen View (Host)<br>/screen/:roomCode"]
Mobile["๐ฑ Mobile Controller (Player)<br>/join/:roomCode"]
Admin["โ๏ธ Admin Panel<br>/admin"]
end
subgraph Server [Express & Socket.io Server]
NodeApp["Node.js Server (server.js)"]
RoomMgr["Room Manager (roomManager.js)"]
JigsawAct["Jigsaw Activity (jigsaw/index.js)"]
end
subgraph Storage ["Storage & Database"]
DB[(Knex DB: SQLite or Postgres)]
Cache[(Redis Cache / PubSub)]
S3Bucket["โ๏ธ AWS S3 (or Local Fallback)"]
end
Screen <-->|Socket: host-room / piece-move| NodeApp
Mobile <-->|Socket: join-room / place-piece| NodeApp
Admin <-->|REST & Socket: admin-start-activity| NodeApp
NodeApp <--> RoomMgr
RoomMgr <--> JigsawAct
JigsawAct <-->|File operations & Fallbacks| S3Bucket
RoomMgr <-->|Sync room state| Cache
RoomMgr <-->|Save rooms / participants| DB
- Host Setup: The host opens the Big Screen View at
/screen/DEMO(or any generated room code). This establishes a Socket.io host connection. - Player Joins: Players scan a QR code or visit
/join/[ROOM_CODE], enter their name, and get assigned a unique neon color. - Admin Launch: The administrator uses the
/admindashboard to select a puzzle image (or upload one), configure the grid dimensions (rows/columns), and start the activity. - Gameplay:
- The server uses the
sharplibrary to slice the chosen image into grid pieces. - The server assigns pieces to active players (up to 2 at a time).
- Players drag and drop pieces on their phone. Drag coordinates are synced live to the Big Screen.
- When a player drops a piece near its correct coordinates, it snaps into place, awards points, and gives the player new pieces until the puzzle is completed.
- The server uses the
โโโ Dockerfile # Production container configuration
โโโ server.js # Application entry point, server boot & SSL config
โโโ crowdplay.sqlite # Local development SQLite database (auto-generated)
โโโ public/ # Static frontend files (HTML, CSS, JS)
โ โโโ index.html # Landing page
โ โโโ admin.html / .js / .css # Admin panel to upload images & launch rooms
โ โโโ mobile.html / .js / .css# Mobile player controller UI
โ โโโ screen.html / .js / .css# Shared big screen viewer UI
โ โโโ style.css # Shared layout stylesheet
โ โโโ uploads/ # Local directory for uploaded puzzle images
โโโ src/
โ โโโ config.js # Central environment variable mapping
โ โโโ activities/ # Game modes / activities
โ โ โโโ base.js # BaseActivity parent class defining lifecycles
โ โ โโโ jigsaw/
โ โ โโโ index.js # JigsawActivity game state logic
โ โ โโโ puzzleGenerator.js # Slices image buffers into pieces using sharp
โ โโโ routes/ # REST API endpoints
โ โ โโโ admin.js # Admin authentication and image uploads
โ โ โโโ room.js # Room details fetching
โ โโโ services/ # DB, Cache, and S3 Storage clients
โ โ โโโ db.js # Knex database wrapper (Postgres/SQLite)
โ โ โโโ redis.js # Redis client (with an in-memory mock fallback)
โ โ โโโ storage.js # S3 client (with local fs fallback)
โ โโโ socket/ # Socket.io event architecture
โ โโโ index.js # Gateway router for room and player socket events
โ โโโ roomManager.js # Active room and connection tracker
โโโ infrastructure/
โโโ aws/
โโโ cloudformation.yaml # CloudFormation template for AWS ECS Fargate- Node.js:
v18.17.0or higher is required. - System Dependencies: The
sharpimage-processing library compiles native binaries. Ensure your system tools are up to date.
Install dependencies in the root directory:
npm installLaunch the development server:
npm run devAfter npm run dev is running, keep that terminal open and use the URLs below.
Open this on the computer, TV, or projector that will show the shared puzzle board:
https://localhost:3000/screen/DEMO
This screen shows the room code and the shared puzzle board. Leave it open while people play.
Open this in another browser tab:
https://localhost:3000/admin
Log in with the admin password. In local development, the default is:
admin123
Then:
- Choose or upload a puzzle image.
- Set the puzzle rows and columns.
- Start the activity for room
DEMO.
Players should join from their phones using the same room code:
https://<YOUR_LOCAL_IP>:3000/join/DEMO
Replace <YOUR_LOCAL_IP> with the IP address of the computer running the server, for example:
https://192.168.1.5:3000/join/DEMO
Each player should:
- Open the join URL on their phone.
- Accept the browser warning for the self-signed development certificate.
- Enter their name.
- Use the phone screen to drag and drop assigned puzzle pieces.
For a quick local test on the same computer, you can also open:
https://localhost:3000/join/DEMO
If your mobile device cannot access your development machine on the local network, use a public HTTPS tunnel instead of deploying the app immediately.
Recommended local tunnel flow:
- Start the app as a plain HTTP server locally:
NODE_ENV=production PORT=3000 npm run dev
- Start a tunnel to
http://localhost:3000using a tool such as Cloudflare Tunnel or ngrok. - Open the public tunnel URL on your laptop:
https://your-tunnel-url.example/admin https://your-tunnel-url.example/screen/DEMO - Let players scan the QR code from the screen. The QR code will use the tunnel hostname when the tunnel forwards normal proxy headers.
If your tunnel or hosting provider does not preserve the public host header, set PUBLIC_BASE_URL explicitly:
NODE_ENV=production PORT=3000 PUBLIC_BASE_URL=https://your-public-url.example npm run devFor temporary public testing, also set a real admin password:
ADMIN_PASSWORD=change-me JWT_SECRET=change-me NODE_ENV=production PORT=3000 PUBLIC_BASE_URL=https://your-public-url.example npm run devMobile browsers block access to Device Orientation API (gyroscope/accelerometer) and other interactive touch gestures when running over unencrypted HTTP (except on localhost).
To allow mobile devices on your local network to connect and function correctly:
- In development (
NODE_ENVnot set toproduction), the server generates dynamic, self-signed SSL certificates on the fly usingselfsignedand launches an HTTPS server. - The console will output:
Running in DEVELOPMENT mode (Self-signed HTTPS server)... Access Admin Dashboard: https://localhost:3000/admin Access Screen view: https://localhost:3000/screen/DEMO - To connect your phone:
- Find your computer's local IP address (e.g.,
192.168.1.5). - Navigate on your phone to
https://<YOUR_LOCAL_IP>:3000/join/DEMO. - Your browser will show a warning ("Your connection is not private"). Click Advanced -> Proceed to bypass the self-signed warning.
- Find your computer's local IP address (e.g.,
Set the following variables in your shell environment when you need to override the defaults:
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Mode of operation (development or production) |
development |
PORT |
The port the application listens on | 3000 |
PUBLIC_BASE_URL |
Public HTTPS origin used for QR/join URLs when running behind a tunnel or hosted domain | None |
DATABASE_URL |
PostgreSQL connection string. If omitted, falls back to SQLite | None |
USE_SQLITE_FALLBACK |
Set to false to disable SQLite database fallback |
true |
SQLITE_PATH |
Path to the SQLite DB file | ../crowdplay.sqlite |
REDIS_URL |
Redis URL. If omitted, falls back to an In-Memory mock client | None |
S3_BUCKET |
AWS S3 Bucket name for puzzle image storage | None |
AWS_REGION |
AWS Region for the S3 bucket | us-east-1 |
AWS_ACCESS_KEY_ID |
IAM credential key for S3 uploads | None |
AWS_SECRET_ACCESS_KEY |
IAM credential secret for S3 uploads | None |
ADMIN_PASSWORD |
Password to log in to /admin |
admin123 |
JWT_SECRET |
Secret key used to sign Admin JWT session tokens | crowdplay-super-secret-key-change-in-prod |
In production (NODE_ENV=production):
- The Node.js application runs as a standard HTTP server (dynamic SSL generation is skipped for performance).
- SSL termination and HTTPS routing must be handled at the load-balancer level (e.g., AWS Application Load Balancer).
- A health-check endpoint is available at
/healthreturning HTTP 200 (required for target groups).
A complete CloudFormation template is provided under cloudformation.yaml to provision:
- An ECS Cluster running tasks on Fargate.
- An Application Load Balancer with listener rules routing HTTPS traffic to HTTP container instances.
- Integration support for an RDS PostgreSQL database instance and AWS ElastiCache Redis.