Skip to content
tommoonPublic

About

Build and play treasure hunts that unfold as a chat conversation. React + Firebase monorepo.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

KwikQuest

Build and play treasure hunts that unfold as a chat conversation.

Live: kwikquest.io to build · play.kwikquest.io to play


What it does

KwikQuest is two apps over one backend:

  • Creator — authors build a quest: an ordered chain of clues, each with an answer, an optional hint, and custom responses for right and wrong guesses. Publishing yields a short access code.
  • Player — players enter the code and work through the quest in a messaging UI. Clues arrive as incoming messages, answers go out as replies, and the next clue unlocks on a correct guess.

Quests can be anchored to physical places (a museum, a neighbourhood) or run entirely online.

Why it's technically interesting

The conversation is derived, not stored. A session holds an append-only log of events — answer, hint, system. The client folds that log into a conversation and replays it, animating incoming messages with typing delays so a resumed session feels live rather than dumped on screen. Reloading mid-quest restores from sessionStorage and skips the animation instead of replaying it. Because the log is authoritative, the server can correct client state at any point and the UI converges.

Answer matching is deliberately forgiving. Players type on phones, often one-handed and outdoors, so exact string comparison would be hostile. Submissions run through a layered match in answerHelpers.ts: Unicode normalisation and punctuation stripping, then article/filler removal, then exact → substring → token-set overlap → Levenshtein distance with a length-relative threshold that loosens for short answers (one typo in a four-letter word is proportionally huge). All of it runs server-side.

Answers never reach the browser. The getQuest callable projects a player-safe view of a quest — questions and a hasHint boolean, never the answer or hint text. Firestore rules deny direct client access outright: no browser code initialises the Firestore SDK, and every read goes through a callable backed by the Admin SDK. Cheating means guessing, not opening devtools.

Guests are first-class. A cookie-issued localId lets people create and play quests with no account, and claim them later by signing in.

Stack

Frontend React 19, TypeScript, Vite 7, MUI 7
Forms/validation React Hook Form + Zod
Data fetching TanStack Query
Backend Firebase Cloud Functions v2 (europe-west3), Firestore
Auth Firebase Auth — email/password, Google, anonymous guest
Monorepo Turborepo + npm workspaces

Two apps (apps/creator, apps/player) and a functions codebase (apps/functions) sit over seven shared workspace libs in libs/ — chat (the messaging UI), authorization, ui (theme), firebase, types, firestore (rules), sharedAssets.

Run it locally

Prerequisites

  • Node 22 (apps/functions pins this — the deployed runtime matches)
  • Firebase CLI: npm i -g firebase-tools
  • A JDK — the Firestore emulator needs one

Setup

git clone https://github.com/tommoon/kwikquest.git
cd kwikquest
npm install

cp .env.example .env   # then fill in your Firebase web config

The values come from Firebase console → Project settings → Your apps → SDK setup. They are not secrets; they ship in the client bundle either way. Access is controlled by Firestore rules, not by hiding them.

Run

npm run dev

That starts both apps and the emulator suite in parallel:

Creator http://localhost:5173
Player http://localhost:5174
Emulator UI http://localhost:4000

The client points at emulators automatically in dev builds. Override with VITE_USE_FIREBASE_EMULATORS=true|false.

To run one app on its own: npm run dev:creator or npm run dev:player.

To keep emulator data between runs, use npm run dev:persist -w functions — it imports from and exports to apps/functions/emulator-data/, which is gitignored (emulator auth exports store passwords in plaintext).

Other commands

npm run build   # build all workspaces
npm run lint    # lint all workspaces

Project layout

apps/
  creator/     quest authoring app
  player/      quest playing app
  functions/   Cloud Functions — creatorApi/, playerApi/, helpers/
libs/
  chat/        messaging UI component
  authorization/  auth flows, account management, legal pages
  ui/          MUI theme + toast provider
  firebase/    client SDK init
  types/       shared domain types
  firestore/   security rules + indexes
  sharedAssets/   images

Status

A personal project, built to work end to end rather than to be exhaustive. Known gaps: there is no automated test suite, and a handful of react-hooks/exhaustive-deps warnings are suppressed where effects intentionally run once.

License

MIT — see LICENSE.

About

Build and play treasure hunts that unfold as a chat conversation. React + Firebase monorepo.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages