Leaderboards

Play this lesson

Who is the best pilot?

Kai scored 48,200 in Hyper Shards. Is that good? With scores kept only on one laptop, there's no way to know.

A leaderboard is a list of the top scores. A global leaderboard puts every player in the world on the same list. To build one, scores must go to a server, and our server is Firestore, Firebase's database.

Sending a score

Firestore keeps data as documents (like cards) inside collections (like folders). When a run ends, the game adds a new card to a collection called scores:

await addDoc(collection(db, "scores"), {
  name: "SwiftComet", score: 48200,
  day: today, uid: user.uid
});

addDoc gives each card a random name, because one player can post many scores. The uid (the player's user ID) lets us check later who sent it.

Asking for the top 10

To show the board, the game asks Firestore to sort the scores, biggest first, and send only the top 10:

const top10 = query(collection(db, "scores"),
  orderBy("score", "desc"), limit(10));
const snap = await getDocs(top10);

desc is short for descending: biggest first. asc, ascending, is smallest first. Firestore sorts on the server, so a phone downloads 10 scores, not millions.

Quick check

Why does the game ask the server for just the top 10 scores?

  1. So the phone doesn't download millions of scores
  2. Because a leaderboard can only hold 10 players
  3. Because sorting only works on 10 numbers
Show the answer

So the phone doesn't download millions of scores

The server sorts everything and sends back only what the screen shows. That keeps the leaderboard fast and saves data on the player's phone.

Bug hunt

leaderboard.js
const top10 = query(  collection(db, "scores"),  orderBy("score", "asc"),  limit(10));

Kai's new board shows the 10 lowest scores instead of the best. Which line is the bug?

  1. Line 2
  2. Line 3
  3. Line 4
Show the answer

Line 3

"asc" means ascending, smallest first. Line 3 should say "desc", so the biggest scores come first and limit(10) keeps the best ten.

The forever board problem

A daily board adds a filter: where("day", "==", today) before the orderBy. Filtering on one field and sorting by another needs a composite index, a ready-sorted list Firestore keeps so the query stays fast.

Read the error

Browser console, Kai's laptop
FirebaseError: The query requires an index. You can create it here: https://console.firebase.google.com/...

Kai's new daily board query fails with this message. What should Kai do?

  1. Open the link and create the composite index the query needs
  2. Delete all of yesterday's scores
  3. Move the leaderboard back to local storage
Show the answer

Open the link and create the composite index the query needs

A query that filters on day and sorts by score needs a composite index. Firestore's error includes a link that sets it up in a few clicks. It takes a few minutes to build, then the query works.

Safe display names

Name check

The Hyper Shards name filter checks every new display name. Which one should it block?

  1. SwiftComet
  2. NovaBlaster
  3. EmmaJones_Room4B
  4. ShardQueen
Show the answer

EmmaJones_Room4B

EmmaJones_Room4B looks like a real full name plus a class, which tells strangers who the player is. The others are fun and private.

But what about 99999?

One big danger is left. What if someone sends a fake score of 99999? They would sit at number 1 forever, and real players would give up.

Stopping that takes Security Rules and code on the server, which come up in the lessons on cheaters and Cloud Functions.

Bolt says: A leaderboard is only fun if players trust it. Fair scores matter more than fancy graphics.

Fact-checked 9 October 2026