Play. Vote. Learn.
QuizTown is a real-time interactive quiz platform designed for conferences, companies, and schools. Engage your audience with live quizzes, real-time voting, and dynamic leaderboards.
- Instant Join -- Scan a QR code or enter a short code. No account needed.
- Live Quizzes -- Real-time question sync, countdown timer, instant feedback.
- Leaderboard -- Animated rankings based on speed + accuracy.
- Host Control Deck -- Start, pause, skip questions. Monitor participation live.
- Public Screen -- Projection-optimized 16:9 display with giant text and vote bars.
- Quiz Studio -- Create and manage quizzes with multiple-choice questions and GIFs.
- GIF Search -- Built-in GIPHY search to add animated GIFs to questions (Kahoot-style).
- Multilingual -- French and English support.
- Dark Mode -- Automatic dark mode support via system preference.
- Demo Mode -- Try the full experience without Firebase (BroadcastChannel sync between tabs).
- PWA -- Installable on mobile devices.
| Layer | Technology |
|---|---|
| Frontend | Astro 5, React 19 (Islands) |
| Styling | Tailwind CSS v4, CSS Variables |
| Animations | Framer Motion |
| Auth | Firebase Auth (Google SSO) |
| Database | Cloud Firestore |
| Live Engine | Firebase Realtime Database |
| Hosting | Firebase Hosting |
| Tests | Vitest, React Testing Library |
| E2E Tests | Playwright (Chromium) |
| CI/CD | GitHub Actions |
| Language | TypeScript (strict) |
- Node.js 20+ (recommended: 22.x)
- npm 10+
- Firebase CLI (
npm install -g firebase-tools) - A Firebase project with Auth, Firestore, and Realtime Database enabled
# Clone the repository
git clone <repo-url>
cd quiztown
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Fill in your Firebase config in .env
# Start development server
npm run devThe dev server starts at http://localhost:4321.
| Command | Description |
|---|---|
npm run dev |
Start dev server (port 4321) |
npm run build |
Build for production |
npm run preview |
Preview production build |
npm test |
Run all tests |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Run tests with coverage report |
Copy .env.example to .env and fill in your values:
# Firebase (required) -- Single JSON string on one line (recommended)
PUBLIC_FIREBASE_CONFIG={"apiKey":"...","authDomain":"...","projectId":"...","databaseURL":"...","storageBucket":"...","messagingSenderId":"...","appId":"..."}
# Or use individual keys instead:
# PUBLIC_FIREBASE_API_KEY=your-api-key
# PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
# PUBLIC_FIREBASE_PROJECT_ID=your-project-id
# PUBLIC_FIREBASE_DATABASE_URL=https://your-project-default-rtdb.europe-west1.firebasedatabase.app
# GIPHY (optional -- enables GIF search in quiz editor)
PUBLIC_GIPHY_API_KEY=your-giphy-api-key
Important: The
PUBLIC_FIREBASE_CONFIGvalue must be valid JSON on a single line (no quotes around the value, no multiline). Copy the config object directly from the Firebase Console.
Database URL: If your Realtime Database is in a non-default region (e.g.,
europe-west1), the URL format ishttps://<db-name>.europe-west1.firebasedatabase.app(notfirebaseio.com).
GIPHY API key: Create a free account at developers.giphy.com, then create an app to get an API key. The free tier is sufficient (no credit card needed). If omitted, the GIF picker will still open but search results will be empty.
Go to Firebase Console and create a new project.
In the Firebase Console, go to Project Settings > General > Your apps and click the Web icon (</>). Register your app (e.g., "QuizTown Web") -- this gives you the Firebase SDK config needed for the .env file.
- Authentication: Go to Authentication > Sign-in method, enable Google provider
- Cloud Firestore: Create database in your preferred region
- Realtime Database: Run
firebase init databaseto create the database instance, choose your region (e.g.,europe-west1)
# Login to Firebase
firebase login
# Associate your project
firebase use --add
# Select your project from the list# Deploy Firestore + Realtime Database rules
firebase deploy --only firestore:rules,databaseNote:
firebase.jsonincludes bothfirestore.rulesanddatabase.rules.jsonreferences. These are deployed together.
In Firebase Console > Project Settings > General > Your apps > Web app, copy the firebaseConfig object and paste it as a single-line JSON into PUBLIC_FIREBASE_CONFIG in your .env file.
# Build the project
npm run build
# Deploy to Firebase Hosting
firebase deploy --only hostingThe project includes a GitHub Actions workflow (.github/workflows/deploy.yml) that automatically:
- Runs tests on every push and PR
- Builds the project with Firebase config injected from secrets
- Deploys Firebase security rules (Firestore + Realtime Database)
- Deploys to Firebase Hosting on push to
main
Required GitHub Secrets:
| Secret | Description |
|---|---|
FIREBASE_SERVICE_ACCOUNT |
Firebase service account JSON key |
FIREBASE_PROJECT_ID |
Your Firebase project ID (e.g., quiztown-app) |
PUBLIC_FIREBASE_CONFIG |
Firebase SDK config as single-line JSON (same as .env) |
PUBLIC_GIPHY_API_KEY |
(optional) GIPHY API key for GIF search |
src/
├── components/ # Astro components (static)
├── islands/ # React islands (interactive)
│ └── ui/ # Design system components
├── lib/ # External API helpers (GIPHY)
├── firebase/ # Firebase SDK helpers
├── hooks/ # Custom React hooks
├── i18n/ # Translations (FR/EN)
├── layouts/ # Page layouts
├── pages/ # Astro pages (routes)
├── styles/ # Global CSS + design tokens
├── types/ # TypeScript type definitions
└── utils/ # Business logic (scoring, state machine)
tests/
├── hooks/ # Hook tests
├── islands/ # Component tests
└── utils/ # Business logic tests
| Route | Description |
|---|---|
/ |
Landing page (FR) |
/en/ |
Landing page (EN) |
/host |
Host dashboard (auth required) |
/host/create |
Quiz editor (auth required) |
/host/live/:id |
Host control deck (auth required) |
/play/:id |
Player join + game |
/screen/:id |
Public projection screen |
/demo |
Demo player (no Firebase needed) |
/demo/screen |
Demo projection screen (no Firebase) |
Auth guard: All
/host/*pages are protected by anAuthGuardReact island that requires Google sign-in. Unauthenticated users see a login screen.
- See
spec/GENERAL.mdfor brand identity and design system - See
spec/DESIGN.mdfor detailed screen layouts and UX flows - See
spec/TECH.mdfor data models and technical architecture - See
spec/EPIC.mdfor user stories - See
spec/PLAN.mdfor implementation phases and progress - See
spec/NEW_FEATURES.mdfor new features based on user feedbacks.