Real-time audience Q&A and voting platform for workshops, conferences, classrooms, and town halls.
LiveQ&A is a real-time audience engagement platform inspired by Pigeonhole Live. Hosts create a Q&A session and share a QR code or session code with their audience. Participants submit questions, upvote the ones they care about most, and the best questions rise to the top — all in real time.
| Feature | Description |
|---|---|
| QR Code Join | Participants scan a QR code to instantly join a session |
| Real-Time Sync | Questions, votes, and answers update live via Firebase |
| Upvoting | Audience votes surface the most popular questions |
| Host Dashboard | Answer, pin, spotlight, and moderate questions |
| Anonymous Auth | No sign-up required — Firebase anonymous auth handles identity |
| Dark Mode | System-aware dark/light theme toggle |
| CSV Export | Export all questions and answers for post-event review |
| Duplicate Detection | Prevents similar questions via word-similarity matching |
| Mobile-First | Participant view optimized for phone screens |
| Category | Technology |
|---|---|
| Frontend | HTML5, CSS3, Vanilla JavaScript |
| Backend | Firebase Cloud Firestore (real-time NoSQL) |
| Auth | Firebase Anonymous Authentication |
| QR Code | qrcode-generator (CDN) |
| Hosting | GitHub Pages via GitHub Actions |
| CI/CD | GitHub Actions with secret injection |
┌─────────────────────────────────────────────────┐
│ GitHub Pages │
│ (Static Hosting + CDN) │
└──────────────────────┬──────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
index.html host.html participant.html
(Landing) (Dashboard) (Mobile Q&A)
│ │ │
└──────────────┼──────────────┘
▼
┌────────────────┐
│ firebase- │
│ config.js │
│ (SDK init) │
└───────┬────────┘
▼
┌─────────────────────────┐
│ Firebase Services │
├─────────────────────────┤
│ Cloud Firestore │
│ ├── sessions/ │
│ │ ├── questions/ │
│ │ │ └── votes/ │
│ Anonymous Auth │
└─────────────────────────┘
live-qna/
├── index.html # Landing page — create or join session
├── host.html # Host dashboard with moderation tools
├── participant.html # Mobile-friendly participant view
├── css/
│ └── style.css # Full styling with dark mode support
├── js/
│ ├── firebase-config.js # Firebase initialization (secrets injected at deploy)
│ ├── utils.js # Shared helpers (QR, toast, time, CSV, similarity)
│ ├── app.js # Landing page logic (create/join sessions)
│ ├── host.js # Host dashboard (moderation, answers, export)
│ └── participant.js # Participant view (submit, vote, real-time)
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions — builds & deploys to Pages
├── firebase.json # Firebase Hosting config
├── firestore.rules # Firestore security rules
├── firestore.indexes.json # Firestore index config
└── .gitignore
- A Firebase project with:
- Anonymous Authentication enabled
- Cloud Firestore database created
- GitHub CLI (optional, for setting secrets)
-
Clone the repository
git clone https://github.com/alfredang/live-qna.git cd live-qna -
Add your Firebase config
Edit
js/firebase-config.jsand replace the__FIREBASE_*__placeholders with your actual Firebase config values. -
Open in browser
open index.html
Or use any local server (e.g.,
npx serve .).
Set these secrets in your GitHub repo under Settings → Secrets and variables → Actions:
| Secret | Value |
|---|---|
FIREBASE_API_KEY |
Your Firebase API key |
FIREBASE_AUTH_DOMAIN |
your-project.firebaseapp.com |
FIREBASE_PROJECT_ID |
Your Firebase project ID |
FIREBASE_STORAGE_BUCKET |
your-project.firebasestorage.app |
FIREBASE_MESSAGING_SENDER_ID |
Your sender ID |
FIREBASE_APP_ID |
Your app ID |
FIREBASE_MEASUREMENT_ID |
Your measurement ID |
The GitHub Actions workflow injects these at deploy time so secrets never appear in source code.
Push to main and the GitHub Actions workflow handles everything:
- Checks out code
- Injects Firebase secrets from GitHub environment
- Deploys to GitHub Pages
npm install -g firebase-tools
firebase login
firebase init hosting
firebase deploysessions/{sessionId}
├── title (string)
├── code (string, 6-char unique)
├── hostId (string, Firebase UID)
├── isActive (boolean)
├── acceptingQuestions (boolean)
├── createdAt (timestamp)
├── participantCount (number)
│
└── questions/{questionId}
├── text (string)
├── participantName (string)
├── participantId (string)
├── createdAt (timestamp)
├── voteCount (number)
├── status (string: open | answered)
├── isPinned (boolean)
├── isCurrentlyAnswering (boolean)
├── answer (string)
│
└── votes/{participantId}
├── voted (boolean)
└── timestamp (timestamp)
Contributions are welcome!
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Dr. Alfred Ang
- Firebase — Real-time backend
- qrcode-generator — QR code generation
- Pigeonhole Live — Inspiration
If you found this useful, please give it a ⭐
