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:

  1. Click the big button to earn
  2. Buy a helper that earns every second
  3. Buy more of it as the price climbs
  4. 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. 1

    The UI first

    A ScreenGui named ClickerGui in StarterGui with a big TextButton named ClickButton and a Frame named Shop holding one TextButton per helper, named exactly like the helpers in the script (Helper, Machine, Robot).

  2. 2

    Server script

    Paste the server script below into ServerScriptService. It creates the RemoteEvents, the Clicks leaderstat and the per-second income.

  3. 3

    Client script

    Paste the LocalScript into ClickerGui. It sends clicks and purchases and shows the price on each shop button.

  4. 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, perSecond and COST_GROWTH until it feels right.

  5. 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. 6

    Save and add upgrades

    Save Clicks and 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: ClickerGui with ClickButton and a Shop frame holding TextButtons named Helper, Machine and Robot.
  • 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) + StarterGui > ClickerGui > ClickerClient (LocalScript)
-- 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
end

How 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 Clicks and 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.

Keep reading