In short
To make an RNG game in Roblox Studio, keep one odds table in a ModuleScript that both the server and the client read, roll on the server with Random when a player asks, save what they got, and reveal it with a build-up animation. Add luck as a server-set multiplier on rare weights, announce very rare rolls to every server with MessagingService, and grow the game with a collection index, upgrades and new auras.
- The server picks every result. The client only asks and plays the reveal.
- Anything bought with Robux that gives a random result, or changes the odds, is a paid random item: show every outcome and its odds before purchase.
- Check
PolicyServicefor each player and hide or replace paid random items whereArePaidRandomItemsRestrictedis true.
On this page
What makes an RNG game work
The core loop in one line: roll, watch the reveal, keep the best result, get luckier or faster, roll again. There is no level to beat. The player plays against the odds table, and the table has to be designed as carefully as a level would be.
| How often | What happens | What it needs from you |
|---|---|---|
| Every second or two | A roll and a short reveal | A reveal that builds up longer and louder the rarer the result |
| Every few minutes | A rare roll, a luck boost wearing off, an upgrade bought | Rarities spaced so something new arrives often early on |
| Every session | A new entry in the collection, a new best aura equipped | A collection index with empty slots, and saving |
| Every update | New auras, a limited-time pool, a new luck upgrade | An odds table you can extend without breaking old rolls |
- A rarity curve with a long tail. Show rarity as "1 in N" so players can feel the gap. Early rarities should arrive within a few minutes; the top ones should be stories players tell.
- A reveal worth watching. The animation is the gameplay. A common roll should be quick so it never feels slow; a rare one earns a pause, a colour flash and a sound.
- Showing off. The equipped aura is visible on the character, and the rarest rolls are announced to the server or every server. Rare results only matter if other people see them.
- Progress besides luck. Roll speed, luck upgrades, inventory space, a collection to complete. Without these, a player with a bad hour has nothing to show for it.
- Trust. Players compare notes. Odds you show must be the odds the server uses, and the server must be the only one deciding.
Egg and pet games use the same roll with a different reward; see the pet game guide and the egg hatching script.
The systems you need
| System | What it does | Roblox pieces | Guide |
|---|---|---|---|
| Odds table | Every aura and its weight, read by server and client | ModuleScript in ReplicatedStorage | This page |
| Server roll | Picks a weighted result with a cooldown | Random, RemoteFunction | This page, RemoteEvents |
| Reveal | Spins through names, then lands on the result | ScreenGui, TweenService, Sound | UI animations |
| Collection and storage | Keeps owned auras, lets players equip and delete | DataStoreService, a grid GUI | Inventory, Data stores |
| Equipped aura | Shows the aura on the character | ParticleEmitter, Attachment on the character | This page |
| Luck | Multiplies rare weights for a while | Player attributes set by the server | This page |
| Announcements | Tells every server about a very rare roll | MessagingService | This page |
| Upgrades and shop | Roll speed, luck tiers, storage slots | A shop GUI, MarketplaceService | Shop GUI, Game pass |
| Leaderboards | Total rolls, rarest aura found | leaderstats, OrderedDataStore | Leaderboard |
Build plan: from a baseplate to launch
First playable
- Write the odds table first. Six to ten auras with weights. Turn each weight into "1 in N" (total weight divided by its weight) and read the list as a player would: how long until the second rarity? The fifth?
- Add the roll server below and a single Roll button that prints the result. Roll a few hundred times in a test and check the counts look like the table.
- Make the reveal. A label that cycles through aura names and slows down onto the result. Make common rolls short.
- Save results to a list per player and add an equip button that puts a particle effect on the character.
- Add the odds screen: every aura with its percentage, read from the same module.
First public test
Roll button, reveal, storage, equip, odds screen, saving. Nothing for sale yet. Watch how long players stay before their first rare roll, and whether they come back the next day. If they leave before anything exciting happens, lower the early rarities before you add content.
What to add for launch
- Upgrades bought with an earned currency: faster rolls, more storage, small luck tiers.
- A collection index that shows unfound auras as silhouettes with their "1 in N".
- Rare roll announcements, a rolls leaderboard and daily rewards.
- Anything sold for Robux, only after the policy work in the next sections is done.
- The Maturity and Compliance Questionnaire in the Creator Dashboard, so your game is not restricted for missing content maturity information.
The defining mechanic: a weighted roll on the server
The roll is a weighted pick: add up every weight, choose a random number below the total, and walk the list until it runs out. The module below holds the table and the maths. Because it lives in ReplicatedStorage, the client can require it to draw the odds screen, and the numbers it shows are computed from the exact table the server rolls with.
-- ModuleScript in ReplicatedStorage named Auras.
-- One table drives the roll on the server and the odds the client shows, so they always match.
export type Aura = { name: string, weight: number }
local LIST: { Aura } = {
{ name = "Common Glow", weight = 600 },
{ name = "Ember", weight = 250 },
{ name = "Frostbite", weight = 120 },
{ name = "Stormcaller", weight = 25 },
{ name = "Eclipse", weight = 4.9 },
{ name = "Void Crown", weight = 0.1 },
}
local LUCK_FROM = 25 -- luck multiplies every aura this rare or rarer (weight at or below)
local Auras = {}
Auras.LIST = LIST
local function weightOf(aura: Aura, luck: number): number
return if aura.weight <= LUCK_FROM then aura.weight * luck else aura.weight
end
-- Each aura's chance in percent for a player with this luck. They add up to 100.
function Auras.odds(luck: number): { [string]: number }
local total = 0
for _, aura in LIST do
total += weightOf(aura, luck)
end
local result = {}
for _, aura in LIST do
result[aura.name] = weightOf(aura, luck) / total * 100
end
return result
end
-- Shows a percentage with four more decimal places than its first non-zero digit
function Auras.formatPercent(percent: number): string
local places = if percent >= 1 then 4 else -math.floor(math.log10(percent)) + 4
return string.format("%." .. places .. "f%%", percent)
end
-- The roll itself. Only the server calls this.
function Auras.roll(rng: Random, luck: number): string
local total = 0
for _, aura in LIST do
total += weightOf(aura, luck)
end
local pick = rng:NextNumber(0, total)
for _, aura in LIST do
pick -= weightOf(aura, luck)
if pick <= 0 then
return aura.name
end
end
return LIST[#LIST].name
end
return Auras
Luck multiplies the weight of every aura at or below LUCK_FROM. That makes rare auras more likely and common ones less likely, and Auras.odds(luck) returns the new percentages, so a client can redraw the odds screen while a boost is active. formatPercent rounds the way the policy allows: four decimal places below the first non-zero digit.
-- Script in ServerScriptService named RollServer.
-- Players ask to roll; the server picks the aura, keeps the cooldown and announces rare finds.
local MessagingService = game:GetService("MessagingService")
local PolicyService = game:GetService("PolicyService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
type AurasModule = { roll: (Random, number) -> string, odds: (number) -> { [string]: number } }
local Auras = require(ReplicatedStorage:WaitForChild("Auras")) :: AurasModule
local ROLL_COOLDOWN = 1 -- seconds between rolls
local ANNOUNCE_BELOW = 1 -- auras under 1% chance are announced to every server
local TOPIC = "RareRoll"
local function remote(name: string): RemoteFunction
local r = Instance.new("RemoteFunction")
r.Name = name
r.Parent = ReplicatedStorage
return r
end
local rng = Random.new()
local lastRoll: { [Player]: number } = {}
remote("Roll").OnServerInvoke = function(player: Player)
local now = os.clock()
if now - (lastRoll[player] or 0) < ROLL_COOLDOWN then
return nil
end
lastRoll[player] = now
-- Luck is an attribute only server scripts set, from earned boosts or a paid potion
local luck = math.max(1, tonumber(player:GetAttribute("Luck")) or 1)
local aura = Auras.roll(rng, luck)
if Auras.odds(luck)[aura] < ANNOUNCE_BELOW then
pcall(function()
MessagingService:PublishAsync(TOPIC, player.DisplayName .. " rolled " .. aura .. "!")
end)
end
return aura
end
-- The shop asks before it shows a paid luck potion. false: hide it and offer an earnable boost.
remote("CanBuyLuck").OnServerInvoke = function(player: Player)
local ok, info = pcall(function()
return PolicyService:GetPolicyInfoForPlayerAsync(player)
end)
return ok and info.ArePaidRandomItemsRestricted == false
end
pcall(function()
MessagingService:SubscribeAsync(TOPIC, function(message)
print(message.Data) -- replace with a banner on every client
end)
end)
Players.PlayerRemoving:Connect(function(player)
lastRoll[player] = nil
end)
The client calls Roll:InvokeServer() and plays the reveal for whatever name comes back. A nil means the cooldown was not over. Luck comes from a Luck attribute that only server scripts write, so a player cannot raise it from their own client. Store the result in the player's saved list before returning it, so a crash during the reveal never loses a roll.
Roblox's rules for paid random items
Roblox's policy page on paid random items says that, because of regulations in various countries, certain requirements may apply to random items bought directly or indirectly with Robux. That includes random items bought with in-game currency that was itself bought with Robux. In an RNG game, this is most of what you might sell.
What counts
| Type in the policy | Roblox's examples | In an RNG game |
|---|---|---|
| Capsule items | Spin a wheel, hatch an egg, open a chest | A pack of rolls sold for Robux, or a roll paid with gems bought with Robux |
| Enhancement items | A potion with a random duration, an upgrade with a chance to work | A potion that may or may not double a result |
| Combination items | Combining two rare eggs for a better chance | Fusing auras for a chance at a higher one |
| Probability modifier items | Luck boosts, pity systems, rate-up scrolls | A paid luck potion, a paid guaranteed-rare-after-N counter |
Rolls you give away for playing are not paid. The policy says that if a randomized reward comes from an action that does not involve paying Robux or currency bought with Robux, you are not required to disclose the odds. If every roll in your game is free and nothing paid touches the odds, the disclosure rules do not apply, though showing odds anyway builds trust.
Odds disclosure
- Show every possible outcome and its actual numerical odds before the purchase.
- Odds are percentages, and all final outcomes must add up to exactly 100%. For long decimals you may round to four or more places below the first non-zero digit, with a note such as "Probabilities are rounded; total of displayed individual probabilities may not equal 100%."
- If the list does not fit on screen, put it behind a button labelled with a word such as "Info" or "Details" that players can open before buying. A lone (i) icon is not enough.
- When many outcomes share the same odds, you may state once at the top of the list "Odds for each item listed below: X%".
- If a player can only get an outcome once, update the remaining odds for that player.
- A paid item that improves other odds, such as a lucky potion, must explain its effect in numbers before purchase, and the shown odds must update while it is active to show the player's true odds.
- This covers indirect purchases too: if Robux buys keys, re-roll tickets or spin tickets, show the odds before the player spends them.
Players who cannot buy paid random items
Call PolicyService:GetPolicyInfoForPlayerAsync(player) on the server. When ArePaidRandomItemsRestricted is true, that player cannot interact with paid random items, either with Robux or with currency bought with Robux, and Roblox lists six treatments you must choose from:
- Offer an unpaid, earnable path to the item, such as free rolls at gameplay milestones.
- Offer outcomes in a fixed, non-random order shown before purchase.
- Sell the specific outcome directly, priced on its expected value.
- Remove or hide the paid random item.
- Block the purchase with an error message, such as "Sorry, this item is unavailable for users in your region".
- Keep the player out of the parts of the game that have paid random items, for example by teleporting them away.
The CanBuyLuck remote in the script above is the hiding treatment: the shop asks before it shows a paid luck potion. Check again on the server when the purchase is prompted, not only in the shop. A second field, IsPaidItemTradingAllowed, matters if you add trading: where it is false, that player must not be able to trade the result of a paid random item or other paid items. The trading guide has a server-side trade you can gate with it.
Monetization that fits an RNG game
Because the policy covers luck and paid rolls, the cleanest RNG monetization sells things that are not random at all. These are the usual options, and how each one sits with the policy:
| Roblox feature | Example | Policy note |
|---|---|---|
| Game pass (bought once) | More storage slots, a quick reveal, an extra equip slot | Not random and does not change odds |
| Game pass (bought once) | A permanent luck multiplier | A probability modifier: explain it in numbers, update shown odds, apply PolicyService treatments |
| Developer product (bought again) | A 10-minute luck potion | A probability modifier, same rules as above |
| Developer product (bought again) | A pack of rolls | A capsule item: full odds before purchase, PolicyService treatments |
| Subscription (monthly) | A daily free roll bundle and a monthly cosmetic | Keep the benefits for the full term once live |
| Private server | Rolling with friends | Not random |
The policy page does not list speed passes such as auto roll or faster rolls. They do not change the odds of a single roll, but they do buy more random results per minute, so the careful choice is to treat them like the paid rolls above: keep the odds screen up to date and apply the PolicyService treatment to players who are restricted.
Two other rules: rewards for rewarded video ads must be developer products and cannot be randomized items, so "watch an ad for a free roll" is out, while "watch an ad for a fixed currency bonus" fits. And Roblox's Creator Rewards program replaced engagement-based payouts on 24 July 2025. The Robux tax calculator shows what a sale earns after Roblox's cut.
What brings rollers back
- The collection index. Empty silhouettes with their "1 in N" are a to-do list. Reward completing a tier of the index with a permanent, earned luck bonus.
- Free luck that is earned. A luck boost for playing 15 minutes, or a daily one from a daily reward streak. Unpaid rewards keep the loop going for players who never buy.
- Server events. A short window where everyone in the server rolls with more luck turns a quiet server into a crowd watching the same screen.
- Limited auras. A pool that only exists for a season gives a reason to come back during it. Disclose its odds like any other.
- Quests. "Roll 200 times", "find an aura rarer than 1 in 1,000". They give structure to a loop that has none of its own.
- Codes for free rolls or earned boosts at update time (codes guide).
Build an RNG game with RoCode
RoCode is an independent AI agent for Roblox Studio. You describe the game in a web chat, and its free Studio plugin writes the Luau and creates the scripts, parts and UI in the place you have open. For an RNG game, ask for the roll and odds first, then add the reveal, storage and upgrades in later rounds.
Make the first playable version of an aura RNG game: an odds table of 8 auras in a ModuleScript, a Roll button that asks the server to roll with weighted odds and a 1 second cooldown, a reveal that cycles names and lands on the result, an odds screen with every aura as a percentage, saved storage of rolled auras, and an equip button.
- Writes the odds module and the server roll, with the result decided on the server
- Can add ready-made screens such as an inventory, a collection index, notifications, a shop and daily rewards
- Builds the game a working part at a time, so you can test the roll before the reveal
- Can run a test robot that clicks buttons and reports PASS or FAIL when the agent decides to check
It cannot judge whether your odds feel good, make custom particle textures or meshes, or publish the game. Free credits every day, no card needed. How RoCode builds a game.
Questions
Do I have to show odds in a Roblox RNG game?
You must show every outcome and its numerical odds for any random item bought directly or indirectly with Robux, including luck boosts. For rolls that are completely free and not affected by anything paid, the policy says you do not have to, though many players expect it.
Can a luck potion be sold for Robux?
Yes, but the policy lists luck boosts as probability modifier items. Explain the effect in numbers before purchase, update the shown odds while it is active, and use PolicyService to hide it or offer another treatment for restricted players.
Why roll on the server and not the client?
Anything a client decides, a cheater can change. The server picks the result with Random and the client only plays the reveal, so the result cannot be forced.
How do I make "1 in a million" auras?
Give the aura a weight a million times smaller than the total of all weights. Weights do not need to be whole numbers, as the 0.1 weight in the table above shows.
Can I make an RNG game without scripting?
Not a working one: the roll, saving and the odds screen are all scripts. You can paste the code on this page, or describe the game to an AI tool such as RoCode and test what it builds.
Sources and further reading
- Roblox Creator Docs: Paid random items policy guidelines. what counts, odds disclosure, PolicyService treatments
- Roblox Creator Docs: PolicyService. GetPolicyInfoForPlayerAsync
- Roblox Creator Docs: Random
- Roblox Creator Docs: Cross-server messaging. global announcements with MessagingService
- Roblox Creator Docs: Passes
- Roblox Creator Docs: Developer products
- Roblox Creator Docs: Rewarded video ads. rewards cannot be randomized items
- Roblox Creator Docs: Creator Rewards
- RoCode guide: Egg hatching script
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 26 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.