# Leaderboards and saves

> A leaderboard and saved progress without a server of your own. Submit scores, show the top 50, and keep progress in cloud storage so it follows players to any phone. From the Lia developer docs: https://docs.lialabs.org/guides/scores-and-saves

## A leaderboard

```js title="board.js"
async function gameOver(score) {
  const { best } = await Lia.submitScore(score);
  if (score === best) Lia.haptic("success");
  showBoard(await Lia.leaderboard());
}

function showBoard({ top, me }) {
  list.innerHTML = top
    .map((row) => `<li class="${row.me ? "me" : ""}"><b>${row.rank}</b> @${escapeHtml(row.name)} <span>${row.score}</span></li>`)
    .join("");
  if (me && me.rank > top.length) footer.textContent = `You: #${me.rank} with ${me.score}`;
}
```

Escape names before putting them in HTML, as with any text from people.

Each app has one leaderboard, with each player's best. Scores are whole numbers and higher is better; for a time or "fewest moves", submit something like `1000000 - milliseconds`.

> **Important** `Lia.submitScore` is called by your page, so a player could send any score. Keep it for bragging rights. For prizes, have your server decide the winners from games it saw, and pay them from your [reward pool](/guides/reward-players).

## Saved progress

Cloud storage keeps up to 1024 keys per player in your app on Lia's server, and follows them to any phone.

```js title="save.js"
const SAVE = "save";

async function load() {
  const text = await Lia.cloudStorage.getItem(SAVE);
  return text ? JSON.parse(text) : { level: 1, coins: 0 };
}

let saving = null;
function save(state) {
  // Coalesce saves: at most one write every 2 seconds.
  clearTimeout(saving);
  saving = setTimeout(function () {
    Lia.cloudStorage.setItem(SAVE, JSON.stringify(state)).catch(function () {
      Lia.deviceStorage.setItem(SAVE, JSON.stringify(state)); // keep it on the phone meanwhile
    });
  }, 2000);
}

Lia.onPause(function () { /* save now: they may not come back */ });
```

Values are up to 4096 characters, so keep big things (a level editor's maps, say) on your own server or split them across keys and read them back with `getItems`.

## What goes where

| Keep it in | When |
| --- | --- |
| `Lia.cloudStorage` | Progress, settings and anything the player expects on a new phone |
| `Lia.deviceStorage` | Caches, drafts and anything you can lose (up to 5 MB) |
| `Lia.secureStorage` | A token for your own server (up to 10 items) |
| Your own server | Anything that's worth money or decides prizes, coins bought with $LIA, and what other players see |

Cloud storage is the player's to change from your page, so treat it like anything from the page: fine for their own progress, not for balances. Test and live share it.
