Where your score actually is
When you beat your best score on this site, a string of JSON is written into your browser's local storage under a key beginning with `ca:`. That is the entire mechanism. Nothing is transmitted, nothing is queued for upload, and no server learns anything, because there is no server-side application here at all.
Local storage is a key-value store that browsers provide to pages, scoped to the origin that wrote it. Only code running on candyarcade.xyz can read Candy Arcade's keys, and only in the browser where they were written. It persists across page loads and browser restarts, with no expiry.
What a record looks like
The key `ca:best:candy-rush` holds something like:
{"bestScore":18420,"bestCandy":214,"bestCombo":5,"bestTime":186}
That is it. No identifier, no timestamp beyond what the metrics themselves imply, nothing that describes you rather than the run.
Advertising keeps Candy Arcade free. Ads never cover gameplay or controls.
The limits
Local storage is well supported and reliable within its constraints, and the constraints are real.
| Limit | Detail | Consequence for a game |
|---|---|---|
| Capacity | Typically 5-10 MB per origin | Ample for scores; unsuitable for images or replays |
| Synchronous API | Reads and writes block the main thread | Never write during a frame loop |
| Strings only | Values must be strings | Everything goes through JSON.stringify and JSON.parse |
| Per-origin, per-device | Not shared between browsers or machines | No cross-device progress without an account |
| Can throw | Blocked storage or a full quota raises an exception | Every access needs a try/catch |
| Can vanish | Private browsing, cleared site data, some extensions | Records can disappear with no warning |
The synchronous point is the one that bites developers. Writing to local storage blocks the main thread, so writing every frame - or even every pickup - produces stutter. Our engine writes records once, at the end of a run, which is also the only moment the values can be final.
Why every access needs a try/catch
The most common mistake in browser game storage is assuming local storage is available. It is not always, and the failure mode is an exception rather than a null, so an unguarded access crashes the game.
- Some browsers block site data entirely by configuration or policy.
- Private browsing modes may provide storage that throws on write, or that vanishes on close.
- A full quota throws on write.
- Some privacy extensions stub the API in ways that throw on access rather than returning nothing.
- Accessing storage inside a sandboxed frame can throw depending on the sandbox flags.
Our storage module probes availability once at startup by writing and removing a test key inside a try/catch. If that fails, every subsequent operation falls back to an in-memory object. The games still work perfectly for the current session; records simply do not survive it. Game pages then show a notice saying so, because silently losing someone's high score is worse than telling them it will not be kept.
Detect once, fall back gracefully, and tell the player. All three parts matter - the third one is the one almost everyone skips.
What local records cannot do
This is where most browser game sites become dishonest, so it is worth being direct. Local storage cannot support a leaderboard. It cannot compare your score with anyone else's, because no other score is available to compare with. A static site with local storage has no aggregate knowledge of anything.
That means a site with no backend claiming a global high score table is either running a backend it has not disclosed, or making it up. There is no third option.
We take the constraint seriously rather than working around it. Every record on this site is labelled "your local best". Our popular games page ranks by play counts in your own browser and explains that on the page. We do not display global rankings, player counts, or "players worldwide" figures, because we would have to invent them.
Designing around the constraint
Local-only records are a genuine limitation, and they also have real advantages worth designing toward.
- No account friction. Nothing to create, nothing to remember, nothing to lose access to.
- No privacy exposure. There is no personal data to leak because none is collected.
- No cheating problem worth caring about. Anyone can edit their own local storage, and since nobody else can see it, it does not matter.
- Instant play. No network round trip between clicking play and playing.
- Honest comparison. Your only competition is your own previous performance, which is the comparison that actually drives improvement.
That last point is worth taking seriously rather than treating as consolation. Competing against a global leaderboard is demotivating for most players, because the top of it is occupied by people with far more practice. Competing against your own best from last week is achievable, immediate and specific.
What to store, and what not to
A short opinionated list, based on what we ended up with after building thirty games.
- Do store per-game records as a small object keyed by game id, not as dozens of separate keys.
- Do store enough metrics to be interesting: score, items, best combo, distance, time, level.
- Do store play counts, which give you an honest basis for a 'popular' ordering.
- Do store preferences - sound, reduced motion - because they are exactly the kind of thing users expect to persist.
- Do not store anything identifying. There is no reason a game needs it.
- Do not store replays or screenshots. The quota is not for that.
- Do not write during gameplay. Write at the end of a run, once.
- Do namespace your keys. Ours all begin with `ca:` so clearing them is a single filtered pass.
Giving users control
If you store something on a person's device, they should be able to see it and delete it. Both are easy to provide and both are frequently omitted.
Our privacy policy lists every key we write, in full, and includes a button that clears all of them immediately. It also warns that doing so erases high scores permanently and that we cannot restore them, which is true and worth saying before rather than after.
Browsers also provide this independently: clearing site data for a domain removes everything, and developer tools show the exact stored values. Telling users that explicitly is more useful than a vague assurance that their data is safe.
The alternatives, briefly
| Mechanism | Persists | Server sees it | Good for |
|---|---|---|---|
| localStorage | Indefinitely, per origin and device | No | Scores, preferences, small state |
| sessionStorage | Until the tab closes | No | Temporary state you want discarded |
| IndexedDB | Indefinitely, larger quota | No | Large structured data, replays, assets |
| Cookies | Until expiry, sent with every request | Yes | Sessions - and the reason for cookie banners |
| A backend | Indefinitely, centrally | Yes | Leaderboards, cross-device sync, accounts |
For an arcade game's scores, localStorage is the right answer: small, synchronous-but-rare writes, no privacy exposure, no infrastructure. IndexedDB is the right answer the moment you want to store something substantial. A backend is the only answer if you genuinely want leaderboards, and it brings accounts, moderation, cheat prevention and a privacy policy that has considerably more in it than ours does.
Common mistakes
- Assuming localStorage is availableIt can throw rather than return null. Unguarded access crashes the game in private browsing and with some extensions.
- Writing during the frame loopThe API is synchronous and blocks the main thread. Write once at the end of a run.
- Claiming a leaderboard without a backendLocal storage cannot compare your score with anyone else's. A static site showing global rankings is inventing them.
- Failing silently when storage is blockedLosing someone's high score without telling them is worse than telling them it will not be kept.
- Scattering keysDozens of unnamespaced keys are hard to clear and easy to collide with. Namespace everything.
Practical tips
- Your scores are per browser and per device - a different browser on the same machine has its own records.
- Clearing site data erases your high scores permanently; there is no copy anywhere.
- In private browsing, expect records to vanish when you close the window.
- If a game page warns that storage is blocked, your scores will last only for the current session.
- You can inspect exactly what a site has stored in your browser's developer tools, under Application or Storage.
Key takeaways
- Scores are written to localStorage in your own browser and never transmitted anywhere.
- localStorage is not cookies: it is never sent to a server, which is why this site sets no cookies.
- The API is synchronous and can throw, so writes belong at the end of a run and inside a try/catch.
- Local storage cannot support a leaderboard. A static site claiming one is inventing it.
- Local-only records mean no accounts, no privacy exposure and instant play.
- If you store data on someone's device, show them what it is and let them delete it.
Frequently Asked Questions
Where are my Candy Arcade high scores stored?
In your own browser's local storage, on the device you played on, under keys beginning with 'ca:'. Nothing is uploaded and we cannot read them.
Will my scores sync between my phone and my computer?
No. Local storage is per browser and per device. Cross-device progress would require an account and a server, neither of which this site has.
What happens to my scores if I clear my browser data?
They are erased permanently. We hold no copy, because the data never left your device.
Why is there no global leaderboard?
Because a static site with local storage has no way to know anyone else's score. Any site in this position showing global rankings is either running an undisclosed backend or making them up.
Does local storage mean the site is tracking me?
No. Local storage is never transmitted to any server, and what is stored here is game records and preferences with nothing identifying in them.
Read next
- how candy multipliers affect scores — what those records measure
- accessibility in browser arcade games — more on the web platform
- how to design mobile-friendly candy games — the mobile side
- our privacy policy — the full list of what we store
- popular games — local play counts used honestly
References
- Window.localStorage - MDN Web Docs — API reference, including the exceptions it can throw.
- HTML Living Standard: Web storage — The specification defining localStorage and sessionStorage behaviour.
Every external source listed here has been read. We do not cite sources we have not checked, and we do not invent references. See our fact-checking policy.