LSL Scripting Fundamentals

Your Complete Introduction to Programming in Virtual Worlds - Master the basics of LSL scripting from absolute zero to creating interactive objects.


Course Code
LSL-101

Level
Beginner

Duration
3-4 Hours

Certificate
Available

Lead Instructor

Sorin Todys - LSL Master Developer

With over 20 years of experience teaching scripting in virtual worlds, Sorin has helped thousands of creators bring their ideas to life. No prior programming experience required! All classes held at Alife Virtual School region.

Course Overview

Welcome to the exciting world of LSL scripting! This course will take you from complete beginner to confident script creator.

What You'll Learn

  • What LSL is and how scripts work in virtual worlds
  • Script structure, syntax, and fundamental concepts
  • Variables and data types (integers, floats, strings, vectors)
  • Functions and how to use them effectively
  • Events and how objects respond to the world
  • Control flow (if/else statements, loops)
  • States and state machines
  • Creating your first interactive objects

Learning Objectives

By the end of this course, you will be able to:

  • Read and understand LSL scripts
  • Write your own basic scripts from scratch
  • Use variables to store and manipulate data
  • Respond to touch, collision, and other events
  • Make decisions with if/else statements
  • Create multi-state objects (like light switches)
  • Debug common scripting errors
  • Continue learning more advanced topics
No Prerequisites: This course is designed for absolute beginners. No programming experience required! Just bring your curiosity and willingness to learn.

Lesson 1: What is LSL and How Does Scripting Work?

Understanding LSL

LSL (Linden Scripting Language) is a programming language specifically designed for virtual worlds like Second Life and OpenSimulator (which powers Alife Virtual). It allows you to add behavior and interactivity to objects.

What Can Scripts Do?
  • Respond to touch: Doors that open, buttons that activate
  • Move objects: Elevators, vehicles, animated sculptures
  • Communicate: Chat bots, greeters, information kiosks
  • Change appearance: Color changers, texture switchers
  • Detect avatars: Security systems, automatic doors
  • Create particles: Fire, smoke, magic effects
  • Play sounds: Music players, ambient effects
  • Control physics: Throwing objects, realistic movement

How Scripts Work

Scripts are text files that contain instructions written in LSL. When you place a script inside an object (a "prim"), the server reads and executes those instructions.

1 You write the script - Using the built-in script editor
2 Server compiles it - Converts your text to machine code
3 Script runs - Executes instructions and responds to events
4 Object becomes interactive - Now responds to touch, chat, timers, etc.

Accessing the Script Editor

To create your first script:

  1. Create or select an object (prim) in-world
  2. Right-click it and select Edit
  3. In the Edit window, click the Content tab
  4. Click New Script button
  5. A default script appears - you can edit it!
Pro Tip: Always save your scripts! Use Ctrl+S (or Cmd+S on Mac) to save. The script won't work until you save it.

Lesson 2: Anatomy of a Script - Your First "Hello World"

The Default Script

When you create a new script, you'll see this default code:

default
{
    state_entry()
    {
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Touched.");
    }
}

Breaking It Down

Let's understand each part:

1. States: default { }

Every script has at least one state. Think of a state as a mode or condition. The default state is where every script begins. More complex scripts can have multiple states (like "on" and "off").

default     // This is the state name
{
    // Events go inside these curly braces
}
2. Events: state_entry() and touch_start()

Events are things that happen to the object. When an event occurs, the code inside it runs.

  • state_entry() - Runs when the script starts or enters a new state
  • touch_start(integer total_number) - Runs when someone touches the object
state_entry()           // Event name
{
    // Code here runs when script starts
}

touch_start(integer total_number)    // Event with parameter
{
    // Code here runs when object is touched
}
3. Functions: llSay()

Functions are pre-built commands that do specific things. LSL has hundreds of built-in functions. They always start with ll (Linden Lab).

llSay(0, "Hello, Avatar!");
//    │   └─ The message to say
//    └─ Channel number (0 = public chat)

Common functions:

  • llSay(channel, text) - Speak in chat
  • llOwnerSay(text) - Message only to owner
  • llWhisper(channel, text) - Quiet message (10m range)
  • llShout(channel, text) - Loud message (100m range)

Customizing Your First Script

Let's modify the default script to make it more personal:

default
{
    state_entry()
    {
        llSay(0, "Welcome to my interactive object!");
        llOwnerSay("Script is running and ready.");
    }

    touch_start(integer total_number)
    {
        llSay(0, "Thanks for touching me!");
        llOwnerSay("Someone touched your object.");
    }
}
Try It Now:
  1. Copy the script above
  2. Paste it into a new script in your object
  3. Save it (Ctrl+S)
  4. Touch your object and see what happens!

Lesson 3: Variables - Storing Information

What Are Variables?

