SÉANCE
a haunted parlour game for one house, sixteen phones and three real rooms — this page is also the git repo.
Clone
This site serves the repository over git's dumb-HTTP protocol as plain static files (it's hosted on IPFS — there is no git server here, or any server at all):
git clone https://git-game4.ardegazu.ro/seance.git
cd seance
What it is
A party game you play with your feet. Gather 1–16 people in a home, agree which real room is the Study, the Parlour and the Cellar, and open one shared link on every phone. Each time the bell tolls you have ~20 seconds to physically walk to a room and tap it. A ghost sweeps one room — and it favours wherever the crowd stood last bell. Everyone caught loses a candle; lose all three and you fade. Last lit candle wins the séance.
Every bell, one player is secretly the medium: their phone alone names a room the spirit will shun — truthfully. They can share it, keep it, or lie about it out loud. Where you actually stand is the honor system; the walking is the point, not the enforcement. Installable PWA, works from an iOS home screen.
How it works
- No game server. Browsers meet through a blind WebSocket
relay and form a full WebRTC mesh. The room secret lives in the URL fragment
(
#…); the relay only ever sees a one-way HKDF room id. - Everything is end-to-end encrypted. Signaling payloads and
every game frame ride the sueta
p2p core (
client/src/lib/, copied unchanged): per-sender AES-256-GCM keys, structurally nonce-safe sealing, perfect negotiation, ICE-restart recovery, TURN with ephemeral credentials. - One peer conducts. The lowest peer id runs the bell timers and the ghost's dice, collects secret room picks, and broadcasts tiny phase frames; everyone renders from them. Séances are seat-indexed so guests joining or leaving can't corrupt one in flight. If the conductor's phone dies the séance is voided and the next-lowest id takes over; late joiners get a snapshot and watch from the veil until the next séance.
- Hosting without an origin server. The client is a static Vite build pinned to IPFS, served through an HTTPS tunnel that is deliberately never connected — its "offline page" is the game. This page you're reading works the same way.
What's in the repo
client/src/game/ — the game
TypeScript, no framework: typed wire protocol, host-authoritative séance logic (bells, crowd-hungry ghost, medium hints, candle elimination), WebAudio bell tolls, candlelit DOM/CSS scenes.
client/src/lib/ — the p2p core
sueta's reusable app-agnostic stack, vendored unchanged. If you want to
build your own p2p app, start from its README.md.
deploy/ — the publish pipeline
publish-repo.sh builds this very site: landing page + bare
mirror exploded to loose objects (IPFS gateways and git's dumb-HTTP protocol
agree on "every path must exist"), with an anonymity gate that refuses to
publish identity-bearing bytes.
Run it yourself
cd client && npm install && npm run dev # :4173
# open two tabs on the same #room URL and haunt yourself (keys 1/2/3 pick rooms)
Deploy: npm run build, then publish client/dist/ as
static files anywhere — IPFS, a CDN, a folder behind nginx. It needs nothing but
a signaling relay it's allowed to talk to (see src/game/config.ts).
Guarantees & limits, honestly
- The relay operator sees connection metadata, opaque room ids, and frame sizes/timing. Not content, not names, not who the ghost took.
- Anyone with the room link can join and play — capability model. The room cap is 16 guests (the mesh is O(n²); ~12 is the practical ceiling).
- The conductor is one of the players; a cheating conductor could load the ghost's dice. Between friends that's the same trust as letting someone shuffle the deck. Other players' picks can't be forged.
- Your tapped room is taken on faith — the game can't see your feet. That's a feature: the sprint to the cellar is the game.
- MIT licensed. Built on WebRTC, WebCrypto, and IPFS.