// Leaderboard · DOCS
Leaderboard Integration
A drop-in leaderboard for games uploaded to G-Less. No app keys. No build step. No secrets.
How it works
G-Less embeds every uploaded game in an iframe on its detail page. The leaderboard SDK talks to that iframe's parent window via postMessage. The parent page already knows who the signed-in G-Less user is and attaches that identity when forwarding your submission to the scoring API. Your game therefore never touches an auth token, never needs an API key, and never has a secret to leak.
Players who are not signed in can still play your game. Their submissions are politely rejected with error: "not_logged_in", and the parent page shows a low-key "sign in to save your score" prompt.
Install
<script src="https://g-less.com/sdk/leaderboard.js"></script>That's the whole dependency. The script exposes window.GlessLeaderboard. No app ID, no API key, no secret — the parent page hands the game's identity to the SDK during handshake.
Multiple leaderboards (Steam-style)
A single game can own any number of leaderboards, addressed by a string boardKey. The design mirrors Steam's FindOrCreateLeaderboard(name, sortMethod, displayType), with no hard cap on count.
| Steam SDK | G-Less |
|---|---|
| FindOrCreateLeaderboard(name, sort, display) | owner dashboard + auto-create on first submit |
| leaderboard name | boardKey (/^[a-z0-9][a-z0-9_-]{0,31}$/) |
| sortMethod Asc/Desc | sort_direction asc / desc |
| displayType Numeric / TimeSeconds / TimeMilliSeconds | score_format integer / time_ms / float |
| UploadLeaderboardScore(..., KeepBest, ...) | submit(boardKey, score) — always keep-best |
| DownloadLeaderboardEntries(...) | getTop(boardKey, { limit, scope }) |
// Typical setup for one game:
await window.GlessLeaderboard.submit("main", totalScore); // desc / integer
await window.GlessLeaderboard.submit("speedrun", clearMs); // asc / time_ms
await window.GlessLeaderboard.submit("distance", metersTraveled); // desc / floatQuick start
const session = await window.GlessLeaderboard.init();
async function onGameOver(score) {
const res = await window.GlessLeaderboard.submit("main", score);
if (res.error === "not_logged_in") toast("Sign in to save your score");
else if (res.error) console.warn(res.error);
else if (res.hidden) toast("Score saved, held for review");
else toast(`Rank #${res.rank} with ${res.best}!`);
}API
- init() — returns
{ available, isLoggedIn, user, gameId, playStartedAt }. Call once on startup. - submit(boardKey, score, meta?) — returns
{ submitted, best, hidden, rank, board }or{ error }. Never throws. - getTop(boardKey, {limit, scope}) — scope can be
"all"/"month"/"week"/"day". Returns{ board, entries, me, scope }. - isAvailable() —
falsewhen the game isn't in our iframe; lets you hide leaderboard UI during local dev.
Board configuration
Open My Games → Boards after your game is approved to configure boards:
sort_direction:desc(high-score) orasc(speedruns).score_format:integer,time_ms, orfloat.min_play_ms: required session length before a submit counts. Default 3s.max_score: optional hard cap for obvious nonsense.
Boards you haven't explicitly created are auto-provisioned on first submit with default settings, so zero-setup integration works out of the box.
Anti-cheat (what we do)
- Signed-in users only.
user_idcomes from the G-Less cookie, not from your game. - Minimum play duration. Submits too soon after
playStartedAtare rejected. - Per-user rate limit: 30 submits / minute / board.
- Outlier filter: scores far outside the current top-10 distribution are accepted but auto-hidden.
- One-best-per-player: only the player's personal best is shown publicly.
- Hard cap enforcement when the board has a
max_scoreconfigured.
Anti-cheat (what your game must do)
- Submit at the natural end of a run, not by replaying saved progress on load.
- Never ingest a "saved high score" from
localStorageand resubmit it. - For time boards, start the timer on first input, pause when the game pauses.
- If the player leaves and returns much later,
init()again before the next submit. - Don't put the score into URL hashes / visible DOM fields that a user can edit pre-submit.
AI-friendly docs
Point your coding assistant at /sdk/SKILL.md — it's a Cursor / agent-ready skill file that teaches the integration in a single read.