Roblox Luau Tutorial: From Zero to Your First Game

Last updated:

Quick answer: A Roblox Luau tutorial takes you from your first print('Hello world') in Roblox Studio to a working mini-game by teaching the core language step by step: variables, strings, conditionals, loops, functions, and where each script type actually runs.

A Roblox Luau tutorial teaches you to write Luau, the Lua-based language Roblox runs, inside Roblox Studio. You start with print('Hello world'), then build up to variables, loops, and functions that move parts and keep score. This guide takes you from zero to a working mini-game.

Where do I write Luau code in Roblox Studio?

Roblox Studio is the free editor where every Roblox game is built, and it is where all of this tutorial happens. Get the setup right first, because half of beginner confusion is code running in the wrong place rather than wrong code.

The rule of thumb: Script objects go in ServerScriptService (or inside the part they control), and the editor tab that opens is a normal text editor with syntax highlighting and autocomplete for Luau.

What is the first script to write in Roblox?

The traditional first program prints a message to the Output window. Replace whatever the new script contains with this and press Play (F5):

print("Hello world!")

The game starts, the Output window shows Hello world!, and you have just run Luau code on the Roblox server. print writes any value to Output — numbers, strings, tables — which makes it your main debugging tool from day one. Its sibling warn prints in yellow and is the convention for "something unexpected happened but keep running":

print("Game started")
warn("This shows up in yellow")
print(2 + 2) -- 4

Stop the playtest with the Stop button before editing again: changes made while playing do not stick.

How do local variables work in Luau?

Variables store values you want to reuse or change. In Luau you almost always declare them with local, which limits the variable to the block it is written in:

local coins = 0
local playerName = "Stefi"
local isRunning = true

coins = coins + 5
print(coins) -- 5

coins += 3 -- shorthand for coins = coins + 3
print(coins) -- 8

Every value has a type, but Luau infers it for you: numbers, strings, booleans, nil (the "no value" value), tables, functions, and Roblox Instances like parts and scripts. A variable declared without a value is nil, and reading a typo'd name gives nil instead of an error — a classic source of the attempt to index nil error. Skipping local creates a global variable: slower to look up, visible everywhere, and a bug factory, so use it only on purpose.

How do strings and string interpolation work in Luau?

Strings are text in quotes. You can join them with .., measure them with #, and — the modern way — embed values directly with backtick interpolation:

local name = "Stefi"
local coins = 10

print("Hello, " .. name)                 -- classic concatenation
print("Name length: " .. #name)          -- 5
print(`Hello, {name}!`)                  -- interpolation
print(`You have {coins * 2} coins.`)     -- expressions work inside the braces

Backtick strings treat {...} as code to evaluate, which makes building messages far less error-prone than a chain of .. and tostring. Remember that .. joins strings only: "Coins: " .. coins errors when coins is a number, so wrap the number with tostring(coins) — or just use an interpolated backtick string, which calls tostring for you.

How does if/else work in Luau?

Conditionals decide which code runs. Luau uses then, elseif, and end — and the comparison operator is ==, never a single =:

local coins = 10

if coins >= 10 then
	print("you can buy the sword")
elseif coins > 0 then
	print("almost there")
else
	print("no coins yet")
end

if coins ~= 10 then -- ~= means "not equal"
	print("changed")
end

Combine conditions with and, or, and not. One Luau quirk to memorize: only nil and false are falsey — the number 0 and the empty string are both truthy, unlike languages such as C or JavaScript.

How do loops work in Luau?

Loops repeat work: counting with for, repeating with while. task.wait(n) pauses the script for n seconds, which is how you keep a loop from freezing the whole game:

for i = 1, 5 do
	print("countdown " .. i)
	task.wait(1)
end

local coins = 0
while coins < 3 do
	coins += 1
	print(coins)
	task.wait(0.5)
end

for _, part in workspace:GetChildren() do
	print(part.Name)
end

Always use task.wait, task.spawn, and task.defer. The older globals wait(), spawn(), and delay() are legacy: less precise, scheduler-hostile, and deprecated — there is a full task.wait vs wait() comparison if you are migrating old code. The last loop uses the generic for form to walk a list of objects; _ is the conventional name for a loop variable you ignore.

How do functions work in Luau?

Functions package reusable behaviour. Declare them with local function, pass values in as parameters, and send a result back with return:

local function doubleCoins(amount)
	return amount * 2
end

local function greet(playerName)
	print(`Welcome, {playerName}!`)
end

print(doubleCoins(5)) -- 10
greet("Stefi")

local function makeCoinValue(startAmount)
	local value = Instance.new("IntValue")
	value.Value = startAmount
	return value
end

Functions are values, so you can store them in variables and pass them to Roblox events — exactly how event callbacks like part.Touched:Connect(function(hit) ... end) work. A function without a return gives back nil.

What is the difference between Script, LocalScript, and ModuleScript?

This is the concept that decides whether your first code works at all. Roblox runs the game on a server and shows it to each player through their own client, and each script type lives on one side of that line:

TypeRuns onPut it hereUse it for
ScriptThe server (one copy for everyone)ServerScriptService, WorkspaceGame rules, currency, damage, saving data
LocalScriptOne player's own clientStarterPlayerScripts, StarterCharacterScripts, StarterGuiCamera, keyboard/mouse input, UI, visual effects
ModuleScriptWherever it is requiredReplicatedStorage, ServerStorageShared code, config tables, reusable services

A Script changing a coin value is the truth for every player. A LocalScript changing the same value only lies to that one player's screen — and exploiters can edit anything on their client, which is why game rules never live there. ModuleScripts are not scripts at all until someone loads them with require:

-- ModuleScript named Config in ReplicatedStorage
local Config = {}
Config.StartingCoins = 50
return Config

-- In any other script:
local Config = require(game:GetService("ReplicatedStorage"):WaitForChild("Config"))
print(Config.StartingCoins)

If your very first script should print something in the Output window while you play alone in Studio, a Script in ServerScriptService is the one to pick — the FAQ below covers why.

How do I build a first mini-game: a coin collector with leaderstats?

Enough syntax — let's make something playable. The project: coins scattered on the baseplate, a counter in the player list, and a score that goes up when you touch a coin. This needs exactly two scripts.

First, the scoreboard. Add a Script to ServerScriptService that creates a leaderstats folder for every joining player — Roblox automatically shows IntValues inside it in the player list:

-- Script in ServerScriptService
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Value = 0
	coins.Parent = leaderstats
end)

