Leaderboards are project-scoped ranked score boards. A board springs into existence the first time a player submits to it, and every player holds at most one entry per board — resubmitting updates that entry according to the board's mode. Boards persist across sessions and servers, and you manage them from the dashboard.

Board names match ^[a-z0-9_.\-]{1,64}$ (lowercase letters, digits, _, ., -).

Submitting scores

await SpawnWeaver.start()

var result := await SpawnWeaver.leaderboard_submit("highscore", 4200)
if result.ok:
    print("Best: ", result.value["best"], "  rank #", result.value["rank"])
    if not result.value["updated"]:
        print("Didn't beat your stored score.")

leaderboard_submit(board, score, mode) resolves with {best, rank, updated}:

Field Meaning
best The score stored for you after the submit
rank Your 1-based rank on the board
updated false when the submit didn't beat your stored score (max/min modes)

The display name set with set_display_name() is stored with your entry, so listings can show names instead of player ids.

Modes

The third argument picks how a resubmit interacts with your stored score:

Mode Behavior Use for
"max" (default) Keep the higher score Highscores, most kills, longest streak
"min" Keep the lower score; ranks read ascending Lap times, speedruns, fewest moves
"latest" Always overwrite Season ratings, ELO-style values you compute

Use the same mode for every submit to a given board — the mode travels with the submit, not the board.

Reading the board

# The top 10, best first (value: Array[SWLeaderboardEntry]):
var top := await SpawnWeaver.leaderboard_top("highscore")
if top.ok:
    for entry in top.value:
        print("#", entry.rank, "  ", entry.display_name(), "  ", entry.score)

# The window around you (great for "you are #17" UIs):
var around := await SpawnWeaver.leaderboard_around("highscore", 2)
if around.ok:
    print("You are #", around.value["player_rank"], " of ", around.value["total"])
    for entry in around.value["entries"]:
        print("#", entry.rank, "  ", entry.display_name(), "  ", entry.score,
            "  ← you" if entry.is_local else "")

leaderboard_top(board, limit) returns up to limit (1–100, default 10) entries. leaderboard_around(board, entry_range) returns your entry ± entry_range (1–50, default 5) neighbors, plus player_rank and the board's total entry count. If you have no entry yet, player_rank is -1 and the top of the board is returned instead.

Each SWLeaderboardEntry carries rank, player_id, player_name, score, updated_at, and an is_local flag that is true on your own row — perfect for highlighting it.

Ranks and ties

  • Ranks are 1-based. Equal scores share a rank — two players at 100 are both #1, and the next score is #3.
  • Within a tie, the oldest entry lists first (defending a score beats matching it).
  • On "min" boards ranks read ascending: the lowest score is #1.

Managing boards from the dashboard

The Leaderboards page in the dashboard (Build → Leaderboards) shows every board of a project with its top 100. From there you can:

  • Delete a single entry — e.g. an obvious cheater. The player can submit again immediately (their next submit re-creates the entry).
  • Reset a whole board — removes every entry, e.g. at the start of a new season. The board re-appears on the next submit.

Limits

Limit Value Error
Board name ^[a-z0-9_.\-]{1,64}$ leaderboard-invalid-board
Score range ±2^53-1 (JSON-safe integer) leaderboard-invalid-score
top page size 1–100 entries (default 10) clamped
around range ±1–50 entries (default 5) clamped
Player name 64 characters trimmed to fit

Leaderboard calls share the connection's message budget (20/s) — submit when a run ends, not every time the score ticks up.

Common mistakes

Mistake What happens Fix
Calling before start() not-connected Leaderboards ride the realtime connection
Uppercase or spaced board names ("High Scores") leaderboard-invalid-board Use "high_scores" — lowercase, no spaces
Mixing modes on one board Confusing best-score behavior Pick one mode per board and stick to it
Submitting lap times with "max" The worst time is kept Use "min" for time-based boards
Storing scores in player storage No ranks, not readable by other players Storage is private per player; leaderboards are shared
Submitting every frame Burns the message budget Submit on run end / meaningful changes

Next