Slidev - Live Audience Polling for Presentations

Github user asm0dey has published slidev-polls, a self-hostable live audience polling system built around a Java 25 backend and a companion Slidev addon. The mechanic is simple: drop a <PollResults> Vue component on a slide, and when the presenter advances to that slide, the matching question opens for voting automatically. Audience members join from a phone or laptop with no install, and the tally animates live, on the slide in the deck's theme as ballots arrive.

Slidev is a markdown-driven slide framework that conference speakers might reach for when a talk has more code than bullets. It sits in a small family of developer-oriented slide tools alongside reveal.js and Marp, all of which trade Keynote-style drag-and-drop for plain text that diffs cleanly and lives next to the demo repo. None of them ships a built-in way to poll the audience; that gap has left presenters reaching for things like Poll Everywhere, Slido, or Mentimeter and accepting whatever flavor of SaaS lock-in came with the pick. slidev-polls puts the whole apparatus on one container.

The backend stack

The service runs on Java 25 and Spring Boot 4, with jOOQ for type-safe SQL, Flyway for migrations, and Server-Sent Events to fan a live tally out to every connected viewer. The build produces a single fat-JAR; the voter page and administrator pages are bundled in as static assets and served from the same port as the API. There is no separate web server or reverse proxy required to put a single talk on the air.

The storage layer does some neat stuff with Flyway. PostgreSQL is the production default; H2 in file mode is the single-container path for a self-hosted one-off. They share one Flyway migration set keyed on the {vendor} placeholder, and jOOQ's dialect is auto-detected from the JDBC URL. The V9 migration referenced in the changelog rewrites the schema explicitly for cross-engine portability. The Postgres-only polls.allowed_origins text[] becomes a poll_allowed_origins child table, and the partial unique on poll_questions(status='ACTIVE') together with the functional unique on lower(polls.slug) are reworked as generated columns so the same DDL holds against H2.

Auth and the deck contract

Auth splits across three mechanisms: presenters use a Spring Session cookie; voters get a separate HttpOnly SP_VOTER cookie, which is what lets a refresh mid-talk avoid losing the ballot, and the Slidev addon authenticates over a short-lived bearer token, minted by a button in the Slidev toolbar that exchanges the presenter's admin credentials for a deck token. The admin UI lists every token issued for a poll and revokes any of them on demand, which logs the relevant deck out immediately.

The per-poll CORS allowlist is worth borrowing; a presenter restricts which website origins are allowed to talk to a given poll, so a leaked or forked copy of the deck served from a different domain cannot drive the live tally. The same admin UI previews the QR audience members will scan and the join URL they will see, with one-click copy.

Deployment

Deployment is via conventional Docker Compose. The PostgreSQL stack is two services and an env file; the H2 stack is one service and one volume, with the database file at /data/polls.mv.db. The first-run admin wizard creates the initial presenter account from the browser, no SQL or env vars involved.

Frontends are Vue 3 plus Vite in a bun workspace with four packages: a shared DTO, api-client, and sse-client module; the voter SPA at /; the admin SPA at /admin/; and the Slidev addon published to npm as @slidev-polls/component. Tests run vitest on the frontend side and JUnit plus Testcontainers on the backend side, so the jOOQ integration tests exercise a real Postgres rather than a mock dialect.

It's licensed under GPL-3.0.

What is and is not Slidev-bound

The addon is Slidev-specific by design. It is a Vue component packaged for Slidev's addons: frontmatter, and it relies on Slidev's runtime to know which question to open when the deck advances. The backend is just a Spring Boot service with a documented HTTP and SSE surface; nothing about it presumes Slidev. A reveal.js plugin, an mdx-deck component, or even a Google Slides overlay could speak the same protocol with no changes on the Java side. The repo already includes a specs/003-google-slides-overlay design document, which suggests asm0dey is thinking the same way. Potentially, someone (maybe an AI!) could write a fifty-line reveal.js plugin against the existing backend and ship a parallel addon overnight.

This is good, community-friendly design.

Provenance

The README states plainly that the project was written with Claude and that every line and bug is the author's, with the Java side reviewed line by line and the frontends owned but less thoroughly inspected. That sounds like a wise design methodology, leveraging the tooling to do what it's good at (implementation) while a human mind keeps the shape and intent on course.

Even readers who will never give a Slidev talk should find the parallel migrations on PostgreSQL, the SSE tally hub, the {vendor}-keyed Flyway layout, the dialect-detected jOOQ wiring, and the dynamic per-poll CORS allowlist worth a read in isolation.

I don't know if this project is inspiring, as such, but it's a good example worth learning from.

Comments (0)

Sign in to comment

No comments yet.