§ How to · with Picoo

How to make a Roblox leaderboard (with AI)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A cross-server Roblox leaderboard needs three wired-up parts: an OrderedDataStore that ranks every player's score, a loop that publishes and reads it without burning the DataStore budget, and a board in the world that shows the top N. Ask Picoo for a global leaderboard and it applies its global_leaderboard recipe, which builds all three.

2 files · 186 lines of Luau plus a 45-instance board · one recipe call. Ranks leaderstats Coins by default; name another stat in the prompt.

OrderedDataStore ranking

Scores go into an OrderedDataStore named Leaderboard_v1, keyed by UserId. GetSortedAsync(false, 10) reads the highest 10 across every server in one call.

Integer-safe publishing

OrderedDataStore stores whole numbers only, so every score goes through math.floor before it is written. Negative scores are skipped, and a score is written again only when it has changed.

Publish and refresh every 60 seconds

Each cycle publishes the score of every player in the server, then reads the top 10 and redraws the board. The interval is 60 seconds by default and never below 30.

Publish on leave and shutdown

A player's score is published when they leave, and BindToClose publishes everyone still in the server when it shuts down.

Board you can see in Edit mode

An anchored 18 × 14 stud Part named LeaderboardBoard with a SurfaceGui on its front face: a title, then 10 rows of rank, player name and score. It is built as one instance tree, so it appears in Explorer before you press Play.

Cached name lookup

UserIds are turned into names with Players:GetNameFromUserIdAsync inside pcall, and each name is cached for the life of the server. If a lookup fails, the row shows 'User' and the id.

Files Picoo ships for this prompt

2 files · 186 lines · one recipe call

ServerScriptService/GlobalLeaderboardServer.lua

Server Script: publishes each player's leaderstats score to the OrderedDataStore, reads the top 10 every 60 seconds, and writes the names and scores onto the board.

186 lines

Workspace/LeaderboardBoard

Anchored 18 × 14 stud board Part with a SurfaceGui: a 'Top Coins' title and 10 rows of rank, player and score. 45 instances in one build_tree call, visible in Edit mode.

instance

Sample output: ServerScriptService/GlobalLeaderboardServer.lua

--!strict
-- Generated by Picoo recipe: global_leaderboard
-- Cross-server top-10 board backed by OrderedDataStore.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")

-- GetOrderedDataStore itself errors in a place that was never published, and
-- can in Studio without API access. Called bare up here, that one error stopped
-- this whole script before the board was ever drawn. A failure now leaves
-- BOARD nil: the board ranks the players in this server instead (see
-- useServerBoard below), and nothing is written.
local boardOk, boardResult = pcall(function()
	return DataStoreService:GetOrderedDataStore("Leaderboard_v1")
end)
local BOARD: OrderedDataStore? = if boardOk then boardResult else nil
local STAT_NAME = "Coins"
local TOP_N = 10
local REFRESH_INTERVAL = 60

-- (excerpt) publishing a score, and the refresh loop:

local function publish(player: Player)
	local store = BOARD
	if not store then return end -- no DataStore: nothing to publish to
	local value = currentValue(player)
	if value == nil then return end

	-- OrderedDataStore accepts INTEGERS ONLY. A float is rejected outright, so
	-- a stat that ever holds 10.5 silently stops appearing on the board.
	local whole = math.floor(value)
	if whole < 0 then return end            -- negative scores are not ranked here
	if published[player.UserId] == whole then return end

	local _, err = safe(function()
		return store:SetAsync(tostring(player.UserId), whole)
	end)
	if err and RunService:IsStudio() then
		useServerBoard(err)
	elseif err then
		warn("[Leaderboard] publish failed for " .. player.Name .. ": " .. err)
	else
		published[player.UserId] = whole
	end
end

task.spawn(function()
	while true do
		-- Publish everyone first so the board reflects the current server too,
		-- then read the global top N.
		for _, player in Players:GetPlayers() do
			publish(player)
		end
		refresh()
		task.wait(REFRESH_INTERVAL)
	end
end)

Building a Roblox leaderboard

Cross-server leaderboards require OrderedDataStore. A regular DataStore cannot be read in sorted order, so retrieving the top N would mean loading every key. Picoo builds leaderboards from a fixed recipe, global_leaderboard, which writes each player's score to an OrderedDataStore keyed by UserId and reads the top 10 with a single GetSortedAsync call.

The recipe is built around the ways hand-written leaderboards usually break. OrderedDataStore stores whole numbers only, so a score of 10.5 is rejected; every write goes through math.floor first. Writing on every change spends the DataStore budget fast on a busy server; the script publishes on an interval and when a player leaves, and only when the score differs from the last value it wrote. GetSortedAsync returns pages rather than a list, so the script reads GetCurrentPage before filling the rows. Name lookups are web calls that can fail, so each one runs inside pcall, is cached for the life of the server, and falls back to 'User' and the id.

Every 60 seconds the server publishes the score of every player in it, then reads the global top 10 and writes the rank, name and score into the board's rows. Empty ranks show a dash. When the server shuts down, BindToClose publishes everyone still in it and waits 3 seconds.

Without DataStore access, in a place that was never published or a Studio play test with API access off, GetOrderedDataStore runs inside pcall, so the script keeps running: the board ranks the players in the server from their leaderstats, writes nothing, and the Output says once how to turn the global board on.

The board is an anchored 18 × 14 stud Part with a SurfaceGui on its front: a title such as 'Top Coins' and ten rows. It is built as one instance tree, so it shows up in Explorer in Edit mode, where you can move it, resize it or restyle it. The scores come from a value in each player's leaderstats, Coins unless you name another stat; if your game does not have one yet, ask for it in a follow-up prompt.

See more on the Luau generator, the AI Luau script writer, the 3D model generator and the game builder, read how Picoo compares with ChatGPT, or browse the full blog.

Frequently asked

Why OrderedDataStore and not regular DataStore?+

OrderedDataStore keeps its values sorted and supports GetSortedAsync, so the top N comes back in one call. A regular DataStore has no sorted read; finding the highest scores would mean loading every key.

Where do the scores come from?+

From each player's leaderstats. The script reads the value named Coins, or the stat you name, and publishes it; players without that value are skipped. If your game does not have that leaderstats value yet, ask for it in a follow-up prompt, for example 'save player coins so they don't reset'.

How often does the leaderboard refresh?+

Every 60 seconds by default. You can ask for a different interval, but the recipe keeps it at 30 seconds or more, because every cycle spends DataStore requests on publishing and on the sorted read.

Can I show more than the top 10?+

Yes. The recipe takes a row count from 3 to 50 and builds one row per rank. The default board is sized for 10 rows, so when you ask for more, ask for a taller board as well.

What about per-game-mode or per-week leaderboards?+

Use a separate OrderedDataStore per scope (TopScores_FFA, TopScores_Capture). For weekly resets, put the week in the store name (TopScores_2026W14) and each new week starts empty. The recipe takes the DataStore name as a setting; switching it automatically each week is a follow-up prompt.

Where does the board go?+

At position 0, 8, -30 by default, and you can give a position in the prompt. It is a normal anchored Part in Workspace, so you can also move, resize or recolor it in Studio. A ScreenGui list or a podium for the top 3 are follow-up prompts.

Does the leaderboard work in a Studio play test?+

Yes. In a place that was never published, or with Studio API access off, there is no DataStore to read, so the board ranks the players in the play test from their leaderstats and writes nothing. The Output says once how to turn the global board on: publish the place and turn on Game Settings > Security > Enable Studio Access to API Services.

Related Picoo prompts