Skip to content

Repository files navigation

Imposter

A real-time, browser-based social deduction party game. Everyone in the room gets the same word — except the imposter, who gets something close enough to bluff with. Describe, discuss, and vote before the imposter talks their way out of it.

Built with a plain Node.js/Express backend and a single-page vanilla JS frontend, wired together over WebSockets. No build step, no framework, no database — just clone it, npm install, and play on your local network.


Contents


How it plays

  1. Create a room. One player creates a room and shares the 4-character room code with everyone else.
  2. Join. Other players join using that code (3–10 players per room).
  3. Roles are assigned. Every round pulls a random category from the content pack. Everyone gets the majority word or question — one random player secretly gets the imposter version instead.
  4. Describe or answer. Players take turns, in a shuffled order, describing their word (or answering their question) out loud without saying it outright.
  5. Vote. Once everyone's had a turn, the room votes on who they think the imposter is. Ties are broken at random.
  6. Reveal. The eliminated player is revealed, along with whether they were actually the imposter and what the real words/questions were.
  7. Play again. The host can send everyone back to the lobby for another round — the game remembers recently used sets so you won't see the same pairing again too soon.

Player names must be unique within a room — if a name is already taken (case-insensitive), the join is rejected so nobody gets confused about who's who.

There are two content modes, mixed into the same pool:

Mode What happens
Word Everyone describes a word (e.g. Pencil) — the imposter secretly has a different, related word (Pen) and has to fake it.
Question Everyone answers a question about a topic (e.g. "How often do you take it for walks?" about a Dog) — the imposter answers a different but similarly-shaped question about a different topic (Cat).

Play styles

The host chooses how players take their turns, via a Game Mode toggle in the lobby:

Style What happens
Typed (default) Each player types their description/answer on their turn; everyone sees the answers build up in a shared feed before voting.
Live Play 🎙️ Players describe out loud, in person or on a call — no typing. The app just tracks the speaking order and shows whose turn it is; the current speaker (or the host) taps to pass the turn.

Live Play is ideal when everyone's in the same room and only needs their phones to see their secret word and to vote.

Quick start

Requirements: Node.js 18 or later.

# 1. Clone the repo
git clone https://github.com/dev-rjav/Imposter
cd Imposter

# 2. Install dependencies
npm install

# 3. (Optional) copy the env template if you want to override any defaults
cp .env.example .env

# 4. Start the server
npm start

The server logs the URL it's listening on:

🎮 Imposter Game → http://localhost:3000
📦 111 sets (81 word, 30 question) across 34 categories
🔄 Tracks last 20 games per room to avoid repeats

Open that URL in a browser. To play with others on the same WiFi, share http://<your-local-ip>:3000 instead of localhost — every player just needs to be able to reach your machine on the network.

Deployment

The app is a single Node service: Express serves the frontend and runs the WebSocket game server on the same origin. The frontend connects to window.BACKEND_URL if set, otherwise it falls back to same-origin — so the default deploy needs no configuration.

Everything on one host (recommended)

Any platform that runs a persistent Node process works — Railway, Render, Fly.io, a VPS, etc.

  1. Push this repo to GitHub and create a new project from it on your host.
  2. The platform installs dependencies and runs npm start automatically. No env vars are required — config.js reads the PORT the platform provides.
  3. Generate/open the public domain. Because the WebSocket is same-origin, leave window.BACKEND_URL empty. Done.

⚠️ The backend needs persistent WebSocket connections, so a static / serverless-only host (e.g. plain Vercel or GitHub Pages) cannot run it.

Split frontend and backend

If you want the static frontend on one host and the WebSocket backend on another (e.g. frontend on Vercel, backend on Railway):

  1. Deploy the backend as above and copy its URL.

  2. In public/index.html, set the backend URL (must be wss:// over HTTPS):

    window.BACKEND_URL = "wss://your-backend.example.com";
  3. Deploy the public/ directory as a static site (Framework: Other, Root Directory: public, no build command).

Project structure

imposter-game/
├── data/                 # All game content — the only place word/question
│   │                     # sets live. server.js never hardcodes content.
│   ├── wordSets.js       #   "describe a word" round content, by category
│   ├── questionSets.js   #   "answer a question" round content, by category
│   └── index.js          #   merges both into ALL_SETS + derives category stats
├── public/               # Static frontend served as-is by Express
│   └── index.html        #   single-page UI: lobby, game, voting, results
├── .github/              # Issue templates, PR template, CI workflow
├── config.js             # Centralised, environment-driven server settings
├── server.js             # Express + WebSocket game server (all game logic)
├── .env.example          # Documents every configurable environment variable
├── package.json
└── README.md

Configuration

The server reads its settings from environment variables, all optional — sensible defaults are defined in config.js. Copy .env.example to .env to override any of them:

Variable Default Description
PORT 3000 Port the HTTP + WebSocket server listens on
MAX_PLAYERS_PER_ROOM 10 Max players allowed in a single room
MIN_PLAYERS_TO_START 3 Minimum players the host needs before starting
SET_HISTORY_SIZE 20 How many previous sets a room remembers, to avoid repeats
ROLE_REVEAL_DELAY_MS 5000 Delay before the first describer's turn, so players can read their word

Adding your own categories

Game content is fully decoupled from game logic, so you can extend it without touching server.js at all.

To add a word-guessing pair, append an entry to data/wordSets.js:

{ type: "word", category: "Your Category", majority: "Real Word", imposter: "Close-but-different Word" },

To add a question pair, append an entry to data/questionSets.js:

{
  type: "question",
  category: "Your Category",
  majority: "Topic A 🎯",
  imposter: "Topic B 🎲",
  majorityQ: "A question that makes sense for Topic A.",
  imposterQ: "A similarly-shaped question that makes sense for Topic B.",
},

Both files export a plain array — nothing else needs to change. data/index.js automatically merges them into ALL_SETS and recomputes category counts on the next server start.

Architecture notes

  • No database. All game state lives in memory (Maps keyed by room code / WebSocket connection) inside server.js. Restarting the server clears every room — this is intentional for a lightweight party game, not an oversight.
  • One WebSocket connection per player, tracked alongside their room code and player ID. Every state-changing action broadcasts a fresh room state to all connected players in that room, so clients never need to diff state themselves.
  • Repeat avoidance is per-room, not global — room.recentHistory stores the indices of the last SET_HISTORY_SIZE sets used in that specific room, so two rooms playing simultaneously don't affect each other.
  • Host migration. If the host disconnects, the next remaining player in the room is silently promoted to host.

Available scripts

Command What it does
npm start Starts the server
npm run dev Starts the server with Node's --watch flag for auto-restart on file changes
npm run lint Runs ESLint over the project
npm test Placeholder — no automated tests yet, see Roadmap

Roadmap

  • Automated tests around room lifecycle and vote resolution
  • Optional category-pack selection from the lobby before starting
  • Reconnect support for players who briefly lose connection mid-round

Support

If you enjoy the game and want to support its development:

Stars on the repo are appreciated too — they help other people find it.

License

MIT — do whatever you'd like with it.

About

One player is quietly handed a different word or question. Everyone describes theirs out loud, then votes on who's lying. Built on Node.js, Express and WebSockets with 110+ word and question sets, rolling history so rounds don't repeating.

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages