Faker is a browser party word game for 3-20 players. Legit players share a secret word and give one-word clues without revealing it; one faker does not know the word and has to bluff. After the clue rounds, players vote. Legit players win only if every legit player votes out the faker; otherwise the faker wins.
Live site: https://play-faker.us
- Single-page frontend in
index.html,app.js, andstyles.css. - Netlify Functions backend in
netlify/functions. - Room state is stored in Netlify Blobs.
- Debug tools and request logs are available with
?debug=1. - Languages: English and Russian word validation.
- Match flow: create/join room, submit words, host starts game, players submit moves in turn order, voting resolves the game, scores persist across the match.
- Host can short-start game 1 with current players if at least 3 players are ready.
- Match-ended rooms are read-only result pages by room code.
- Active non-ended rooms expire after 24 hours without meaningful updates and return
410 Room expired.
Frontend:
index.htmldefines the lobby, room, game, voting, overlay, and debug views.app.jsowns client state, localStorage identity, polling, rendering, validation, and all API calls.styles.cssowns layout and responsive table behavior.validationConstants.jsis generated for browser use.uiErrors.jscontains shared client-only error strings.
Backend:
netlify/functions/*.jsare ES modules.netlify/functions/package.jsonsets"type": "module".shared/validationConstants.cjsis the source for shared validation and timing constants. It is CommonJS because Netlify bundling previously had issues requiring a shared ESM constants file.netlify/functions/roomExpiry.jsenforces active-room expiry.netlify/functions/_vote.jscontains shared voting helpers and voting timer configuration.netlify/functions/_roomStore.jscentralizes room-store access: Netlify Dev uses its local sandbox, while deployed Functions use Netlify's strongly consistent API path.
Storage:
- Netlify Blobs store room records in
faker-rooms. - Deployed room reads use strong consistency because each player action depends on the immediately preceding write. Local development retains Netlify's sandboxed store.
- Room writes update
updatedAt; polling/status reads do not. - Expiry is an access rule, not physical deletion. Cleanup can be added separately later.
- Room codes are 6 characters.
- Names are capped at 24 characters.
- Submitted words and moves are capped at 50 characters.
- Active non-ended rooms expire after 24 hours from
updatedAt || createdAt. - Rooms without timestamps are treated as expired unless
matchEndedis true. - Match-ended rooms remain readable by room code.
- Mutating endpoints reject match-ended rooms.
- Local saved room identity and draft words expire after the same 24-hour active room TTL.
- Leaving a match-ended room only clears local active identity; it does not mutate backend results.
Main room endpoints:
createRoomjoinRoomroomStatusleaveRoomkickPlayer
Word setup:
submitWordsupdateWordsmarkWordsDone
Gameplay:
startGamegetRolegameStatesubmitMove
Voting:
triggerVotecastVote
Debug/helper:
claimPlayer
All endpoints are under:
/.netlify/functions/<name>
Install dependencies:
npm installRun the site and Netlify Functions locally:
npm run devOpen:
http://localhost:8888
For other devices on the same Wi-Fi:
npm run dev:lanIt prints:
http://<your-mac-ip>:8888
For mobile HTTPS testing through Cloudflare Tunnel:
npm run dev:liveOpen:
https://dev.play-faker.us
This assumes a named Cloudflare tunnel faker-dev mapped to dev.play-faker.us:
cloudflared tunnel route dns faker-dev dev.play-faker.usOr a Cloudflare DNS CNAME:
dev -> <tunnel-id>.cfargotunnel.com
Use a different local tunnel name with:
FAKER_TUNNEL_NAME=your-tunnel npm run dev:liveLocal dev uses longer voting timers by default:
VOTE_TOTAL_SECONDS=300VOTE_FINAL_SECONDS=10
Production defaults from shared/validationConstants.cjs are:
VOTE_TOTAL_SECONDS=30VOTE_FINAL_SECONDS=5
Override room expiry locally:
ROOM_ACTIVE_TTL_HOURS=0.01 npm run dev0.01 hours is about 36 seconds.
Generate browser constants:
npm run build:constantsThis writes validationConstants.js from shared/validationConstants.cjs, applying environment overrides for values that should differ in dev, such as voting timers.
The footer badge fetches build.txt and displays:
v1.0 • build <timestamp> <short-sha>
scripts/update_build.sh updates build.txt and refreshes cache-buster query strings in index.html for changed frontend assets.
The pre-commit hook runs this update and stages its generated changes.
Production is hosted on Netlify:
https://faker-game.netlify.app/
The public domain is:
https://play-faker.us
Cloudflare DNS for play-faker.us:
@ CNAME faker-game.netlify.app DNS only
www CNAME faker-game.netlify.app DNS only
Keep these DNS records unproxied unless Cloudflare proxying is intentionally configured and tested with Netlify SSL.
Production deploys through a test-gated GitHub Actions workflow. Eligible pushes
to main deploy only after the complete hosted test suite passes; Markdown-only
pushes do not run CI or deploy. See the deployment guide.
Generate a static itch.io build that points to the production backend:
npm run build:itchThis creates ./itch/ with frontend-only files and rewrites API calls to:
https://play-faker.us/.netlify/functions/
itch/ is generated output and is gitignored.
The project has fast logic tests, local offline API workflows, and Playwright UI coverage for desktop and mobile layouts. Local Git hooks run the fast suite before commits and API workflows before pushes; Markdown-only changes use the documented lightweight exception. See the testing guide.
Gitignored:
.netlify/node_modules/screenshots/notes/itch/dist/
notes/ contains local handoff/testing notes and is not intended for public repo documentation.