Real‑time Q&A for meetups, conferences, and workshops. Built with Node.js, Express, Socket.IO, and Tailwind CSS.
This project is designed for small events running on a trusted local network (no external services or database).
npm install
npm startOpen:
- Audience:
http://localhost:3000/ - Display:
http://localhost:3000/display - Admin:
http://localhost:3000/admin
These screenshots were captured from a sample event: StellarJS Summit 2026.
Example questions used:
- “Welcome to StellarJS Summit 2026 — what should we cover first?”
- “How do you scale Socket.IO without sticky sessions (or do you embrace them)?”
- “Can you share a quick checklist for shipping secure webhooks in Node?”
- “What’s your best workflow for moderating duplicates and keeping Q&A moving?”
- “How do you measure end-to-end latency in Node + WebSocket apps (without lying to yourself)?”
- Audience (
/) – attendees submit questions, vote, and react. - Display (
/display) – projector‑friendly view showing the top questions. - Admin (
/admin) – moderation tools and basic analytics.
- Submit questions with an optional name.
- Character limits and basic validation (default 10–500 characters).
- Duplicate detection by text (case‑insensitive).
- One vote per question per browser session.
- One active reaction per question (thumbs up, heart, laugh).
- Local storage for:
- generated session ID,
- remembered display name,
- which questions were voted on,
- current reactions.
- Shows the top 10 questions, always with:
- pinned questions first,
- then by vote count and recency.
- Four themes:
dark,light,blue,purple(controlled from the admin view). - Optional on‑screen timer (3 / 5 / 10 minutes) for structured Q&A segments.
- Keyboard shortcuts:
H– toggle help overlayT– toggle theme selectorSpace– toggle timer controlsF– toggle fullscreen
- Live analytics (via
/api/analytics):- total questions, votes, reactions
- average votes per question
- basic “questions over time” buckets
- approximate unique users (based on vote sessions)
- Moderation tools:
- pin / unpin questions
- mark as answered
- hide / show questions (hidden questions are not shown to audience or display)
- delete a single question
- clear all questions and votes
- Optional approval queue:
- when enabled, new questions go to a pending list
- admin can approve or reject before they appear to the audience.
-
Backend
expressHTTP server serving static HTML frompublic/.socket.iofor all real‑time interactions (questions, votes, reactions, config updates).- In‑memory state only:
questions: live questionspendingQuestions: questions awaiting approvaluserVotes:sessionId → Set<questionId>userReactions:sessionId → Map<questionId, reaction>
- Sorting: pinned questions first, then by vote count, then by creation time.
- Simple analytics endpoint at
GET /api/analytics.
-
Frontend
- Plain HTML + Tailwind via CDN.
- Vanilla JavaScript for Socket.IO integration and UI behavior.
- No build step or front‑end framework.
- Node.js (see
package.jsonengines)
npm installnpm startBy default the server listens on port 3000 on all interfaces. On startup it prints:
- Local URL:
http://localhost:3000 - Network URL:
http://<your-lan-ip>:3000
You can override the port:
PORT=3001 npm startnpm run dev- Open the display view on the projector:
http://<your-lan-ip>:3000/display - Open the admin panel on your device:
http://<your-lan-ip>:3000/admin - Share the audience view:
- URL:
http://<your-lan-ip>:3000/ - or let people scan the QR code shown in the audience UI (fetched from
/api/qrcode).
- URL:
Configuration is defined in server.js:
const config = {
requireApproval: false, // enable/disable approval queue
maxQuestionLength: 500, // maximum characters per question
minQuestionLength: 10, // minimum characters per question
theme: 'dark', // display theme (dark, light, blue, purple)
editTimeLimit: 5 * 60 * 1000 // how long authors can edit their question (ms)
};The admin UI can toggle requireApproval and set the theme at runtime; other values require a restart.
- No authentication:
- anyone on the network can open
/adminand moderate questions. - intended for trusted local networks only.
- anyone on the network can open
- All state is in memory:
- data is lost on server restart,
- there is no persistence or database.
- Basic XSS mitigation:
- user‑supplied text is escaped in the browser via a small helper before insertion into the DOM.
- Vote and reaction limits are per browser session, not per person or device.
If you intend to expose this on the public internet, you should at minimum:
- put the admin view behind authentication (reverse proxy or app‑level),
- add a proper data store,
- harden rate limiting and validation.
MIT. See LICENSE.


