Vital Verdict
An Among Us-inspired social deduction game where your heart and facial expressions gives you away. Each player's webcam measures their pulse live using the Presage SmartSpectra SDK, and everyone sees everyone's heart rate during emergency meetings.
On a normal Wi-Fi network, one laptop hosts and the others join it. On a campus network that blocks laptop-to-laptop traffic, everyone joins the shared Cloud Run server instead.
How it works
- One camera per player. SmartSpectra measures one face per camera, so every player uses their own laptop.
- Calibration. In the lobby each player sits still for about 20 seconds. The app records their resting pulse (the "baseline").
- Spikes. During meetings, a player whose pulse rises 10+ BPM above their resting rate gets a red glow and a sound cue.
- Accuse. Accusing someone puts them in the spotlight, so everyone watches their pulse react — their card also shows how much their BPM has moved since the moment they were accused, not just vs. resting baseline. Once someone's in the spotlight, nobody can switch the accusation to a different player for 9 seconds, so a pulse actually has time to react before the group moves on.
- Fake vitals. Once per meeting the impostor can broadcast a calm, resting pulse for 25 seconds. Is that steady number real?
- Calm the reactor. One possible task only completes if you keep your pulse near resting for 6 seconds.
- Face cams. During discussion and voting, everyone's live face appears on their meeting tile at a few frames per second — enough to read reactions without flooding the network.
Heart rate is computed on each laptop by the SDK. The SDK needs internet access only to validate the API key; game traffic stays on your local network.
Setup (every laptop)
Requirements: macOS on Apple Silicon (also supports Linux x64/arm64 and Windows x64), Node.js 20+, a webcam.
npm install
cp .env.example .env # then paste your key into PRESAGE_API_KEY
Get a free API key from the Presage Developer Portal.
.env settings:
| Variable | Required | Default | Purpose |
|---|---|---|---|
PRESAGE_API_KEY |
yes | none | SmartSpectra key. Without it the app uses simulated vitals. |
VV_SIM |
no | 0 |
1 forces simulated vitals (hold H to fake a spike). |
VV_PORT |
no | 7777 |
Port the host listens on. Must match on every laptop. |
Running
npm start # real camera vitals
npm run sim # simulated vitals, no camera SDK
npm run host # optional: run the game server alone in a terminal
npm test # game logic + LAN relay tests
- Host laptop: enter a name, click Host game. The lobby shows the IP address others should use.
- Other laptops: enter a name, type the host's IP, click Join game.
- Wait for Vitals locked on each laptop, then the host clicks Start game. At least 3 players are needed; the host can add bots to fill seats.
Shared server (campus Wi-Fi)
The server for this event is already running at https://vital-verdict-120242246392.us-east1.run.app.
Each player still runs the app on their own laptop. Nobody clicks Host game.
npm install
cp .env.example .env # paste your own PRESAGE_API_KEY
npm start
Enter a name, paste the URL above into Join a friend, and click Join game. The first person in the lobby is the host and can add a bot, then start once vitals are locked.
No Docker, gcloud, or VV_PORT change is required to join. .env stays on your machine and is not in git. Shut the Cloud Run service down after the event so the always-on instance does not keep billing.
To test two windows on one laptop (only one can use the camera):
npm start # window 1: real camera, host
VV_SIM=1 VV_PROFILE=2 npm start # window 2: simulated, join "localhost"
Game flow
- Lobby: calibrate resting pulse.
- Role reveal: one player is secretly the impostor.
- Shift (150s): everyone spawns around the emergency button in the Mess Hall and explores the ship. Crew walk to their task stations (yellow, with arrows at the screen edge pointing to them). The impostor fakes tasks and eliminates crew up close (25s cooldown), leaving a body. Finding and reporting a body, pressing the emergency button, or the shift timer running out starts a meeting. Each player can only press the emergency button once per match (body reports are unlimited), and there's a ~28s cooldown after a shift starts or a meeting ends before anyone can call another one.
- Meeting (45s discussion, 20s voting): a banner shows why the meeting started — emergency button, reported body, or the shift timer — and who called it. Live heart-rate cards for everyone, with Accuse and Vote buttons. If every surviving player marks themselves ready, voting opens early instead of waiting out the discussion timer. The host can also skip timers.
- Reveal: the ejected player's role is shown.
- Win: crew wins by ejecting the impostor or finishing all tasks; the impostor wins when they equal the remaining crew. If the impostor kills someone (or they're voted out) with unfinished tasks, those tasks get handed off to the surviving crew instead of disappearing — eliminating someone never shrinks the total work left.
Controls (during the shift)
| Key | Action |
|---|---|
W A S D / arrow keys |
Move |
E |
Use a task station, or hold to call the emergency button in the Mess Hall |
R |
Report a body you're standing near |
Q |
Eliminate a nearby crewmate (impostor only) |
Esc |
Cancel the task you're doing |
You can only see players within your vision radius (the impostor sees further). Eliminated players become ghosts who can drift through walls and watch, but can't vote.
The ship has nine rooms: Mess Hall, Medbay, Lab, Hub, Reactor, Navigation, Electrical, Storage and Comms.
Getting a good reading
- Light in front of you, not behind (no window at your back).
- Face the camera, upper chest visible, about an arm's length away.
- Sit still. Talking and big movements reduce accuracy. Pulse is a ~12 second average, so reactions show up after a few seconds.
- No flickering light sources (TVs, some LEDs) on your face.
- The yellow hint over the camera preview tells you what to fix (too dark, face not centered, multiple faces, and so on).
Project layout
src/main/ Electron main process + preload (SDK bridge, host game server)
src/server/ WebSocket game server (state machine, positions, bots, vitals relay)
src/shared/ map.js: rooms, corridors, collision, task stations, bot waypoints (used by server and client)
src/renderer/ UI: vitals.js (SmartSpectra + simulator), app.js (screens), map-view.js (canvas map + WASD), net.js, sound.js
src/socket-url.js turns a typed IP or Cloud Run URL into a websocket address
Dockerfile game server only, for Cloud Run
test/ node:test suite
SDK metrics are for general wellness and entertainment only, not medical use. Among Us is a trademark of Innersloth; this project uses no Among Us assets.
npm install
cp .env.example .env # then paste your key into PRESAGE_API_KEY
Get a free API key from the Presage Developer Portal.
.env settings:
| Variable | Required | Default | Purpose |
|---|---|---|---|
PRESAGE_API_KEY |
yes | none | SmartSpectra key. Without it the app uses simulated vitals. |
VV_SIM |
no | 0 |
1 forces simulated vitals (hold H to fake a spike). |
VV_PORT |
no | 7777 |
Port the host listens on. Must match on every laptop. |
Running
npm start # real camera vitals
npm run sim # simulated vitals, no camera SDK
npm run host # optional: run the game server alone in a terminal
npm test # game logic + LAN relay tests
- Host laptop: enter a name, click Host game. The lobby shows the IP address others should use.
- Other laptops: enter a name, type the host's IP, click Join game.
- Wait for Vitals locked on each laptop, then the host clicks Start game. At least 3 players are needed; the host can add bots to fill seats.
Shared server (campus Wi-Fi)
The server for this event is already running at https://vital-verdict-120242246392.us-east1.run.app.
Each player still runs the app on their own laptop. Nobody clicks Host game.
npm install
cp .env.example .env # paste your own PRESAGE_API_KEY
npm start
Enter a name, paste the URL above into Join a friend, and click Join game. The first person in the lobby is the host and can add a bot, then start once vitals are locked.
No Docker, gcloud, or VV_PORT change is required to join. .env stays on your machine and is not in git. Shut the Cloud Run service down after the event so the always-on instance does not keep billing.
To test two windows on one laptop (only one can use the camera):
npm start # window 1: real camera, host
VV_SIM=1 VV_PROFILE=2 npm start # window 2: simulated, join "localhost"
Game flow
- Lobby: calibrate resting pulse.
- Role reveal: one player is secretly the impostor.
- Shift (150s): everyone spawns around the emergency button in the Mess Hall and explores the ship. Crew walk to their task stations (yellow, with arrows at the screen edge pointing to them). The impostor fakes tasks and eliminates crew up close (25s cooldown), leaving a body. Finding and reporting a body, pressing the emergency button, or the shift timer running out starts a meeting.
- Meeting (60s discussion, 30s voting): live heart-rate cards for everyone, Accuse and Vote buttons. The host can skip timers.
- Reveal: the ejected player's role is shown.
- Win: crew wins by ejecting the impostor or finishing all tasks; the impostor wins when they equal the remaining crew.
Controls (during the shift)
| Key | Action |
|---|---|
W A S D / arrow keys |
Move |
E |
Use a task station, or the emergency button in the Mess Hall |
R |
Report a body you're standing near |
Q |
Eliminate a nearby crewmate (impostor only) |
Esc |
Cancel the task you're doing |
You can only see players within your vision radius (the impostor sees further). Eliminated players become ghosts who can drift through walls and watch, but can't vote.
The ship has nine rooms: Mess Hall, Medbay, Lab, Hub, Reactor, Navigation, Electrical, Storage and Comms.
Getting a good reading
- Light in front of you, not behind (no window at your back).
- Face the camera, upper chest visible, about an arm's length away.
- Sit still. Talking and big movements reduce accuracy. Pulse is a ~12 second average, so reactions show up after a few seconds.
- No flickering light sources (TVs, some LEDs) on your face.
- The yellow hint over the camera preview tells you what to fix (too dark, face not centered, multiple faces, and so on).
Project layout
src/main/ Electron main process + preload (SDK bridge, host game server)
src/server/ WebSocket game server (state machine, positions, bots, vitals relay)
src/shared/ map.js: rooms, corridors, collision, task stations, bot waypoints (used by server and client)
src/renderer/ UI: vitals.js (SmartSpectra + simulator), app.js (screens), map-view.js (canvas map + WASD), net.js, sound.js
src/socket-url.js turns a typed IP or Cloud Run URL into a websocket address
Dockerfile game server only, for Cloud Run
test/ node:test suite
SDK metrics are for general wellness and entertainment only, not medical use. Among Us is a trademark of Innersloth; this project uses no Among Us assets.
Built With
- computervision
- electron
- game
- javascript
- node.js
- presage


Log in or sign up for Devpost to join the conversation.