An Open Source, Real World Scavenger Hunt type competition game for team play.
Find a file
2026-03-25 08:32:15 -05:00
backend Add specific team chat channels, and update README 2026-03-25 08:32:15 -05:00
frontend Add specific team chat channels, and update README 2026-03-25 08:32:15 -05:00
node_modules Added settings for user data deletion 2026-03-21 12:23:20 -05:00
application_requirements.md Initial commit 2026-03-18 09:02:21 -05:00
docker-compose.yml tiny update 2026-03-18 11:29:27 -05:00
package-lock.json Added settings for user data deletion 2026-03-21 12:23:20 -05:00
package.json Initial commit 2026-03-18 09:02:21 -05:00
README.md Add specific team chat channels, and update README 2026-03-25 08:32:15 -05:00

Treasure Trails

An online scavenger hunt application where Game Masters create and manage hunts, Teams compete to complete clues, and Spectators can follow along in real-time.

Inspiration

Scavenger Hunts, of course, but also a Movie from my childhood called "Midnight Madness". A game master creates teams from people he knows at his University. The Jocks, The Nerds, The awkward girls, and the average guy / girl mix. Yep, as cliche as you can get. BTW - Michael J Fox's first movie role as a young teen.

Game Play

Game Master

Users can create a game, making them the Game Master. Give your game a name, and if you'd like a prize for the winner(s). Set the radius of the map the game legs and objectives should remain within. Set game rules for all to read and abide by. Set default leg time-outs, and time based penalty periods for taking too long, or breaking rules. And get on with creating routes.

You can create 1 route for all teams to use, or you can create multiple routes, perhaps 1 per team. The system shows distance totals for each route, so you can make sure they are approximately equal if needed.

You can invite players who will make teams, and keep the game private, or you can make a public game, which still allows invites, but you can also allow users to simply join a game and make their team.

Game masters devise routes which can each have multiple legs and a clue for each leg. The game allows for taking a photo to prove the completion of the objective of a leg, or perhaps you want to encourage patronage at local businesses so you make it a purchase leg (where they still send a photo of their purchase and receipt). It's all in good fun.

Teams and Team Members

Once the teams are formed, and the routes, legs, and clues set, the game master will start the game.

The teams will attempt to solve a clue (if needed), and get to their first objective in the allotted time for the leg. Provide the required proof, and the game master will approve or deny their ability to go to the next leg.

The game master will control the game from the game master dashboard, where the game master sees all teams' locations on the map, which leg they are on, and can chat with teams as needed (broadcast or direct messages).

The teams have the mobile version which runs right in your mobile device browser. Recommended to just have the team captain's device for the game. Don't leave the browser window during the game, or take a chance on a time based deduction, or removal from the game. Allow location tracking, and your team is set.

Use the map, built in photo sharing options, and chat as needed to get clues, and complete objectives. And be the first team to the end.

Spectators

Part of "Midnight Madness" was how the Game Master's neighbors all got into the game and made it as exciting from the control room as it was on the streets.

With that in mind, public games will have a spectator mode, they'll be able to follow the team progression throughout the race.

Who would ever use this?

Well, me... I have an annual family reunion, and I think my family would have a blast with something like this.

Also, perhaps as a way to earn money for charity, or just a way to have local activities that people can join in on. It's a great way to get out of the house.

What about the Game Master? Don't forget, in the movie the game master moved to the final objective to meet the teams and be there to personally congratulate the winners. His neighbors all went with him as well. A laptop, and mobile hotspot, or tablet would likely be ideal for such mobility in this day and age!

Features

  • Game Master Dashboard:
    • Create scavenger hunts with routes which can each have multiple legs and clues,
    • Manage live games,
    • View team progress on real-time map,
    • Chat with all teams or direct message specific teams
  • Team Interface:
    • Mobile-friendly gameplay with clues,
    • Map navigation,
    • Photo submissions, and
    • Real-time chat
  • Spectator View:
    • Public dashboard showing team positions and leaderboard
  • Real-time Updates:
    • Live tracking via WebSockets (Socket.io)
  • OpenStreetMap Integration:
    • All maps use OpenStreetMap data
  • User Preferences:
    • Metric/Imperial distance units