Then make a coin: insert a Part into Workspace, name it "Coin", set Anchored to true, and give it a gold color. Insert a Script inside that part:

-- Script inside the coin Part in Workspace
local Players = game:GetService("Players")
local coin = script.Parent
local debounce = false

coin.Touched:Connect(function(hit)
	if debounce then
		return
	end

	local player = Players:GetPlayerFromCharacter(hit.Parent)
	if not player then
		return
	end

	local leaderstats = player:FindFirstChild("leaderstats")
	local coins = leaderstats and leaderstats:FindFirstChild("Coins")
	if not coins then
		return
	end

	debounce = true
	coins.Value += 1
	print(`{player.Name} picked up a coin ({coins.Value} total)`)
	coin.Transparency = 1
	coin.CanCollide = false

	task.wait(2) -- respawn the coin

	coin.Transparency = 0
	coin.CanCollide = true
	debounce = false
end)

Press Play: walking into the coin bumps the counter in the player list, prints to Output, hides the coin, and brings it back two seconds later. Duplicate the coin part a few times and you have a working collectible loop. The debounce flag is what stops one touch from counting ten times per step.

From here the sky is the limit: store the score permanently with DataStore saving, or spend coins in a shop — the leaderboard structure above is the same one used by leaderstats in every Roblox game.

Common mistakes beginners make in Roblox Luau

Learn Luau by doing

Reading a Roblox Luau tutorial is the map, but the language sticks when you type the code yourself. On Luablox you can study the concepts in depth on /theory, drill syntax and debugging on /problems, and follow structured Luablox lessons that carry this exact path from first print to full gameplay systems. When you want the deeper language background, the what is Luau guide covers how Luau relates to Lua and why Roblox uses it.

Frequently asked questions

Is Luau hard to learn?

No. Luau is one of the gentlest first languages: it has a small syntax, no semicolons or curly braces to memorize, and Roblox Studio shows errors immediately in the Output window. Most beginners write their first working script within an hour and a playable coin-collector loop within a day; the parts that take longer are Roblox concepts like client vs server, not the language itself.

Do I need to know Lua before learning Luau?

No. Luau is based on Lua 5.1 but is a separate, better-typed language, and learning Lua first is unnecessary — you would still have to learn Roblox APIs, Roblox Studio, and Luau additions like string interpolation and += either way. If you already know Lua, you are ahead on syntax but should skip legacy Lua patterns like module() and learn task.* instead of wait()/spawn().

Should my first script be a Script or a LocalScript?

A Script in ServerScriptService. It runs once on the server, its print output shows in the Output window immediately, and it is the correct home for game logic like scores and damage. Reach for a LocalScript only when you need one player's input, camera, or UI — and remember a LocalScript's changes are visible only to that player.

How long does a Roblox Luau tutorial like this take to finish?

The walkthrough above takes one to two hours if you type every example and build the mini-project in Studio. Reaching comfort with the language — writing scripts without copying — usually takes a few weeks of small projects, and this guide's sections map directly onto the order you should practice them.

What is the difference between Luau and Lua in Roblox?

Luau is Roblox's fork of Lua 5.1: the core syntax is the same, but Luau adds type annotations, string interpolation with backticks, compound assignment like +=, and performance optimizations, while dropping or deprecating pieces like getfenv and the legacy wait()/spawn() scheduler functions in favor of task.*.

Understand loop structures in Luau, including numeric, while, generic pairs, and ipairs iterators.

Related pages