From LSL to Lua
For LSL Scripters
Section titled “For LSL Scripters”You already know how Second Life scripting works—events, permissions, object communication, inventory management. All of that knowledge transfers directly to Lua. This page focuses on the syntax and pattern differences you need to know.
Why Consider Lua?
Section titled “Why Consider Lua?”- Faster execution and ~50% less memory than LSL/Mono
- Modern features: coroutines, gradual typing, multiple return values
- Easier development: familiar syntax, better tooling, standard libraries
- Same ll functions* you already know
Syntax Quick Reference
Section titled “Syntax Quick Reference”Comments & Structure
Section titled “Comments & Structure”Variables & Types
Section titled “Variables & Types”Typecasting
Section titled “Typecasting”In many cases, Luau does typecasting automatically. Here’s how to do it explicitly:
LSL and Lua typecasts return different results on invalid input: LSL returns “zero”; Lua returns nil. The Lua examples include an or expressions that converts nil to what LSL typecast would have returned
Operators
Section titled “Operators”| Operation | LSL | Lua | Notes |
|---|---|---|---|
| Not equal | != | ~= | Different operator |
| Logical AND | && | and | Word, not symbol |
| Logical OR | || | or | Word, not symbol |
| Logical NOT | ! | not | Word, not symbol |
| String concat | + | .. | Different operator |
| Increment | x++ | x = x + 1 or x += 1 | No ++ operator |
| Decrement | x-- | x = x - 1 or x -= 1 | No -- operator |
| Power | llPow(2, 3) | 2 ^ 3 | Native operator |
| Integer division | 7 / 4 → 1 | 7 // 4 → 1 | Use // not / |
| String length | llStringLength(s) | #s | Native operator |
| Bitwise AND | llGetAgentInfo(id) & AGENT_TYPING | bit32.btest(ll.GetAgentInfo(id), AGENT_TYPING) | No & operator (1) |
| Bitwise OR | PASSIVE | SCRIPTED | bit32.bor(PASSIVE, SCRIPTED) | No | operator |
| Bitwise NOT | ~0 | bit32.bnot(0) | No ~ operator (2) |
| Bitwise eXclusive OR | 6 ^ 3 | bit32.bxor(6, 3) | ^ means Power instead (2) |
| Bitwise Shift Left | 1 << 2 | bit32.lshift(1, 2) | No << operator (2) |
| Bitwise Shift Right | 0x80000000 >> 2 | bit32.arshift(0x80000000, 2) | No >> operator (2) |
Notes:
bit32.btestreturns a boolean;bit32.bandreturns an integer. Both match the behavior of LSL&. Integers (bit32.band) don’t work insideifstatements, unlike LSL. Use whichever is most appropriate.- Hexadecimal numbers
0x8000000and above are positive in Lua, but negative in LSL, due to different number formats (signed 32-bit int vs 64-bit float). Usebit32.s32to wrap large numbers down to the 32-bit signed integer range for LSL compatability.
Control Flow
Section titled “Control Flow”Functions
Section titled “Functions”Lists vs Tables
Section titled “Lists vs Tables”Tables are Lua’s most powerful data structure. Unlike LSL’s separate list type, tables serve two purposes: they work as both arrays (like LSL lists) and dictionaries/maps (which LSL doesn’t have).
Tables as Arrays (LSL list replacement)
Section titled “Tables as Arrays (LSL list replacement)”Tables as Dictionaries/Maps
Section titled “Tables as Dictionaries/Maps”LSL doesn’t have a dictionary/map type—you had to use two parallel lists for key-value pairs. Lua tables can store key-value pairs natively:
-- Dictionary/maplocal playerData: {[string]: number} = { ["Alice"] = 100, ["Bob"] = 85, ["Charlie"] = 92}
-- Access values by keylocal aliceScore: number = playerData["Alice"] -- 100
-- Add/update entriesplayerData["Diana"] = 88playerData["Alice"] = 105 -- Update
-- Check if key existsif playerData["Unknown"] ~= nil then ll.Say(0, "Found!")end
-- Iterate over key-value pairsfor name: string, score: number in playerData do ll.Say(0, `{name}: {score}`)endShorthand syntax for string keys:
-- Instead of brackets and quotes, use dot notationlocal player = { name = "Alice", health = 100, position = vector(10, 20, 30)}
ll.Say(0, player.name) -- "Alice"player.health = 50 -- UpdateMixed Tables
Section titled “Mixed Tables”Tables can even mix numeric indices and string keys:
local data = { 10, 20, 30, -- Indices 1, 2, 3 name = "Alice", -- String key active = true -- String key}
ll.Say(0, `First: {data[1]}`) -- 10ll.Say(0, `Name: {data.name}`) -- "Alice"String Operations
Section titled “String Operations”ll* Functions
Section titled “ll* Functions”Key difference: Use ll.FunctionName (dot notation), not llFunctionName.
Event Handling: The Big Change
Section titled “Event Handling: The Big Change”This is the most significant difference between LSL and Lua. Instead of state-based event handlers, Lua uses event callbacks with LLEvents:on().
Lua Event Patterns
Section titled “Lua Event Patterns”Basic event handler:
LLEvents:on("touch_start", function(events: {DetectedEvent}) ll.Say(0, `Touched by {events[1]:getName()}`)end)Multiple events (run simultaneously):
LLEvents:on("touch_start", function(events: {DetectedEvent}) ll.Say(0, "Touched!")end)
LLEvents:on("collision_start", function(events: {DetectedEvent}) ll.Say(0, "Collision!")end)Listen events:
ll.Listen(0, "", NULL_KEY, "")
LLEvents:on("listen", function(channel: number, name: string, id: string, message: string) ll.Say(0, `{name} said: {message}`)end)Accessing detected data:
LLEvents:on("touch_start", function(events: {DetectedEvent}) for i, event in events do local name: string = event:getName() local key: uuid = event:getKey() ll.Say(0, `{name} touched me!`) endend)Global Variables
Section titled “Global Variables”Common Patterns
Section titled “Common Patterns”Door Script
Section titled “Door Script”Timer Events
Section titled “Timer Events”Type Annotations Guide
Section titled “Type Annotations Guide”While types are optional, use them in your code! They catch errors and improve readability. Learn more about Luau’s type system.
-- Basic typeslocal count: number = 0local name: string = "Alice"local active: boolean = true
-- SL typeslocal pos: vector = vector(10, 20, 30)local rot: rotation = rotation(0, 0, 0, 1)local id: string = "uuid-here"
-- Tables/arrayslocal scores: {number} = {95, 87, 92}local names: {string} = {"Alice", "Bob"}
-- Custom typestype Player = { name: string, health: number, position: vector}
local player: Player = { name = "Alice", health = 100, position = vector(0, 0, 0)}
-- Function types (always use!)function heal(player: Player, amount: number): Player player.health = player.health + amount return playerendStandard Libraries
Section titled “Standard Libraries”Lua includes Lua standard libraries you can use alongside ll* functions. See the full Luau library reference for more details.
-- String librarystring.upper("hello") -- "HELLO"string.sub("hello", 1, 2) -- "he" (1-based!)string.format("Health: %d", health)
-- Table librarytable.insert(items, value)table.remove(items, index)table.sort(items)
-- Math librarymath.floor(3.7) -- 3math.ceil(3.2) -- 4math.random(1, 10) -- Random 1-10math.abs(-5) -- 5Next Steps
Section titled “Next Steps”- Lua Basics - Deep dive into the language
- Lua Events - Master event handling patterns
- Lua Reference - All ll* functions
- Lua FAQ - Common questions