Tech Stack

Component Technology
Frontend Vue 3 + TypeScript + Vite + Pinia
Backend Node.js + Express + TypeScript
Real-time Socket.io
Database PostgreSQL + Prisma
Maps Leaflet + OpenStreetMap
Styling Pico CSS

Prerequisites

  • Node.js 20+ (for local development)
  • Docker & Docker Compose

Quick Start with Docker

Install Docker and Docker Compose

curl https://get.docker.com | sh

The easiest way to run the application:

# Start all services (PostgreSQL, Backend, Frontend)
docker compose up -d

# View logs
docker compose logs -f

# Stop services
docker compose down

The application will be available at:

Local Development

1. Clone and Install Dependencies

# Backend
cd backend
npm install

# Frontend
cd ../frontend
npm install

2. Set Up Database

Create a PostgreSQL database:

# Connect to PostgreSQL
psql -U postgres

# Create database
CREATE DATABASE treasure_trails;

Update the database connection in backend/.env:

DATABASE_URL="postgresql://postgres:your_password@localhost:5432/treasure_trails?schema=public"

3. Initialize Database Schema

cd backend
npx prisma generate
npx prisma db push

4. Run the Backend

cd backend
npm run dev

The backend API will run on http://localhost:3001

5. Run the Frontend

cd frontend
npm run dev

The frontend will run on http://localhost:5173

Usage

As a Game Master

  1. Register a new account
  2. Go to Dashboard and click "Create New Game"
  3. Fill in game details and click on the map to set the treasure location
  4. Click "Create Game" to save as draft
  5. Click "Edit Game" to add routes / legs / clues
  6. Once legs are added, click "Publish Game" to make it live
  7. Use the "Live Dashboard" to monitor teams and communicate via chat

As a Team Member

  1. Register or login
  2. Browse public games or use an invite link
  3. Join a game by creating a team (3-5 members) or joining an existing team
  4. During the game, use the "Play" interface to:
    • View current clue
    • See your position on the map
    • Submit photo proof
    • Chat with other teams and the Game Master

As a Spectator

  1. Navigate to any public live game
  2. Click "Spectate" to view:
    • Live map with all team positions
    • Leaderboard showing progress

Project Structure

TreasureTrails/
├── docker-compose.yml              # Docker Compose configuration
├── README.md                       # This file
├── application_requirements.md      # Original requirements
├── backend/
│   ├── Dockerfile                  # Backend container
│   ├── package.json
│   ├── tsconfig.json
│   ├── prisma/
│   │   └── schema.prisma          # Database schema
│   └── src/
│       ├── index.ts               # Server entry point
│       ├── middleware/
│       │   └── auth.ts            # JWT authentication
│       ├── routes/
│       │   ├── auth.ts            # Registration, login, me
│       │   ├── games.ts           # Game CRUD, publish, archive
│       │   ├── routes.ts          # Route and leg management
│       │   ├── teams.ts           # Team CRUD, advance, deduct
│       │   ├── upload.ts          # Photo uploads
│       │   └── users.ts           # User profile, settings
│       └── socket/
│           └── index.ts           # Socket.io handlers
├── frontend/
│   ├── Dockerfile                 # Frontend container
│   ├── nginx.conf                # Nginx configuration
│   ├── index.html
│   ├── package.json
│   ├── tsconfig.json
│   ├── vite.config.ts
│   └── src/
│       ├── main.ts                # App entry point
│       ├── App.vue                # Root component
│       ├── style.css              # Global styles
│       ├── components/
│       │   ├── Modal.vue          # Reusable modal component
│       │   └── NavBar.vue         # Navigation bar
│       ├── composables/
│       │   └── useModal.ts        # Modal alert/confirm composable
│       ├── pages/
│       │   ├── HomePage.vue       # Landing page
│       │   ├── LoginPage.vue      # Login form
│       │   ├── RegisterPage.vue   # Registration form
│       │   ├── DashboardPage.vue  # User's games list
│       │   ├── CreateGamePage.vue # Game creation wizard
│       │   ├── GamePage.vue      # Game details & management
│       │   ├── EditGamePage.vue  # Route/leg editor
│       │   ├── JoinGamePage.vue  # Team creation/joining
│       │   ├── PlayGamePage.vue  # Team gameplay interface
│       │   ├── GameLivePage.vue  # Game Master dashboard
│       │   ├── SpectateGamePage.vue # Public spectator view
│       │   ├── InvitePage.vue   # Invite link landing
│       │   └── SettingsPage.vue  # User profile settings
│       ├── router/
│       │   └── index.ts           # Vue Router configuration
│       ├── services/
│       │   └── api.ts             # Axios API service
│       ├── stores/
│       │   └── auth.ts            # Pinia auth store
│       ├── types/
│       │   └── index.ts           # TypeScript interfaces
│       └── utils/
│           └── units.ts           # Distance conversion utilities