Variables are containers that store information. Think of them as labeled boxes where you can keep different types of data.

Data Types in LSL
Type Description Example
integer Whole numbers 42, -7, 1000
float Decimal numbers 3.14, -0.5, 99.9
string Text (in quotes) "Hello", "Alife Virtual"
key Unique identifier (UUID) "a1b2c3d4-..."
vector 3D coordinates (x,y,z) <128.0, 128.0, 25.0>
rotation Rotation (quaternion) <0.0, 0.0, 0.0, 1.0>
list Collection of items [1, 2.5, "text", key]

Declaring and Using Variables

// Declaring variables (global scope - outside events)
integer counter = 0;
float temperature = 98.6;
string message = "Hello, World!";
vector position = <128.0, 128.0, 25.0>;

default
{
    state_entry()
    {
        llSay(0, "Counter is: " + (string)counter);
        llSay(0, "Temperature is: " + (string)temperature);
        llSay(0, message);
    }
    
    touch_start(integer total_number)
    {
        // Increment the counter each touch
        counter = counter + 1;
        llSay(0, "Touched " + (string)counter + " times!");
    }
}
Variable Scope:
  • Global variables: Declared outside events (top of script) - accessible everywhere
  • Local variables: Declared inside events - only accessible in that event

Type Casting (Converting Types)

Sometimes you need to convert one type to another:

integer num = 42;
float decimal = 3.14;
string text = "Hello";

// Convert to string for displaying
llSay(0, "Number: " + (string)num);          // "Number: 42"
llSay(0, "Decimal: " + (string)decimal);     // "Decimal: 3.14"

// Convert string to integer/float
integer converted = (integer)"123";          // 123
float convertedFloat = (float)"99.9";        // 99.9

Practical Example: Counter Script

// Simple touch counter
integer touchCount = 0;
string objectName = "Touch Counter";

default
{
    state_entry()
    {
        llSetObjectName(objectName);
        llSetText("Touches: 0", <1,1,1>, 1.0);
        llOwnerSay("Counter initialized.");
    }

    touch_start(integer total_number)
    {
        // Increment counter
        touchCount = touchCount + 1;
        
        // Update floating text
        llSetText("Touches: " + (string)touchCount, <0,1,0>, 1.0);
        
        // Say the count
        llSay(0, "This object has been touched " + (string)touchCount + " times!");
    }
}

Lesson 4: Making Decisions - If/Else Statements

Understanding Control Flow

Control flow allows your script to make decisions and choose different actions based on conditions.

The if Statement
if (condition)
{
    // Code runs if condition is TRUE
}
Comparison Operators
Operator Meaning Example
== Equal to if (x == 5)
!= Not equal to if (x != 0)
> Greater than if (x > 10)
< Less than if (x < 100)
>= Greater or equal if (x >= 5)
<= Less or equal if (x <= 50)

If/Else Structure

if (condition)
{
    // Runs if condition is TRUE
}
else
{
    // Runs if condition is FALSE
}

Multiple Conditions: If/Else If/Else

integer score = 85;

if (score >= 90)
{
    llSay(0, "Grade: A - Excellent!");
}
else if (score >= 80)
{
    llSay(0, "Grade: B - Good job!");
}
else if (score >= 70)
{
    llSay(0, "Grade: C - Average");
}
else if (score >= 60)
{
    llSay(0, "Grade: D - Needs improvement");
}
else
{
    llSay(0, "Grade: F - Failed");
}

Logical Operators

Combine multiple conditions:

  • && - AND (both must be true)
  • || - OR (at least one must be true)
  • ! - NOT (inverts the condition)
integer age = 25;
integer hasTicket = TRUE;

// Both conditions must be true
if (age >= 18 && hasTicket)
{
    llSay(0, "Access granted!");
}

// At least one condition must be true
if (age < 18 || !hasTicket)
{
    llSay(0, "Access denied!");
}

Practical Example: Owner Detection

// Owner-only access system
default
{
    touch_start(integer total_number)
    {
        // Get the key of who touched the object
        key toucher = llDetectedKey(0);
        
        // Get the owner's key
        key owner = llGetOwner();
        
        // Check if toucher is the owner
        if (toucher == owner)
        {
            llSay(0, "Welcome, Owner! Access granted.");
            llOwnerSay("You have full access to this system.");
        }
        else
        {
            llSay(0, "Access denied. Owner only.");
            
            // Get toucher's name for logging
            string toucherName = llDetectedName(0);
            llOwnerSay("Access attempt by: " + toucherName);
        }
    }
}
Security Best Practice: Always use key comparisons for security, never names! Avatar names can be changed, but keys (UUIDs) are permanent and unique.

Lesson 5: States - Creating Multi-Mode Objects

Understanding States

States allow objects to have different behaviors depending on their current mode. Think of a light switch: it has "on" and "off" states, and touching it does different things in each state.

