Wine Night is a mobile-friendly blind tasting app for small social events. Each couple can bring a bottle and contribute to the pot, while every attendee can taste, vote, and keep private notes from their own phone.
- The host creates a room, enters the wines, and starts the tasting.
- Guests join with the room code and vote using numeric scores, a full ranking, or a top-three ranking.
- The host locks voting and controls a staged results reveal.
During tasting, bottles are identified as Wine 1, Wine 2, and so on. The number entered during setup should match the number on the bottle's physical bag.
Raw numeric averages are not comparable when participants use different scales or scoring habits. Wine Night converts each person's ballot into ranks before combining the results.
- Numeric voters choose their own scale maximum. Equal scores remain tied.
- Full-rank voters order every wine.
- Top-three ballots and incomplete numeric ballots leave the remaining wines tied below the wines that were scored.
- Unranked ties share the average of the remaining Borda positions, keeping the total weight of each ballot equal without inventing preferences.
- A Condorcet winner is recognized when one wine beats every other wine in head-to-head comparisons. Otherwise, the Borda totals determine the order.
Exact unresolved ties are shown as shared places. The close, clear, and decisive result labels are descriptive summaries, not statistical significance tests.
npm install
npm run devThe local server runs at http://localhost:8787.
| Command | Purpose |
|---|---|
npm run dev |
Start the local Cloudflare development server |
npm test |
Run the automated test suite |
npm run typecheck |
Validate Cloudflare types and TypeScript |
npm run cf-typegen |
Regenerate Cloudflare binding types |
npm run deploy |
Deploy or update the workers.dev version |
npm run deploy:domain -- DOMAIN |
Deploy or update with a custom domain |
npm run destroy:dry-run |
Preview deletion of the Cloudflare deployment |
npm run destroy |
Delete the live Cloudflare Worker and its data |
See Cloudflare deployment and operations for initial deployment, custom domains, room backups, restores, monthly downtime, public-hosting safeguards, and permanent teardown.
src/index.ts: HTTP API, static assets, authentication, rate limits, and WebSocket routing.src/archive.ts: versioned host-backup validation and import limits.src/event.ts: room state in a Durable Object with SQLite storage and caller-scoped live updates.src/scoring.ts: rank conversion, Borda and Condorcet calculations, Spearman correlation, and result analytics.public/: dependency-free HTML, CSS, and JavaScript frontend.
Host actions require a random room-specific host key. Each voter receives a separate participant key stored per room and browser tab. Before reveal, a voter can retrieve only their own ballot and private notes. The complete raw-votes table is host-only.
Inactive rooms expire after 90 days. Bottle prices are optional, and price-based analysis is omitted when no usable prices were entered.
- Borda count overview: the positional scoring rule used for the main consensus totals.
- A Borda Count for Partially Ordered Ballots, Cullinan, Hsiao, and Polett: the bucket-averaging treatment used for tied and unranked wines on partial ballots.
- The Borda Class, Terzopoulou and Endriss: an axiomatic analysis of Borda rules for top-truncated preferences.
- Condorcet method overview: the head-to-head criterion used to recognize a wine that beats every other wine pairwise.
- Spearman's rank correlation coefficient: the rank-agreement measure used for palate matching and group alignment, with average ranks for ties.
Wine Night is released under the Zero Clause BSD License (SPDX: 0BSD).
Copyright © 2026 Aaron Bull Schaefer and contributors