API Endpoints

Authentication

Method Endpoint Description
POST /api/auth/register Create new account
POST /api/auth/login Login to account
GET /api/auth/me Get current user info

Users

Method Endpoint Description
GET /api/users/me Get user profile
PUT /api/users/me Update profile (name, screenName, avatar, unitPreference)
GET /api/users/me/location-history Get location tracking history
GET /api/users/me/games Get user's game participation history
DELETE /api/users/me/location-data Delete all location history
DELETE /api/users/me/account Delete user account

Games

Method Endpoint Description
GET /api/games List public games
GET /api/games/my-games List games created by user
POST /api/games Create new game
GET /api/games/:id Get game details
PUT /api/games/:id Update game settings
DELETE /api/games/:id Delete game (draft only)
POST /api/games/:id/publish Publish game (make live)
POST /api/games/:id/end End a live game
POST /api/games/:id/archive Archive ended game
POST /api/games/:id/unarchive Unarchive game
GET /api/games/:id/invite Get game invite code
GET /api/games/invite/:code Get game by invite code

Routes

Method Endpoint Description
GET /api/routes/game/:gameId Get routes for a game
GET /api/routes/:routeId Get route details
POST /api/routes Create new route
PUT /api/routes/:routeId Update route
DELETE /api/routes/:routeId Delete route
POST /api/routes/:routeId/copy Copy route
POST /api/routes/:routeId/legs Add leg to route
PUT /api/routes/:routeId/legs/:legId Update leg
DELETE /api/routes/:routeId/legs/:legId Delete leg
POST /api/routes/:routeId/legs/:legId/photo Submit photo for leg

Teams

Method Endpoint Description
GET /api/teams/game/:gameId List teams in game
GET /api/teams/:teamId Get team details
POST /api/teams/game/:gameId Create team
POST /api/teams/:teamId/join Join team
POST /api/teams/:teamId/leave Leave team
POST /api/teams/:teamId/assign-route Assign route to team
POST /api/teams/:teamId/advance Advance team to next leg
POST /api/teams/:teamId/deduct Apply time penalty
POST /api/teams/:teamId/disqualify Disqualify team
POST /api/teams/:teamId/location Update team location

Upload

Method Endpoint Description
POST /api/upload/upload Upload photo file

Socket Events

Client → Server

Event Payload Description
join-game gameId Join a game's socket room
leave-game gameId Leave a game's socket room
team-location { gameId, teamId, lat, lng } Broadcast team position
chat-message { gameId, teamId?, isDirect?, message, userId, userName } Send chat message
team-advanced { gameId, teamId } Notify team advancement

Server → Client

Event Payload Description
team-location { teamId, lat, lng } Team position update
chat-message { id, teamId?, isDirect, userId, userName, message, sentAt } Chat message (filtered by direct)
team-advanced { teamId } Team advanced to next leg

Environment Variables

Backend (.env)

DATABASE_URL="postgresql://..."
JWT_SECRET="your-secret-key"
PORT=3001

License

MIT