State Syntax
state state_name
{
    // Events for this state
    state_entry()
    {
        // Runs when entering this state
    }
    
    touch_start(integer num)
    {
        // Runs on touch while in this state
    }
}

Switching Between States

Use the state command to change states:

state new_state_name;  // Switches to new_state_name

Complete Example: Light Switch

// Two-state light switch
default  // This is the "OFF" state
{
    state_entry()
    {
        // When entering OFF state, make object dark
        llSetColor(<0.2, 0.2, 0.2>, ALL_SIDES);
        llSetText("OFF", <1,0,0>, 1.0);
        llOwnerSay("Light is OFF");
    }

    touch_start(integer total_number)
    {
        // When touched while OFF, turn ON
        llSay(0, "Turning light ON...");
        state on;  // Switch to "on" state
    }
}

state on  // This is the "ON" state
{
    state_entry()
    {
        // When entering ON state, make object bright
        llSetColor(<1.0, 1.0, 0.0>, ALL_SIDES);  // Yellow
        llSetText("ON", <0,1,0>, 1.0);
        
        // Make it glow and emit light
        llSetPrimitiveParams([
            PRIM_FULLBRIGHT, ALL_SIDES, TRUE,
            PRIM_POINT_LIGHT, TRUE, <1,1,0>, 1.0, 10.0, 0.75
        ]);
        
        llOwnerSay("Light is ON");
    }

    touch_start(integer total_number)
    {
        // When touched while ON, turn OFF
        llSay(0, "Turning light OFF...");
        
        // Remove the light
        llSetPrimitiveParams([
            PRIM_FULLBRIGHT, ALL_SIDES, FALSE,
            PRIM_POINT_LIGHT, FALSE, <0,0,0>, 0, 0, 0
        ]);
        
        state default;  // Switch back to default (OFF) state
    }
}
How It Works:
  1. Script starts in default state (OFF)
  2. state_entry() makes object dark
  3. Touch → switches to on state
  4. state_entry() in ON state makes it bright
  5. Touch again → switches back to default
  6. Each state has its own touch_start() behavior!

Advanced: Three-State Traffic Light

// Traffic light with 3 states
state green
{
    state_entry()
    {
        llSetColor(<0, 1, 0>, ALL_SIDES);  // Green
        llSetText("GO", <1,1,1>, 1.0);
        llSetTimerEvent(5.0);  // Change after 5 seconds
    }
    
    timer()
    {
        llSay(0, "Changing to YELLOW");
        state yellow;
    }
}

state yellow
{
    state_entry()
    {
        llSetColor(<1, 1, 0>, ALL_SIDES);  // Yellow
        llSetText("CAUTION", <0,0,0>, 1.0);
        llSetTimerEvent(2.0);  // Change after 2 seconds
    }
    
    timer()
    {
        llSay(0, "Changing to RED");
        state red;
    }
}

state red
{
    state_entry()
    {
        llSetColor(<1, 0, 0>, ALL_SIDES);  // Red
        llSetText("STOP", <1,1,1>, 1.0);
        llSetTimerEvent(5.0);  // Change after 5 seconds
    }
    
    timer()
    {
        llSay(0, "Changing to GREEN");
        state green;
    }
}

Lesson 6: Essential Events

Event Reference

Event When It Fires Common Uses
state_entry() Script starts or state changes Initialization, setup
touch_start() Avatar touches object Buttons, doors, interactive objects
touch_end() Avatar releases touch Detecting release after hold
timer() Timer interval expires Repeating actions, animations
collision_start() Object collides with something Damage systems, triggers
listen() Hears chat on channel Chat commands, communication
sensor() Detects nearby objects/avatars Security, proximity detection
changed() Object properties change Inventory changes, linking

Timer Event Example

// Clock that announces the time every 60 seconds
default
{
    state_entry()
    {
        llSetTimerEvent(60.0);  // Fire timer every 60 seconds
        llSay(0, "Clock started!");
    }

    timer()
    {
        // Get current timestamp
        string timestamp = llGetTimestamp();
        llSay(0, "Current time: " + timestamp);
    }
    
    touch_start(integer num)
    {
        // Stop the timer
        llSetTimerEvent(0.0);
        llSay(0, "Clock stopped.");
    }
}

Collision Event Example

// Collision detector
default
{
    state_entry()
    {
        llSetStatus(STATUS_PHANTOM, FALSE);  // Make sure collisions work
        llSay(0, "Collision detector active");
    }

    collision_start(integer num_detected)
    {
        // Loop through all collisions
        integer i;
        for (i = 0; i < num_detected; i++)
        {
            string name = llDetectedName(i);
            llSay(0, "Collided with: " + name);
        }
    }
}

Listen Event Example

// Simple chat command system
integer listenHandle;

