A live, public-opinion experiment for LLMs. Pick one to kiss, one to marry, one to kill. Watch the rankings change in real time. Monthly snapshots get frozen forever in the archive, with awards.
The digital descendant of a physical voting board from a summit, designed to feel less like a benchmark dashboard and more like a yearbook page the entire internet is signing.
npm install
npm run dev
Open http://localhost:3000. You’ll see the full UI with seeded fake votes and reactions. The site runs in demo mode — votes, reactions, and even past months are stored in memory and reset whenever the server restarts. Perfect for iterating on design.
supabase/schema.sql then supabase/seed.sql..env.local.example to .env.local and fill in:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY (server-only, never expose)CRON_SECRET (any long random string; protects the snapshot endpoint)npm run dev. The demo-mode banner disappears and writes hit Postgres.next/ogGET /api/share/og?kiss=grok&marry=claude&kill=copilotreason_reactions with a unique constraintsnapshot_month()) and the in-memory fallback (src/lib/awards.ts):
GET /api/award/og?key=most_married&model=claude&month=2026-05/embed?cat=marry, ?cat=kill&limit=5, ?theme=lightGET /api/public/leaderboard — current month’s totalsGET /api/public/archive — every frozen month + awardsGET /api/public/models — the LLM rostersrc/lib/easterEggs.ts to add moreAt 00:05 UTC on the 1st of each month, call:
POST /api/cron/snapshot
Authorization: Bearer <CRON_SECRET>
This invokes the snapshot_month() Postgres function which freezes the previous month’s leaderboard and awards into monthly_snapshots.
Easiest hosts to wire this up on:
vercel.json:
{ "crons": [{ "path": "/api/cron/snapshot", "schedule": "5 0 1 * *" }] }
src/
app/
api/
vote/ POST a vote
leaderboard/ GET current month leaderboard
reasons/ GET reasons feed (sort + category)
reactions/ POST toggle a reaction
archive/ GET frozen monthly snapshots
share/og/ GET personalised Vote Card image
award/og/ GET monthly award badge image
cron/snapshot/ POST (cron-only) freeze previous month
public/leaderboard/ GET (CORS) public read-only
public/archive/ GET (CORS)
public/models/ GET (CORS)
embed/ iframe-friendly leaderboard widget
page.tsx the whole site
layout.tsx
globals.css
components/ all UI (Hero, VoteForm, ShareCard, Leaderboards, ReasonWall,
Archive (with awards), EmbedAndApi, Tribute, NavBar,
Footer, Toast, MuteToggle, LiveTicker, CountUp, BoardProvider)
lib/
models.ts the LLM catalog (edit here to add/retire)
awards.ts award computation (mirrors the SQL function)
storage/ pluggable backend (Supabase OR in-memory demo)
voteToken.ts cookie-based voter identity + IP hashing
rateLimit.ts in-process IP rate limit
sound.ts synthesised UI sounds + mute persistence
confetti.ts self-contained canvas confetti burst
easterEggs.ts combo → toast rules
publicApi.ts CORS / cache helpers for the public API
month.ts UTC month helpers
types.ts shared types
supabase/
schema.sql tables + RLS + snapshot_month() + reactions
seed.sql model catalog seed (idempotent)
This ships with the casual anti-double-vote tier:
localStorage flag mirrored client-side(voter_token, month) constraint in PostgresDetermined users can still cheat by clearing cookies + switching networks. To upgrade to strict (require sign-in), wire Supabase Auth and use the auth user id as voter_token. The schema already accommodates this.
src/lib/models.ts.supabase/seed.sql (it upserts by slug).The bottom of the page shows three placeholder photos for the original summit board. To swap in the real ones:
public/origin/ (e.g. board.jpg).src/components/Tribute.tsx and replace each <PlaceholderPhoto /> with <img src="/origin/...jpg" />.