Made by @raknet

Learn the Language

From zero to hero – Lua & Roblox Luau, explained the way you'd actually talk

👁️ Live viewers count: 322

Lua Basics

Lua is a lightweight, fast scripting language. Roblox uses a superset called Luau, but the core is pure Lua. This section covers everything you need to get started – no fluff, just the good stuff.

1. Variables and Data Types

You declare variables with local – that's the right way. It keeps things scoped and avoids messing up the global space. Types are: nil, boolean, number, string, function, table, thread (and userdata, but that's for later).

local name = "Alex"          -- string
local age = 25               -- number
local isAlive = true         -- boolean
local nothing = nil          -- nil
print(type(age))             -- "number"

Why local? Because if you don't use it, the variable becomes global and can be accessed anywhere – that's a recipe for bugs. So always use local.

2. Tables – Your One True Data Structure

Tables are everything in Lua. They're like arrays, dictionaries, and objects all rolled into one. You'll use them for lists, maps, and even classes.

-- Array (list)
local fruits = {"apple", "banana", "cherry"}
print(fruits[1])  -- apple (1‑based, not 0!)

-- Dictionary (key-value)
local person = { name = "John", age = 30 }
print(person.age) -- 30

-- Mixed
local mixed = { "hello", key = 42, [3] = true }

Pro tip: You can use any value as a key – numbers, strings, even other tables. Just remember that arrays start at 1, not 0.

3. Functions – Reusable Blocks

Functions are first‑class citizens – you can pass them around, store them in variables, and return them from other functions.

-- Named function
function greet(name)
    print("Hello, " .. name)
end
greet("Lua")

-- Anonymous function assigned to a variable
local add = function(a, b) return a + b end
print(add(3, 4))  -- 7

You can also define functions with the local function syntax – it's the same, just looks cleaner sometimes.

4. Control Flow – if, for, while

If you've coded before, this will look familiar. Lua uses if ... then ... elseif ... else ... end. Loops are for, while, and repeat ... until.

local score = 85
if score >= 90 then
    print("A")
elseif score >= 80 then
    print("B")
else
    print("C or below")
end

for i = 1, 5 do
    print(i)  -- 1,2,3,4,5
end

for key, value in pairs(person) do
    print(key, value)
end

local counter = 0
while counter < 3 do
    counter = counter + 1
    print(counter)
end

Notice pairs for dictionaries and ipairs for arrays. But in Luau you can skip those – we'll cover that in the Luau tab.

5. Metatables – Power Tools

Metatables let you change the behavior of a table – like overloading operators, handling missing keys, etc. It's the foundation of OOP in Lua.

local t1 = { value = 10 }
local t2 = { value = 20 }

local mt = {
    __add = function(a, b)
        return { value = a.value + b.value }
    end
}
setmetatable(t1, mt)
setmetatable(t2, mt)

local t3 = t1 + t2
print(t3.value)  -- 30

You can also use __index to implement inheritance – that's how you'd create classes in pure Lua.

6. Built‑in Libraries – string, table, math

Lua ships with a bunch of useful libraries. Here are some common ones:

local numbers = {3, 1, 4, 1, 5}
table.sort(numbers)                 -- {1,1,3,4,5}
print(string.upper("hello"))        -- HELLO
print(math.random(1, 10))           -- random number

7. Error Handling with pcall

Sometimes things go wrong. You can catch errors using pcall – it runs a function and returns a success flag and the result or error message.

local success, result = pcall(function()
    error("something went wrong")
end)
if not success then
    print("Error caught: " .. result)
end

This is super useful for preventing your whole script from crashing.

8. Modules – Organize Your Code

Lua lets you split code into modules. A module is just a file that returns a table (or a function). You use require to load it.

-- mymodule.lua
local M = {}
function M.sayHi()
    print("Hi from module")
end
return M

-- main.lua
local mod = require("mymodule")
mod.sayHi()

9. Coroutines – Pause and Resume

Coroutines let you run functions that can be paused and resumed – great for state machines, animations, or any step‑by‑step logic.

local co = coroutine.create(function()
    for i = 1, 3 do
        print(i)
        coroutine.yield() -- pause
    end
end)
coroutine.resume(co) -- prints 1
coroutine.resume(co) -- prints 2

10. File I/O – Reading and Writing

In standalone Lua, you can use the io library to work with files. In Roblox, this doesn't exist – but it's good to know for other projects.

local file = io.open("data.txt", "r")
if file then
    local content = file:read("*a")
    print(content)
    file:close()
end

11. Debug Library – Peek Inside

The debug library lets you inspect stack traces, get line numbers, and even set breakpoints programmatically. Use it carefully – it's powerful but can be slow.

function foo()
    print(debug.getinfo(1).currentline) -- line number
end
foo()

12. Bitwise Operations – bit32

Lua has a bit32 library for bitwise operations (AND, OR, XOR, shifts). Useful for low‑level stuff like flags.

local a = 5  -- 0101
local b = 3  -- 0011
print(bit32.band(a, b)) -- 1 (0001)

