How to make a clicker game in Roblox Studio
A big button, helpers that click for you, and prices that climb — the idle-clicker loop, built so the server keeps the score and auto-clickers can't break it.
Updated November 9, 20269 min read
A clicker (or idle) game is played almost entirely in the UI: players click one big button, spend what they earn on helpers that earn for them, and watch the number grow even when they stop clicking. Unlike a simulator, there is no walking to a sell zone — the whole game is the click button, the shop and the math behind the prices.
The core loop
Every clicker game comes down to one loop that players repeat. Build this first, in grey blocks:
- Click the big button to earn
- Buy a helper that earns every second
- Buy more of it as the price climbs
- Unlock the next, stronger helper and repeat
Systems you need
Click button
A big TextButton or ImageButton in a ScreenGui. The client only says “I clicked”; the server decides how much that is worth.
Helpers
Things that earn per second (workers, machines, robots — your theme). Each one you own adds income; each new type is far stronger and far more expensive.
Growing prices
Each copy of a helper costs more than the last, usually about 15% more. This one number sets the pace of the whole game.
Upgrades
One-time purchases that double a helper's output or the value of a click. They give players a reason to save up.
Number display
Show big numbers as 1.2K, 3.4M, 5.6B. Long raw numbers are unreadable and make the UI overflow.
Saving and offline income
Save the score and helper counts with DataStoreService. Many clickers also pay part of the per-second income for the time a player was away (store the time they left).
Build order
- 1
The UI first
A ScreenGui named
ClickerGuiin StarterGui with a big TextButton namedClickButtonand a Frame namedShopholding one TextButton per helper, named exactly like the helpers in the script (Helper,Machine,Robot). - 2
Server script
Paste the server script below into ServerScriptService. It creates the RemoteEvents, the
Clicksleaderstat and the per-second income. - 3
Client script
Paste the LocalScript into
ClickerGui. It sends clicks and purchases and shows the price on each shop button. - 4
Balance the numbers
Play for 15 minutes. The first helper should come in under a minute, the second in a few minutes. Change
baseCost,perSecondandCOST_GROWTHuntil it feels right. - 5
Juice
A click sound, the button shrinking slightly on press, and a floating “+1” make clicking feel good. Do these in the LocalScript — they are only visual.
- 6
Save and add upgrades
Save
Clicksand the helper counts with a data store, then add upgrades and rebirths once the first 30 minutes feel good.
The key script: Server-side clicks, helpers and income
Where it goes: ServerScriptService > Clicker (Script) + StarterGui > ClickerGui > ClickerClient (LocalScript).
- The UI from step 1 of the build order:
ClickerGuiwithClickButtonand aShopframe holding TextButtons namedHelper,MachineandRobot. - The code below is two scripts — split it at the dashed line. The first is a Script in ServerScriptService, the second a LocalScript inside
ClickerGui.
-- ServerScriptService > Clicker (Script)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HELPERS = {
Helper = { baseCost = 10, perSecond = 1 },
Machine = { baseCost = 120, perSecond = 6 },
Robot = { baseCost = 1500, perSecond = 50 },
}
local COST_GROWTH = 1.15 -- each copy costs 15% more than the last
local MAX_CLICKS_PER_SECOND = 15 -- faster clicks are ignored
local remotes = Instance.new("Folder")
remotes.Name = "ClickerRemotes"
remotes.Parent = ReplicatedStorage
local clickEvent = Instance.new("RemoteEvent")
clickEvent.Name = "Click"
clickEvent.Parent = remotes
local buyEvent = Instance.new("RemoteEvent")
buyEvent.Name = "Buy"
buyEvent.Parent = remotes
local clickWindows = {}
local function costOf(name, owned)
return math.floor(HELPERS[name].baseCost * COST_GROWTH ^ owned)
end
local function setOwned(player, name, owned)
player:SetAttribute(name, owned)
player:SetAttribute(name .. "Cost", costOf(name, owned))
end
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local clicks = Instance.new("IntValue")
clicks.Name = "Clicks"
clicks.Parent = leaderstats
player:SetAttribute("PerClick", 1)
for name in HELPERS do
setOwned(player, name, 0) -- load saved counts here
end
end)
Players.PlayerRemoving:Connect(function(player)
clickWindows[player] = nil
end)
clickEvent.OnServerEvent:Connect(function(player)
local now = os.clock()
local window = clickWindows[player]
if not window or now - window.start >= 1 then
window = { start = now, count = 0 }
clickWindows[player] = window
end
window.count += 1
if window.count > MAX_CLICKS_PER_SECOND then
return
end
local stats = player:FindFirstChild("leaderstats")
if stats then
stats.Clicks.Value += player:GetAttribute("PerClick") or 1
end
end)
buyEvent.OnServerEvent:Connect(function(player, name)
-- Never trust what the client sends: check the name and the price here.
if typeof(name) ~= "string" or HELPERS[name] == nil then
return
end
local stats = player:FindFirstChild("leaderstats")
if not stats then
return
end
local owned = player:GetAttribute(name) or 0
local cost = costOf(name, owned)
if stats.Clicks.Value < cost then
return
end
stats.Clicks.Value -= cost
setOwned(player, name, owned + 1)
end)
-- Income from helpers, once a second.
while true do
task.wait(1)
for _, player in Players:GetPlayers() do
local stats = player:FindFirstChild("leaderstats")
if stats then
local income = 0
for name, helper in HELPERS do
income += (player:GetAttribute(name) or 0) * helper.perSecond
end
stats.Clicks.Value += income
end
end
end
--------------------------------------------------------------------
-- StarterGui > ClickerGui > ClickerClient (LocalScript)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local remotes = ReplicatedStorage:WaitForChild("ClickerRemotes")
local gui = script.Parent
gui:WaitForChild("ClickButton").Activated:Connect(function()
remotes.Click:FireServer()
end)
for _, button in gui:WaitForChild("Shop"):GetChildren() do
if button:IsA("TextButton") then
local function refresh()
local owned = player:GetAttribute(button.Name) or 0
local cost = player:GetAttribute(button.Name .. "Cost") or "?"
button.Text = button.Name .. " x" .. owned .. " — " .. tostring(cost)
end
player:GetAttributeChangedSignal(button.Name):Connect(refresh)
player:GetAttributeChangedSignal(button.Name .. "Cost"):Connect(refresh)
refresh()
button.Activated:Connect(function()
remotes.Buy:FireServer(button.Name)
end)
end
endHow it works
- The client never sends an amount — only “clicked” or “buy Helper”. The server works out the value and the price, so an exploiter can't give themselves points.
- The click limit means auto-clickers and macro tools earn no more than a very fast human. That keeps leaderboards fair and lets you sell an auto-click pass without breaking the economy.
- Prices and owned counts live on the player as attributes, so the shop buttons update by themselves when the server changes them.
- Add saving next: store
Clicksand each helper count in one data store entry per player, and load them where the comment says.
How clicker games make Robux
- A 2× clicks game pass and a 2× income game pass — the classic sellers in the genre.
- An auto-click game pass that clicks for the player on the server (for example 5 clicks a second). The server cap keeps it fair.
- Timed boosts (2× for 15 minutes) as developer products.
- Cosmetic button skins and click effects — they don't change the balance, so free players don't feel left behind.
You receive 70% of what players spend on game passes and developer products; Roblox keeps 30%.
Common mistakes
The client says how much it earned
A RemoteEvent like AddClicks(amount) lets exploiters send any number. Send only “clicked” and let the server decide the amount.
Prices that grow linearly
If each helper costs a fixed amount more, players buy hundreds and the game is over in an hour. Multiply the price instead (about 1.1–1.2× per copy).
Unreadable numbers
Show 1.2M instead of 1200000. Write one small format function and use it for every number on screen.
Nothing to do while waiting
If the next purchase is 20 minutes away, players leave. Space the helpers and upgrades so there is always something affordable soon.
FAQ
How do you make a clicker game on Roblox?
Make a ScreenGui with a big click button and a shop, then a server script that adds points when the button's RemoteEvent fires and sells helpers that earn every second. This page has both scripts.
What is the difference between a clicker and a simulator in Roblox?
A clicker happens almost entirely in the UI: click a button, buy helpers that earn for you. A simulator puts the player in the world: click with a tool, walk to a sell zone, unlock new areas.
How do I stop auto-clickers in my clicker game?
You can't stop people using them, but you can make them pointless: count clicks per second on the server and ignore anything above a human limit, as this script does.
How do I make a click button in Roblox Studio?
Insert a ScreenGui into StarterGui, add a TextButton, and in a LocalScript connect the button's Activated event to a RemoteEvent:FireServer() call. Activated works for mouse clicks and taps on phones.