Guide

How to add a leaderboard to an HTML game

One setting in the dashboard and a few lines of JavaScript. No backend, no keys, and each player can only change their own score.

Updated 2026-11-07 · by Hostus Team

A leaderboard usually means a database, an account somewhere and keys in your code. On Hostus your game gets hostus.data, storage with no backend: every player reads the board, and each one writes only their own score. Play Flipdot on your phone to see one running.

The short way: ask your AI

  1. Paste the game prompt into your conversation and add: Add a leaderboard with the top 20 scores. The prompt tells your AI to use the key pattern score@<me.id>.
  2. Publish the game and make the page public.
  3. In the dashboard, open the page's Data tab and set the mode to everyone reads, each writes their own. The dashboard labels it in Portuguese today: Todos leem, cada um escreve o seu. With a connector, your AI can set it for you with hostus_update and data_mode: public_own.

Skip step 3 and the page stays in shared mode, where any player can overwrite any score.

The code

This comes from the games recipe in runtime.md. It uses await at the top, so put it in a <script type="module">.

// public_own: everyone reads, each player writes only their own score.
const me = await hostus.data.me();
async function saveScore(name, points) {
  await hostus.data.set('scores', `score@${me.id}`, { name, points });
}
hostus.data.watch('scores', (items) => {
  const seen = new Set(); // one line per player, valid numbers only
  const top = items.filter((i) => {
    if (!Number.isFinite(i.value?.points) || i.value.points > 1000000 || seen.has(i.author)) return false;
    seen.add(i.author);
    return true;
  }).slice(0, 20);
  renderBoard(top.map((i) => ({ name: String(i.value.name ?? '').slice(0, 20), points: i.value.points })));
}, { orderBy: 'value.points', order: 'desc', limit: 200 });

The board needs something to draw into, like <ol id="board"></ol>:

function renderBoard(rows) {
  // Names come from other players: textContent, never innerHTML.
  document.getElementById('board').replaceChildren(...rows.map((row) => {
    const li = document.createElement('li');
    li.textContent = `${row.name}: ${row.points}`;
    return li;
  }));
}

A few details matter:

  • The key. Keys are unique per page, so each player's score lives at score@ plus their id. A key ending in another player's id is refused.
  • The filter. A record is whatever its author wrote. The code reads more than it shows, keeps one line per player and drops anything that isn't a sane number.
  • Names. They come from other players, so render them with textContent, never innerHTML.
  • Best score. saveScore overwrites the record, so call it only when the player beats their best.

The limits

  • 32 KB per record. Plenty for a name and a score.
  • 200 items per watch. The code asks for the top 200 by score and shows 20.
  • Records per collection: 1,000 on free and 20,000 on paid, and a player can hold up to 500 on free.
  • Updates every few seconds. watch checks every 3 seconds while the game is in use and slows down when nothing changes. Use it instead of your own timer.
  • Writes are rate limited per player and per page. Catch errors and show them, and save at the end of a round, not on every frame.

Anonymous scores can be faked

The score comes from the player's browser, and Hostus can't check it. Anyone who opens the code can send any number. That is fine among friends, and it won't hold up with a prize at stake.

Requiring sign-in to write reduces this but doesn't solve it. An email gate doesn't help either: it doesn't verify the address, so whoever types someone else's email acts as that person. To stop cheating for real, a server has to run the game.

Other ways to keep scores

OptionOther players see itSetup
hostus.dataYesOne setting in the dashboard
localStorageNo: it lives in one browserNone. Good for saving progress
FirebaseYesA project, a database, security rules and the config in your code

Pick localStorage for a personal best nobody else sees. Pick Firebase if your game already uses it.

Good to know before you publish

Every page shows a small Hostus badge, and you pick the corner. The link is yourgame.hostus.page, and you can't point a domain of yours at it. The page does not appear in Google, so people reach it from your link. A new page starts private: making it public is a switch in the dashboard. The free plan covers 20 pages and 2 MB, media included, and Hostus keeps the last 20 versions of each page. If the file has a secret key in it, the upload stops and warns you. A Firebase web config also raises a warning, and it is public by design, so you can go on. Only publish games that are yours.

Questions people ask

Do I need a backend for a leaderboard?

Not on Hostus. hostus.data stores the scores, and your game only needs the few lines above and the right data mode.

Can players overwrite each other's scores?

Not in the mode that lets everyone read and each player write their own (public_own). In the default shared mode they can, so change the mode in the Data tab.

Can someone fake a score?

Yes. The browser sends the score, so a leaderboard open to anonymous players can be faked. Requiring sign-in reduces that but doesn't solve it.

Do players need an account?

No. Hostus gives each anonymous player a stable id on that page. If they clear the browser, they start over.

Does it update live?

Within a few seconds. watch checks for changes and redraws the board. For things that move on screen, use rooms.

Give your game a leaderboard tonight

Free plan, no card, no backend. Your link is ready in about 30 seconds.