Lua Scripting for Beginners: Write Your First Roblox Script
Scripting is where Roblox games come alive, and it starts smaller than you think: one print, one variable, one part that reacts when a player touches it. This guide takes you from a blank script to three working examples you can paste in and run today.
In this guide
The Three Kinds of Script (And Why Beginners Mix Them Up) Your First Lines: print() and Variables Changing Properties With Code (Example 1: The Makeover Script) Events: Making Parts React (Example 2: The Kill Brick) Putting It Together (Example 3: A Touch Pad That Opens a Door) Reading Errors in the Output Window Common Beginner Mistakes Frequently Asked Questions How long does it take to learn Lua for Roblox? Why does my script do nothing at all — not even an error? What is the difference between FindFirstChild and WaitForChild?Building makes a game you can look at. Scripting makes a game that does things: doors that open, lava that kills, buttons that give points. Roblox games are scripted in Lua (Roblox's version is called Luau), and the jump from "I can't code" to "my script works" is smaller than people fear. You will learn where scripts live, write your first lines, control parts with code, and finish with three complete working examples — including the famous kill brick. No experience needed.
The Three Kinds of Script (And Why Beginners Mix Them Up)
Half of all beginner scripting problems are not coding problems — they are location problems. Roblox has three script types, and each runs somewhere different:
| Type | Runs on | Put it in | Use it for |
|---|---|---|---|
| Script | The server (one copy, for everyone) | ServerScriptService, Workspace, or inside a part | Game rules: kill bricks, points, doors, data saving |
| LocalScript | Each player's own device (one copy per player) | StarterPlayerScripts, StarterCharacterScripts, inside GUIs (StarterGui), or a Tool | Only that player: camera effects, menus, key presses |
| ModuleScript | Nowhere by itself — a toolbox other scripts borrow with require() | ReplicatedStorage (shared) or ServerScriptService | Reusable code and shared data |
The rule: anything that affects the whole game — health, points, doors, money — belongs in a Script on the server. A LocalScript changing health on a player's own device will not count, because the server is in charge and does not trust changes made on a player's device (this is called FilteringEnabled). If your script "runs but nothing happens", check type and location before touching a single line of code. This whole guide uses ordinary Scripts.
To create one: in the Explorer, hover over a part (or ServerScriptService), click +, choose Script. Also open the Output window (View tab > Output) and leave it open — it is where your scripts talk back to you.
Your First Lines: print() and Variables
Select all the text in your new script, delete it, and type this. Put the script inside a normal part in Workspace (insert a Part first if you need one):
local part = script.Parent
local message = "Hello from my first script!"
local luckyNumber = 7
print(message)
print("My lucky number is", luckyNumber)
print("This part is called", part.Name)
Press Play. Your three lines appear in Output — that is a running program. What just happened:
print()writes text to the Output window. Players never see it; it is your window into what the script is doing. Professionals print constantly while building.local part = script.Parentcreates a variable — a named box holding a value. Here it holds the part the script sits inside (scriptis the script itself;.Parentis whatever contains it). Fetch the part once, then just saypart.- Variables hold different types: text (a string, in quotes), numbers (
7,2.5) and booleans (true/false). Lua notices capitals:Trueis nottrue,Printis notprint. Many beginner errors are really capital-letter mistakes.
Changing Properties With Code (Example 1: The Makeover Script)
Everything you can set in the Properties window — colour, size, transparency — a script can set too, while the game runs. Keep the same script inside your part and replace its contents:
local part = script.Parent
part.Color = Color3.fromRGB(0, 170, 255)
part.Material = Enum.Material.Neon
part.Transparency = 0.25
part.CanCollide = false
print(part.Name, "has been restyled!")
Press Play and your part is now glowing blue, slightly see-through, and you can walk straight through it. Details worth knowing:
- Colours are written as Color3.fromRGB(red, green, blue), each number 0–255 — the same values any colour picker shows.
- Transparency runs 0 (solid) to 1 (invisible). Trap: a part can be invisible but still solid, because Transparency and CanCollide are separate. Ghost walls and invisible kill bricks combine them on purpose.
- Materials and other fixed-choice properties use Enum values, like
Enum.Material.Neon. TypeEnum.and Studio suggests options — use them instead of guessing spellings.
Events: Making Parts React (Example 2: The Kill Brick)
Changing things once, at start, is only half of scripting. The other half is events: "when X happens, do Y". The most-used beginner event is Touched — it fires when something touches a part. Here is the complete kill brick:
local killBrick = script.Parent
local recentlyKilled = {}
local function onTouched(hit)
local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
if humanoid and not recentlyKilled[humanoid] then
recentlyKilled[humanoid] = true
humanoid.Health = 0
task.wait(1)
recentlyKilled[humanoid] = nil
end
end
killBrick.Touched:Connect(onTouched)
Read it top to bottom:
- A function is a named bundle of instructions:
local function onTouched(hit) ... end. It does not run when written — it runs when called.hitis the part that touched the brick (usually a character's leg or arm). hit.Parentis the character model the leg belongs to;FindFirstChildOfClass("Humanoid")looks for the Humanoid, which holds health. If a loose part rolls onto the brick there is no Humanoid — and theifstops the script erroring.- An if statement runs code only when its condition is true:
if humanoid and not recentlyKilled[humanoid] then ... end. Everyifends withend, just like every function. - Why the
recentlyKilledtable? Touched does not fire once per player — it fires for every body part brushing the brick, many times a second. Without this "debounce" guard, one touch runs your code ten times. For health it barely matters (dead is dead), but on a pad giving +10 points per touch, one step would give +200. The debounce remembers "just handled this player", ignores repeats for 1 second, then forgets. Almost every real game script uses this pattern. - The last line is the wiring:
killBrick.Touched:Connect(onTouched)— "when Touched happens, run onTouched". Forget it and your perfect function never runs.
Putting It Together (Example 3: A Touch Pad That Opens a Door)
Now combine functions, if statements, properties and an event. Build two parts: a floor pad named DoorPad and a wall part named SecretDoor. Put this script inside DoorPad:
local pad = script.Parent
local door = workspace:WaitForChild("SecretDoor")
local busy = false
local function openDoor()
if busy then
return
end
busy = true
door.Transparency = 0.7
door.CanCollide = false
task.wait(3)
door.Transparency = 0
door.CanCollide = true
busy = false
end
pad.Touched:Connect(openDoor)
Step on the pad: the door fades, lets you through, and closes after 3 seconds. New ideas here: workspace:WaitForChild("SecretDoor") finds another object by name (spelling must match the Explorer exactly); if busy then return end bails out early if the door is already open — the simplest debounce; and task.wait(3) pauses this function without freezing the game. Change the 3 to change the open time. You now know the core of Roblox scripting: variables, properties, functions, if statements, events. Points systems, shops and checkpoints are these same five ideas, arranged bigger.
Reading Errors in the Output Window
Your scripts will error. That is normal — the skill is reading the error. When a script breaks during Play, Output shows a red message like:
Workspace.KillBrick.Script:8: attempt to index nil with 'Health'
It is a full address: where the script is (Workspace > KillBrick > Script), which line (8 — double-click the error and Studio jumps to that line), and what went wrong. The greatest hits:
- "attempt to index nil with …" — the script tried to use something that does not exist (nil = "nothing"). Usually a wrong name or capitalisation in a FindFirstChild/WaitForChild, or assuming a Humanoid was there. Check spelling in the Explorer.
- "… expected … near …" — a grammar typo: a missing
end, bracket, orthen. The reported line is where Lua gave up; the real typo is on that line or just above. - "X is not a valid member of Y" — you asked an object for a child or property it does not have. Wrong name, or the object is not where you think in the Explorer.
- "Infinite yield possible" (yellow warning) — a
WaitForChildhas waited long for something that may never appear. Translation: name or location is wrong.
Method when stuck: read the line number, look at that line, and print() the values just before it to see what is actually nil. Ninety percent of beginner errors are spelling, capitals, wrong location or a missing end — all findable in two minutes with Output open.
Common Beginner Mistakes
- Kill logic in a LocalScript. The server ignores health changes from a player's device. Server rules go in a Script.
- Coding before checking the Explorer. Half of scripting is naming and finding objects. If
script.Parentis not what you think, no code can save you. - Deleting the
ends. Everyfunctionand everyifneeds its ownend. The editor's auto-indentation is your clue: if the shape looks wrong, anendis missing. - No debounce on Touched. One step becomes twenty events: points explode, doors flicker. Guard touch code from day one.
- Editing code in Play mode. Those edits apply to the test session only and vanish on Stop. Script in Stop mode; test in Play mode.
Frequently Asked Questions
How long does it take to learn Lua for Roblox?
Useful scripts — kill bricks, opening doors, speed pads — are possible within the first few days, because they all use this guide's five ideas: variables, properties, functions, if statements, events. Designing bigger systems like shops and data saving usually takes a few weeks of small projects. The speed trick: always script for a real game you are building, not in the abstract.
Why does my script do nothing at all — not even an error?
Checklist: is it a Script (not a LocalScript somewhere the server never sees)? Is it somewhere that runs — inside a Workspace part or ServerScriptService? Did you write the :Connect(...) line wiring your function to the event? Does the part's name in your code match the Explorer exactly, capitals included? One of these four is the answer almost every time.
What is the difference between FindFirstChild and WaitForChild?
FindFirstChild("Name") looks once, instantly, and returns the object or nil if it is not there — good for checks like "does this character have a Humanoid?". WaitForChild("Name") pauses the script until the object appears — good for things that must exist, like your door or a player's data folder at join time. WaitForChild when the thing is definitely coming but might be late; FindFirstChild when it might legitimately not exist.