Game Verdict

A crowdsourced answer to an argument PC gamers have been having forever: is this game better with a controller, or with a keyboard and mouse?

2,334
Verdicts cast
Aug 2026 snapshot
1,580
Games tracked
Aug 2026 snapshot
822
Commits
GitHub API, Aug 2026
50 KB
Per browse page
was 1.8 MB
The problem

Everyone has an opinion, nobody has data

Steam will tell you a game has “full controller support.” It won’t tell you whether anyone actually prefers playing it that way. Game Verdict asks players directly, dedupes anonymous votes with a browser fingerprint, and surfaces the leading input method as a community badge.

Games are searched across Steam and IGDB in parallel, deduped and relevance-ranked, with metadata, cover art, and Steam library hero images pulled in automatically when a game is added.

A Game Verdict game page for The Binding of Isaac: Rebirth, showing cover art, a green Controller verdict badge, developer, release year, and genre tags.
A game page: verdict badge, metadata pulled from Steam and IGDB
The core loop

Free to vote, hard to fake

The whole thing only works if voting is nearly free, and it only means anything if the votes can be trusted. Those pull against each other, and almost every decision in the app is an answer to that tension.

Quick Vote takes the friction out: no account, one game at a time, skip anything you don’t recognize. A browser fingerprint keeps it honest without putting the friction back, counting each voter once whether or not they ever sign up. Sign in later and the votes you already cast are claimed rather than lost.

Game Verdict's Quick Vote screen: a queue counter, a session counter, and a single large game card showing Counter-Strike 2.
Quick Vote: one game at a time, no account required

A quarter of the traffic is on a phone, and the one-card-at-a-time shape was built for exactly that. Nothing about the loop changes on a small screen.

Quick Vote on a phone: the same single-card layout, sized to a narrow screen.
The same loop, thumb-sized
How do you count a vote from someone who refuses to identify themselves, without letting them vote a hundred times?
The design constraint

You can also vote from the game page itself, and the breakdown updates in place rather than sending you somewhere to see what you just did.

The verdict casting card on a game page, offering Keyboard & Mouse, Controller, Both, and a smaller Other option.
Casting a verdict without leaving the page
The results card showing a proportional tricolor bar with percentages for keyboard and mouse, controller, and both.
The breakdown, right after you vote
A small thing I like

The queue knows what you have played

Asking someone to judge a control scheme only works if they have actually played the game, so the queue is ordered by Steam review count rather than by how many verdicts a game already has. Review count is a proxy for “have you actually played this?”. Verdict count would surface the games that are already well answered, which is backwards.

On top of that it subtracts what you have voted on, skipped, watched, and owned — all resolved in a single parallel round trip — then promotes your own unplayed library games to the front while keeping them in popularity order. A stable partition, not a re-sort.

The fingerprint does double duty here. Because the exclusion set is fingerprint-aware, an anonymous voter’s queue shrinks as they vote. The queue gets personal for someone who never made an account.

StateQueue order
Signed in, library syncedYour unplayed games, then popular ones
Signed in, no libraryPopular games, minus voted and skipped
AnonymousPopular games, minus what your fingerprint already voted
Quick Vote with a synced Steam library: the top card is TUNIC, tagged 'In your library', with a queue of 378 and 535 skipped.
Library synced: your own games surface first, tagged in place
One sentence, two audiences

Prose generated from data, not from a model

Every game page ends with a plain-language verdict: how many people voted, which way they leaned, and which controller they tend to use. It’s written by a buildVerdictSummary() function, not by a language model. Four branches on the leading choice, an extra clause when the top two land within ten points of each other, and a controller-subtype sentence that only appears once at least two people have reported one.

The Verdict card for Cyberpunk 2077, reading: based on 28 community verdicts, Cyberpunk 2077 works well either way, 53% of players say both inputs feel good, 11% lean controller, and 36% lean keyboard and mouse. Among controller players, Xbox is the most common setup.
Deterministic prose: same data in, same sentence out

The same string is also emitted as the FAQPage answer in structured data. So roughly 1,600 pages that would otherwise be near-identical templates each carry unique text answering the literal thing people search for, and the reader and the crawler are served by one sentence rather than two systems.

No latency, no per-call cost, no hallucinated percentages, and the same input always produces the same output. A model would have done this worse, slower, and for money.
Why not an LLM
Then scale arrived

The bug that froze the front page

