31. From Lua to Luau

So far the code ran in the terminal with lua. From here on it runs inside Roblox Studio, the editor Roblox uses to build experiences. Studio runs its own flavour of Lua, called Luau. This chapter covers what stays the same, what changes, and where the new ideas come from.

Luau vs Lua: what is the same

Almost everything you have learned carries over to Luau unchanged:

  • Types: nil, boolean, number, string, table, function.
  • All the operators: arithmetic, comparison, logical, .., #.
  • local, if, elseif, else, end, while, repeat, for, break, return.
  • Functions, multiple return values, methods, the self parameter, the colon syntax.
  • Tables as lists and dictionaries, ipairs, pairs, table.insert, table.remove, #.
  • Metatables, __index, the .new / :method class pattern.
  • Most of the string library (string.format, string.upper, string.gmatch, etc.) and the math library.

If you can write the Part 6 inventory project in plain Lua, that same code (minus the io.* calls and terminal print) drops straight into a Luau script.

What is different

Three things change in Roblox.

1. Where the program runs

A terminal program has one entry point: the file you handed to lua. A Roblox experience is a tree of Instances — Parts, Scripts, Folders, Models — with code living inside Script and LocalScript objects on that tree. There is no main.lua. Each script runs on its own once its Instance exists.

There are three kinds of script object:

  • Script — runs on the server; can change the world for everyone.
  • LocalScript — runs on a single player's computer. Used for UI, camera, sound — anything local to one player.
  • ModuleScript — like a plain Lua module from chapter 25. Returns a table; other scripts require it.

The book's mini-project uses one Script (server) and nothing else.

2. Built-in objects

Luau in Roblox gives you names plain Lua does not have:

Name What it is
game The root of the whole experience.
workspace Shortcut for game.Workspace. Holds the world.
game.Players The list of currently connected players.
script The script you are inside (your own object).
Instance.new(class) Create a new Instance of the given class.
task.wait(seconds) Pause this script for the given duration.
task.spawn(fn) Run fn in a new coroutine.

You create objects with Instance.new:

local part = Instance.new("Part")
part.Name = "Coin"
part.Size = Vector3.new(2, 0.2, 2)
part.Position = Vector3.new(0, 5, 0)
part.BrickColor = BrickColor.new("Bright yellow")
part.Anchored = true
part.Parent = workspace

Line one creates a Part. The middle lines set its properties. The last places it inside workspace — which is what makes it appear in the world.

Vector3.new(x, y, z) and BrickColor.new(name) are factory functions for Roblox value types. Like Instance.new, they just return a value.

3. Events

In a terminal program, things happen because you ask: you call a function, it runs. In a game, they happen because the world changes: a button is pressed, a part is touched, a player joins. Roblox exposes these moments as events. Connect a function to an event, and it runs whenever the event fires.

local part = workspace.Coin

part.Touched:Connect(function(other)
    print("Something touched the coin: " .. other.Name)
end)

What is happening:

  • part.Touched is an event on the Part.
  • :Connect(...) registers a function to run when it fires.
  • The function takes one argument — the other Part that touched it.
  • The script keeps running afterward (no loop needed); whenever a Part bumps into part, the function runs.

This is the part of Luau most unlike plain Lua. Once it clicks, most of Roblox programming is just write a function; connect it to the right event.

Side-by-side: same logic, two flavours

A function that prints a greeting, twice:

-- Plain Lua (terminal)
local function greet(name)
    print("Hello, " .. name)
end

greet("Keiko")
greet("Roblox")

The same function in a Roblox Script, called when a player joins:

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

local function greet(name)
    print("Hello, " .. name)
end

Players.PlayerAdded:Connect(function(player)
    greet(player.Name)
end)

The greet function is identical. Only the trigger changed: in the terminal you called greet yourself; in Roblox the engine calls it each time a player joins.

game:GetService("Players") is the idiomatic way to reach the Players service. game.Players works too, but :GetService is the form Roblox docs use, and it is safer if a service has not loaded yet.

Installing Roblox Studio

To run anything in Part 7, you need Roblox Studio, a free download:

  1. Go to https://create.roblox.com/.
  2. Sign in with the same account you use to play Roblox.
  3. Click Start Creating — Studio downloads and installs.
  4. Open Studio and pick the Baseplate template.

A flat grey plate opens. The left panel is the Explorer (the tree of Instances), the right panel shows the Properties of the selected item, and the bottom is the Output panel — where print ends up.

The next four chapters dig into Studio — Instances, events, services, leaderstats — then the Part 7 mini-projects pull them together into real, playable scenes.

Homework

Most homework so far ran in the terminal. The next few only run with Studio open, so treat them as pen-and-paper exercises: write the code, then check the solutions page.

Problem 1 — Spot the difference

Open exercises/31/homework/01-spot-the-difference.lua. It holds two snippets separated by a comment (one plain Lua, one Roblox Luau). List at least three differences in a multi-line comment at the bottom of the file.

Problem 2 — Make a Part appear

Open exercises/31/homework/02-make-a-part.lua. Write a script (no function needed) that creates a red Part of size 4 x 1 x 4 at position 0, 10, 0, anchored and parented to workspace. In Studio this would go inside a Script in ServerScriptService.

Problem 3 — Wire up Touched

Open exercises/31/homework/03-wire-up-touched.lua. Given a variable part that refers to a Part, write the snippet that connects a function to the Part's Touched event. It should print <otherName> touched the part, where <otherName> is the Name of the other Part.

Challenge — Blinking part

Open exercises/31/homework/04-blink.lua. Inside a while true do ... end loop, flip a Part's BrickColor between two colours every second using task.wait(1). Print a line on each change so the Output panel shows progress.

Stuck or finished? Open the homework solutions page.