13. Advanced String Patterns

Lua's pattern matching is like regex but simpler. You can use %d for digits, %a for letters, . for any char, etc.

local s = "hello 123 world"
local num = string.match(s, "%d+") -- "123"
local replaced = string.gsub(s, "%d+", "XXX")

14. Table Manipulation – Insert, Remove, Sort, Concat

These are your bread and butter for managing lists.

local t = {"a", "c", "b"}
table.insert(t, "d")   -- at end
table.remove(t, 2)     -- removes "c"
table.sort(t)          -- {"a","b","d"}
print(table.concat(t, ", ")) -- a, b, d

15. Closures – Functions that Remember

When a function captures variables from outside its scope, it forms a closure. This is how you create private data.

function makeCounter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end
local counter = makeCounter()
print(counter()) -- 1
print(counter()) -- 2

16. Iterators – Custom Loops

You can write your own iterators using closures. This is how pairs and ipairs work under the hood.

function myRange(start, finish)
    local i = start - 1
    return function()
        i = i + 1
        if i <= finish then return i end
    end
end
for num in myRange(1, 3) do print(num) end

17. Variable Arguments – vararg

Use ... to accept a variable number of arguments. You can pack them into a table with {...}.

function sum(...)
    local total = 0
    for _, v in ipairs({...}) do total = total + v end
    return total
end
print(sum(1,2,3,4)) -- 10

18. Calling C Functions – Lua C API

Lua can be extended with C functions. This is how Roblox implements many of its services internally. Not something you'll use in day‑to‑day scripting, but good to know.

19. Garbage Collection – Memory Management

Lua manages memory automatically – it uses garbage collection. You can manually trigger it with collectgarbage() but usually you don't need to.

20. Lua Style Guide – Keep It Clean

Luau Features

Luau is Roblox's enhanced version of Lua. It adds types, modern iteration, performance tweaks, and a bunch of new libraries. Everything here works in Roblox and will make your code cleaner and faster.

1. Type Annotations – Catch Bugs Early

You can annotate variables, parameters, and return types. It's optional but super helpful – the editor will highlight type mismatches.

local name: string = "Luau"
local age: number = 5

function greet(person: string, times: number): string
    return person .. " repeated " .. tostring(times)
end

2. Generalized Iteration – No More ipairs

You can iterate over any array‑like table directly – no need for ipairs or pairs.

local colors = {"red", "green", "blue"}
for color in colors do
    print(color)   -- red, green, blue
end

for index, color in colors do
    print(index, color)   -- 1 red, 2 green, 3 blue
end

3. New String and Table Utilities

local parts = string.split("a,b,c", ",")  -- {"a","b","c"}
local t = {x=1, y=2}
local copy = table.clone(t)
local idx = table.find({"cat","dog","bird"}, "dog")  -- 2

4. Arrow Functions – Shorter Syntax

You can write functions with => – they're shorter and great for one‑liners.

local add = (a, b) => a + b
print(add(2,3))  -- 5

-- With a block body:
local multiply = (a, b) => {
    local result = a * b
    return result
}

5. Enhanced pcall – Error Objects

Same as Lua, but you can also pass custom error objects.

6. Modules with Relative Paths

You can use require("folder.module") – it's cleaner.

7. Bit32 – Still There

Bitwise operations are the same as in Lua – band, bor, bxor, lshift, rshift.

8. Math Round, Sign, and More

Luau adds math.round, math.sign, and math.clamp.

print(math.round(3.6))  -- 4
print(math.sign(-5))    -- -1

9. Table Clear, Move, etc.

table.clear(t) empties a table, table.move copies elements to another table.

10. String.gmatch – Iterator over Matches

Great for parsing.

for word in string.gmatch("foo bar baz", "%a+") do
    print(word) -- foo, bar, baz
end

11. Task Library – Reliable Timers

The task library replaces wait(). It's more precise and doesn't get throttled.

task.spawn(function()
    task.wait(1)
    print("After 1 second")
end)

12. Buffer – Raw Bytes

buffer lets you work with binary data – useful for networking or file formats.

local buf = buffer.create(4)
buffer.writeu32(buf, 0, 12345)
print(buffer.readu32(buf, 0)) -- 12345

13. Debug Improvements

Better stack traces and more info in debug.getinfo.

14. Performance Tips – Cache Globals

Store frequently used globals in locals – it's faster.

local print = print
print("fast")

15. Type Aliases – Define Custom Types

You can create type aliases for complex types.

type PlayerData = { name: string, level: number }
local data: PlayerData = { name = "Alex", level = 10 }

16. Union Types – Multiple Possible Types

Use | to allow multiple types.

function getId(id: number | string): string
    return tostring(id)
end

17. Optional Types – ?

Mark a type as optional with ?.

function getAge(player: { age: number? })
    return player.age or 0
end

18. Never Type – no return

Use never for functions that never return (like error()).

function halt(): never
    error("halt")
end

19. Type Checking at Runtime

You can use type and typeof to check types at runtime.