Home page stats stopped moving. Not wrong — frozen, at a number that looked perfectly plausible. The cause was that getAllGames() ran an unbounded SELECT * against the verdicts table, and PostgREST silently caps responses at 1,000 rows. Everything worked exactly right until the site crossed its thousandth verdict, and then it quietly stopped counting.

The failure mode was not an error. It was a number that stayed believable while it stopped being true.
Why it took a while to spot

The fix was a game_vote_summary aggregate view doing the counting in Postgres. Which raised the more interesting question: why was the browse page shipping the entire games table to the client at all?

What it cost

1.8 MB → 50 KB per pageview

Browse was slicing a full table read. It became a games_browse Postgres view with filtering, sorting, and counting pushed into SQL behind LIMIT/OFFSET, fed by a route handler driving infinite scroll. A 24-row page costs about 50 KB. The old one cost about 1.8 MB — every single pageview, and worst during crawler bursts, when concurrent requests each independently pulled the whole list.

Game Verdict's browse page with platform and sort filters applied, showing a grid of games with verdict badges and vote counts.
Browse: filtering, sorting and counting all happen in SQL
Beyond the vote

Reasons to come back, and somewhere else to be

Twenty badges across six categories are evaluated after every verdict, reaction, and library change, with the first unlock arriving as a toast. Profiles are public, with a verdict history and an input-method tendency bar you can click to filter.

A public Game Verdict profile showing a shelf of earned achievement badges.
The badge shelf on a public profile

The Discord bot is the part that leaves the site entirely. Five slash commands run on a Cloudflare Worker that verifies Discord’s Ed25519 signatures, returning rich embeds with cover art, the verdict color, and the bar breakdown, so an argument in a group chat can be settled without anyone opening a browser.

A Discord embed from the Game Verdict bot showing the verdict for Hades II, with cover art thumbnail and a vote breakdown.
/verdict in Discord
A Discord embed comparing two games side by side with their vote breakdowns.
/compare, side by side

And the home page keeps the most contested games up front, because a game the community cannot agree on is a better invitation to vote than one that is already settled.

The Most Contested section of the Game Verdict home page, showing Steam hero art with the game logo composited on top, a tricolor vote bar, and inline vote buttons.
Most Contested, with Steam hero art and the logo composited on top
The Most Contested card on a phone, with the hero art, vote bar and buttons stacked.
The same card, narrow
Game Verdict's compare view: Counter-Strike 2 against ELDEN RING, with mirrored bars showing 97 percent keyboard and mouse against 80 percent controller.
Compare: Counter-Strike 2 against ELDEN RING
The part nobody asked for

An easter egg you get to keep

Type ↑ ↑ ↓ ↓ ← → ← → B A and hit Enter or Space. Keyboards, mice, and controllers rain down the page, which is a nice five seconds and then it’s over.

Game Verdict's Quick Vote page with keyboard, mouse and controller icons falling down the screen after entering the Konami code.
The icons that fall are the three things you are voting between

What’s actually interesting is what survives the five seconds. The code unlocks CRT mode, and CRT mode isn’t a moment: it’s a real setting, saved to your profile and hydrated on the server, that you can turn back on whenever you like. Three fixed layers do the work, all of them pointer-events: none so nothing underneath stops being clickable.

The browse page with CRT mode toggled on: scanlines, a vignette, and a subtle red and blue channel shift over the game grid.
Browse, with and without CRT. Flip the production state to compare

It also lives in Settings, next to the Steam library import, which is where an easter egg stops being a joke and becomes a feature somebody might actually prefer.

Game Verdict's settings page showing the Steam library import panel and the CRT mode toggle.
Settings: Steam import, and CRT sitting there like it always belonged

There’s a second code. Finish the sequence, tap Tab for Select, then hit Enter, and you get the Contra two-player variant. It greets you differently, keeps greeting you on every entry rather than only the first, and offers the most secret badge on the site.

The first-time Konami dialog, titled with the arrow sequence, reading: the debate is settled.
1P: the debate is settled
The two-player Konami dialog, titled with the sequence plus Select and Start, reading: 30 lives. Player 2 has entered the game. You remembered the Contra 2-player code. Respect.
2P: 30 lives, and the most secret badge on the site

All of it works signed out. The rain falls, the CRT turns on, and the badge waits until you feel like claiming it. Which is the same idea as the voting, arrived at from a completely different direction: the good part should never be behind the account.

The code works here too. This page has been listening the whole time.
Try it on this page