default
{
    state_entry()
    {
        // Listen on channel 0 (public chat)
        listenHandle = llListen(0, "", NULL_KEY, "");
        llSay(0, "I'm listening! Say 'hello' or 'help'");
    }

    listen(integer channel, string name, key id, string message)
    {
        // Convert message to lowercase for easier matching
        message = llToLower(message);
        
        if (message == "hello")
        {
            llSay(0, "Hello, " + name + "!");
        }
        else if (message == "help")
        {
            llSay(0, "Available commands: hello, help, time");
        }
        else if (message == "time")
        {
            llSay(0, "Current time: " + llGetTimestamp());
        }
    }
}

Hands-On Exercises

Exercise 1: Personal Greeter

Task: Create a script that greets the toucher by name.

Requirements:

  • Use llDetectedName(0) to get the toucher's name
  • Say a personalized greeting like "Hello, [Name]!"
  • Bonus: Make it say different greetings for the owner vs. others
Hint

Use: string name = llDetectedName(0); and llSay(0, "Hello, " + name + "!");

Exercise 2: Color Changer

Task: Create an object that changes color each time it's touched.

Requirements:

  • Use an integer counter to track touches
  • Change to different colors: Red, Green, Blue, Yellow (cycle through)
  • Use llSetColor() function
  • Bonus: Display the current color name in floating text
Hint

Use modulo operator: colorIndex = counter % 4; to cycle through 4 colors

Exercise 3: Timer Counter

Task: Create a countdown timer that counts from 10 to 0.

Requirements:

  • Start countdown when object is touched
  • Count down every second (use timer)
  • Say each number in chat
  • Say "BLAST OFF!" when reaching 0
  • Stop the timer after blast off

Exercise 4: Door System

Task: Create a simple automatic door using states.

Requirements:

  • Two states: "closed" and "open"
  • Touch to open (make phantom + change color)
  • Auto-close after 5 seconds using timer
  • Prevent opening while already open
Hint

Use llSetStatus(STATUS_PHANTOM, TRUE); to make door passable

Exercise 5: Quiz Object

Task: Create a simple quiz that asks a question and checks the answer.

Requirements:

  • Touch to start quiz
  • Use llListen() to hear chat
  • Ask "What is 2 + 2?"
  • Check if answer is "4"
  • Say "Correct!" or "Wrong, try again!"

Challenge: Complete Vending Machine

Task: Create a vending machine that gives items.

Requirements:

  • Display menu of items when touched
  • Listen for item selection (1, 2, 3)
  • Give the selected item from inventory
  • Keep track of stock count
  • Say "Out of stock" when empty
  • Owner can say "restock" to refill

Quick Reference Guide

Essential Functions

Function Purpose Example
llSay() Public chat (20m) llSay(0, "Hello!");
llOwnerSay() Message to owner only llOwnerSay("Private msg");
llSetText() Floating text llSetText("Hi", <1,1,1>, 1.0);
llSetColor() Change color llSetColor(<1,0,0>, ALL_SIDES);
llGetOwner() Get owner's key key owner = llGetOwner();
llDetectedName() Get toucher's name string n = llDetectedName(0);
llDetectedKey() Get toucher's key key k = llDetectedKey(0);
llSetTimerEvent() Start/stop timer llSetTimerEvent(5.0);
llListen() Listen to chat llListen(0, "", NULL_KEY, "");

Script Structure Template

// Global variables here (optional)
integer myVariable = 0;
string myText = "Hello";

// Default state (required)
default
{
    state_entry()
    {
        // Initialization code
        llSay(0, "Script started!");
    }

    touch_start(integer total_number)
    {
        // Code when touched
        llSay(0, "Touched!");
    }
    
    timer()
    {
        // Code when timer fires
    }
}

// Additional states (optional)
state my_other_state
{
    state_entry()
    {
        // Code when entering this state
    }
    
    touch_start(integer total_number)
    {
        // Different behavior in this state
        state default;  // Return to default state
    }
}

Common Mistakes to Avoid

  • Forgetting semicolons: Every statement needs a ; at the end
  • Mismatched braces: Every { needs a matching }
  • Wrong data types: Can't say a number directly: llSay(0, myInteger); - must cast: llSay(0, (string)myInteger);
  • Using = instead of ==: Assignment = vs. comparison ==
  • Forgetting to save: Scripts don't work until saved!

Congratulations!

You've completed the LSL Scripting Fundamentals course!

What You've Learned

  • LSL basics and script structure
  • Variables and data types
  • Functions and events
  • Control flow (if/else)
  • States and state machines
  • Creating interactive objects

Next Steps

Continue your learning journey with these recommended courses:

  • SCR-301: Introduction to Scripting - First Interactive Object
  • LSL-202: Object Manipulation, Movement & Rotation
  • LSL-301: Advanced Scripting Techniques