Leaderboard overview

Ranking players is easy for a week and awkward forever after. The weekly board resets and last week is gone. The all-time board is one query that gets slower every month. And the whole thing lives inside a platform that will hand you a CSV if you ask nicely.

@pithy-sh/leaderboard is boards and windows in your own D1. Daily, weekly, calendar-month, calendar-year, all-time — and a board’s window is a cron expression rather than a fixed list, so a season that runs the first Monday of each quarter is a string rather than a feature request.

Closed windows stay yours

A window that closes does not disappear. It stays in your database for as long as you ask, in plain SQL you can join against your own tables.

That is the difference worth paying attention to. Who was in the top ten last March is a query rather than an export you should have taken at the time, and did the players who topped the March board renew is a join against your subscriptions rather than two spreadsheets and an afternoon.

Rank is computed, and you choose how

ModeWhat it costs
live (default)Counted per request. Always correct, and free under roughly ten thousand players per board — quadratic past that
{ materialize: "<cron>" }Recomputed on a schedule. Constant per request, and as stale as the cadence you chose

The default is the honest one for most games, and the threshold is stated rather than discovered: a board with a few thousand players costs nothing and is never wrong. When it stops being free, the fix is one config key rather than a rewrite.

Writes are server-authoritative by default

Posting a score requires a scope, which you mint for your own server-side code. That inverts the usual vendor norm, deliberately — every platform that offers server-authoritative writes makes them opt-in, and every game that skipped the option has a top ten full of impossible numbers.

If your scoring genuinely happens on the client and you accept that, turn it off knowingly. The default is the one you want on the day somebody looks at your board.

What it deliberately does not do

It does not rate players. A leaderboard ranks what has happened; rating estimates what will. A high score is an achievement, and a skill rating is a prediction — conflating them gives you a ladder where the person with the most free time is the best player.

It has no social layer. No friends list, no challenges, no comments. Friends are matchmaking’s graph, and the rest is your product.

It does not moderate names. What a player is called on the board comes from your own tables, and whether that is acceptable is your policy.

It does not replace Game Center or Play Games. Those give you platform-native UI, achievements and the social graph a player already has. This gives you a board you own, that works identically on web, that you can query, and that does not go away when a platform changes its terms. Running both is a reasonable answer.

When you would reach for it

A game with scores. A product with a streak or a total worth showing. Anything where where do I stand is a question your users ask, and where did they stand in March is a question you will ask.

Not for internal analytics — that is a query against your own tables, and a leaderboard is a player-facing artifact with a window and a rank in it.

What it needs

No required peers.

auth is optional and effectively required: a score belongs to a player, and server-authoritative writes need a scope on a session.

Pairs naturally with rating — two numbers that do two jobs — and with multiplayer, whose durable results are the obvious thing to publish.

ESC