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”// Single line comment/* Multi-line comment */
integer x = 5; // Semicolons requiredVariables & Types
Section titled “Variables & Types”// Type declarations requiredinteger health = 100;float damage = 25.5;string name = "Alice";vector pos = <10, 20, 30>;rotation rot = <0, 0, 0, 1>;key uuid = "...";list items = [1, 2, 3];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 |
Control Flow
Section titled “Control Flow”// if, then, elseif (x > 5) { // ...} else if (x > 0) { // ...} else { // ...}
// whilewhile (count > 0) { count--;}
// forfor (i = 0; i < 10; i++) { // ...}
// do-whiledo { count++;} while (count < 10);Functions
Section titled “Functions”float calculateDamage(float base, float mult) { return base * mult;}
// Single return value onlyfloat result = calculateDamage(10.0, 1.5);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)”list items = [10, 20, 30, 40];
// 0-based indexinginteger first = llList2Integer(items, 0); // 10integer second = llList2Integer(items, 1); // 20
// List operationsitems += [50]; // Appendinteger len = llListLength(items);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”string msg = "Hello" + " " + "World";string name = "Alice";string greeting = "Hi " + name + "!";
integer len = llStringLength(msg);string sub = llGetSubString(msg, 0, 4); // "Hello"ll* Functions
Section titled “ll* Functions”llSay(0, "Hello");llSetPos(<10, 20, 30>);llSetRot(<0, 0, 0, 1>);llGiveInventory(avatar, "Object");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().
integer clickCount = 0;
default { state_entry() { llSay(0, "Ready!"); }
touch_start(integer num_detected) { clickCount++; llSay(0, "Clicked " + (string)clickCount + " times");
if (clickCount >= 5) { llSay(0, "Done counting!"); } }}Lua Event Patterns
Section titled “Lua Event Patterns”Basic event handler:
LLEvents:on("touch_start", function(num_detected: number) ll.Say(0, `Touched by {ll.DetectedName(1)}`)end)Multiple events (run simultaneously):
LLEvents:on("touch_start", function(num_detected: number) ll.Say(0, "Touched!")end)
LLEvents:on("collision_start", function(num_detected: number) 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(num_detected: number) for i = 1, num_detected do local name: string = ll.DetectedName(i) local key: string = ll.DetectedKey(i) ll.Say(0, `{name} touched me!`) endend)Global Variables
Section titled “Global Variables”// All script-level variables are globalinteger health = 100;string playerName = "Alice";
myFunction() { health = 50; // Modifies global}Common Patterns
Section titled “Common Patterns”Door Script
Section titled “Door Script”integer isOpen = FALSE;vector closedPos;vector openPos;
default { state_entry() { closedPos = llGetPos(); openPos = closedPos + <0, 0, 2>; }
touch_start(integer num) { if (isOpen) { llSetPos(closedPos); isOpen = FALSE; } else { llSetPos(openPos); isOpen = TRUE; } }}Timer Events
Section titled “Timer Events”integer counter = 0;
default { state_entry() { llSetTimerEvent(1.0); }
timer() { counter++; llSay(0, "Count: " + (string)counter); }}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