20. String Methods – gmatch, split, etc.

All the string goodness from Lua, plus more.

Roblox Features

This is the good stuff – everything you need to build cool stuff in Roblox. From services to events to optimizations – we've got you covered.

1. Services – The Right Way

Always use game:GetService("ServiceName") to get services. It's faster and more reliable than using game.ServiceName.

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

2. Character Handling – Respawn‑Safe

Get the player's character – but be ready for respawns. Here's the pattern I use:

local LocalPlayer = Players.LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
-- Or just: local Character = LocalPlayer.CharacterAdded:Wait()

3. RunService – RenderStepped vs Heartbeat vs Stepped

4. Aimbot – The Clean Implementation

Here's that aimbot we talked about – simple, modern, and works.

-- (same as before – I'll keep it compact)
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Camera = workspace.CurrentCamera

local LocalPlayer = Players.LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local currentTarget = nil
local isLocked = false

UserInputService.InputBegan:Connect(function(input, processed)
    if processed or input.KeyCode ~= Enum.KeyCode.E then return end
    isLocked = not isLocked
    if not isLocked then currentTarget = nil return end

    local root = Character:FindFirstChild("HumanoidRootPart")
    if not root then return end
    local pos = root.Position
    local closest, bestDist = nil, math.huge

    for _, other in Players:GetPlayers() do
        if other == LocalPlayer then continue end
        local char = other.Character
        if not char then continue end
        local otherRoot = char:FindFirstChild("HumanoidRootPart")
        local otherHead = char:FindFirstChild("Head")
        if not otherRoot or not otherHead then continue end
        local dist = (otherRoot.Position - pos).Magnitude
        if dist < bestDist then
            bestDist = dist
            closest = char
        end
    end
    currentTarget = closest
end)

RunService.RenderStepped:Connect(function()
    if not isLocked or not currentTarget then return end
    local head = currentTarget:FindFirstChild("Head")
    if head then
        Camera.CFrame = CFrame.lookAt(Camera.CFrame.Position, head.Position)
    end
end)

5. Raycasting – Shoot or Detect

Raycasting is essential for guns, line‑of‑sight, and picking things up.

local function raycast(origin, direction, range)
    local params = RaycastParams.new()
    params.FilterType = Enum.RaycastFilterType.Blacklist
    params.FilterDescendantsInstances = {Character}
    local result = workspace:Raycast(origin, direction * range, params)
    return result
end

6. RemoteEvents & RemoteFunctions

For client‑server communication – use RemoteEvents for one‑way, RemoteFunctions for request‑response.

-- Client
local remote = game:GetService("ReplicatedStorage").MyEvent
remote:FireServer("hello")

-- Server
remote.OnServerEvent:Connect(function(player, msg)
    print(player.Name .. " said: " .. msg)
end)

7. BindToClose – Clean Shutdown

Use this on the server to save data before the game closes.

game:BindToClose(function()
    -- save stuff
    print("Server shutting down")
end)

8. DataStores – Save Player Progress

Use DataStoreService to save player data across sessions.

local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("PlayerData")
store:SetAsync(player.UserId, {coins = 100})

9. Pathfinding – AI Movement

PathfindingService is your friend for NPC navigation.

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath()
path:ComputeAsync(startPos, endPos)

10. TweenService – Smooth Animations

Animate properties smoothly – position, size, color, etc.

local TweenService = game:GetService("TweenService")
local tween = TweenService:Create(part, TweenInfo.new(1), {Position = Vector3.new(10,0,0)})
tween:Play()

11. CollectionService – Tagging

Tag instances and retrieve them easily – great for grouping enemies or interactables.

local CollectionService = game:GetService("CollectionService")
CollectionService:AddTag(part, "Enemy")
local enemies = CollectionService:GetTagged("Enemy")

12. UserInputService – Keyboard, Mouse, Gamepad

Handles all input – we used it in the aimbot. Also supports mouse movement, key up/down, etc.

13. ContextActionService – Key Bindings

Bind actions to keys with context – useful for menus or interaction prompts.

local ContextActionService = game:GetService("ContextActionService")
ContextActionService:BindAction("Jump", function(action, state, input)
    if state == Enum.UserInputState.Begin then
        -- jump
    end
end, false, Enum.KeyCode.Space)

14. SoundService – Playing Sounds

Manage audio globally – play sounds, adjust volume, etc.

15. PhysicsService – Collision Groups

Control which objects can collide – useful for filtering raycasts or making parts ignore each other.

16. HttpService – Making HTTP Requests

Send and receive data from web APIs – but be careful, it's limited on the client.

17. ScriptContext – Error Handling

You can set a global error handler with ScriptContext.Error to catch script errors.

18. LocalizationService – Multi‑Language

For internationalization – translate strings based on player's locale.

19. MarketplaceService – In‑Game Purchases

Handle developer products, game passes, and purchases.

20. Optimizations – Keep it Snappy

Final tip: Always test your scripts in your executor before publishing. And use task.wait() – it's the new standard.