Sorin Todys - Advanced expert with 20+ years of experience in virtual worlds
All classes take place in Alife Virtual World at our dedicated Alife Virtual School region
Explore the immersive 3D world of Alife Virtual - Your free virtual world alternative
High-resolution image (1920×1080 pixels) from Alife Virtual World School
Course Code: LSL-101
Part of: The School of Creation
Difficulty: Beginner
Duration: 4 Weeks (Self-Paced)
Lead Instructor: Sorin Todys
Welcome, future creator! You are about to take your first step into the incredible world of Linden Scripting Language (LSL). LSL is the magic that brings our virtual world to life. It's the "brain" inside every object that moves, speaks, listens, or interacts with you. If you've ever wondered how a door opens automatically or how a vehicle drives, the answer is LSL.
This course, LSL-101, is designed from the ground up for absolute beginners. We assume you know nothing about programming and will guide you step-by-step from the basic concepts to writing your very first functional, interactive script.
By the end of this 4-week course, you will have a solid foundation in LSL and be able to:
integer, string, and vector.llSay), move (llSetPos), and listen (llListen).if/else statements to make decisions.Learning LSL opens up a universe of creative possibilities. It transforms you from a passive resident into an active creator. You can build anything you can imagine:
No programming experience is needed! All you need is:
Every LSL script, no matter how complex, is built on a simple foundation. Think of it like a small, obedient robot waiting for instructions. It has two primary components:
default state. For now, think of it as the script's main operational mode.The basic structure looks like this:
default
{
// This is the default state. All scripts must have it.
event_name()
{
// Code inside this event runs ONLY when the event happens.
}
}
{ } that define code blocks and the semicolons ; that end most command lines. Comments, which the script ignores, are written with // for a single line or enclosed in /* ... */ for multiple lines.
Let's write a script that says a message when it's first run and a different message when you touch it. This is the "Hello, World!" of LSL.
// My First LSL Script - A simple greeter
default
{
// The state_entry event runs once when the script is saved/reset,
// or when the object is rezzed in-world.
state_entry()
{
// llSay is a function that makes the object chat publicly.
// Channel 0 is the public chat channel everyone can see.
llSay(0, "My script is running! Touch me to get a greeting.");
}
// The touch_start event runs whenever an avatar clicks (touches) the object.
// 'integer total_number' is a variable that holds how many fingers touched the prim.
// We can ignore it for now, but it must be there.
touch_start(integer total_number)
{
// This line will only execute when the object is touched.
llSay(0, "Hello, Avatar!");
}
}
Congratulations! You have just written and executed your first working LSL script. You've mastered the fundamental workflow of all LSL scripting.
In Lesson 1, our script could only react. To make scripts more intelligent, we need to give them a memory. In programming, this memory is called a variable. A variable is simply a named container that holds a piece of data. Each variable has a data type, which tells the script what kind of information it's storing.
Here are the most common data types you'll use:
integer: A whole number (e.g., 5, -20, 1000).float: A number with a decimal point (e.g., 3.14, -0.5, 99.9).string: A piece of text, always enclosed in double quotes (e.g., "Hello World", "Sorin Todys").key: A unique identifier for an avatar or object (a UUID). It looks like "a1b2c3d4-...".vector: A set of three float numbers, often used for position, scale, or color (e.g., <10.0, 20.0, 30.0>).You declare a variable by stating its type, giving it a name, and optionally giving it an initial value. For example: integer touchCounter = 0;
Functions are the action-words of LSL. We've already used llSay(). Let's introduce a few more that are incredibly useful:
llOwnerSay(string message): This is like llSay, but the message is only visible to the object's owner. It's the #1 tool for debugging your scripts!llDetectedName(integer index): Used inside events like touch_start, this function gets the name of the avatar who triggered the event. The index is usually 0.llGetOwner(): This function returns the unique key of the avatar who owns the object.llListen(integer channel, string name, key id, string message): Tells the script to listen for chat messages on a specific channel. This is the foundation for command-based objects.Let's build on our last script. This time, instead of a generic greeting, it will greet the specific person who touches it by name.
// A personalized greeter script
// We declare a 'string' variable to hold our custom greeting message.
// This is a global variable, meaning it can be accessed from anywhere in the script.
string greetingMessage = "Welcome to my place, ";
default
{
state_entry()
{
llOwnerSay("Personalized greeter script is ready.");
}
touch_start(integer total_number)
{
// Create a local variable 'name' of type string.
// It will hold the name of the avatar who touched the object.
string toucherName = llDetectedName(0);
// Concatenate (join) our greeting message with the toucher's name.
// The '+' operator joins strings together.
string fullMessage = greetingMessage + toucherName + "!";
// Say the complete, personalized message in public chat.
llSay(0, fullMessage);
}
}
How it works: When you touch the object, the touch_start event fires. The script immediately calls llDetectedName(0) to get your avatar's name and stores it in the toucherName variable. Then, it uses the + operator to combine the predefined greetingMessage with the detected name, creating a personalized sentence. Finally, it uses llSay to speak the result.
So far, our scripts react the same way every time. The true power of scripting comes from making decisions. LSL does this using control flow statements, the most important of which is the if statement.
An if statement checks if a condition is true. If it is, the code inside its curly braces is executed. If not, the code is skipped.
if (some_condition_is_true)
{
// ...do this code.
}
You can create conditions using comparison operators:
== : Is equal to!= : Is NOT equal to> : Is greater than< : Is less than>= : Is greater than or equal to<= : Is less than or equal toYou can also provide an alternative action using else, for when the condition is false. For even more complex choices, you can chain conditions with else if.
Let's create a script that does one thing if the owner touches it, and something else if a stranger touches it. This is the basis for secure doors, control panels, and more.
key.
// An owner-only security script
// Global variable to store the owner's key.
key ownerKey;
default
{
state_entry()
{
// When the script starts, get the owner's key and store it.
ownerKey = llGetOwner();
llOwnerSay("Security system activated. Owner key is: " + (string)ownerKey);
}
touch_start(integer total_number)
{
// When touched, get the key of the avatar who touched it.
key toucherKey = llDetectedKey(0);
// This is our decision!
// We compare the toucher's key with the stored owner's key.
if (toucherKey == ownerKey)
{
// IF the keys match, run this code.
llOwnerSay("Access Granted. Welcome, Owner!");
}
else
{
// ELSE (if the keys do NOT match), run this code instead.
llSay(0, "Access Denied. This panel is for owner-use only.");
llOwnerSay("Alert: Unauthorized access attempt by avatar key: " + (string)toucherKey);
}
}
}
How it works: In state_entry, the script figures out who the owner is and saves their key. This only happens once. Then, in touch_start, it gets the key of whoever touched it. The if statement is the crucial part: it compares the two keys. If they are identical (==), the "Access Granted" message is shown. If they are different, the else block is triggered, issuing a public denial and a private alert to the owner.
As scripts get more complex, it's useful to separate their behaviors into different modes. For example, a light can be "on" or "off". These modes are called states in LSL. A script can have many states, but it can only be in one at a time. We've been using the default state all along, but now we'll create our own.
You define a new state with the state keyword, followed by a name. You can then switch between states using the command: state new_state_name;
Each state can have its own set of events. For example, a touch_start event in the "off" state might do something different from a touch_start in the "on" state.
This script combines everything we've learned: variables, functions, control flow, and multiple states. It will create a prim that acts as a light switch, toggling between an "on" and "off" state when touched.
We will use the powerful llSetPrimitiveParams function to change the object's color and enable its light.
// A multi-state toggle light switch
// This is the "off" state. Since it's the first state in the script,
// it's where the script will start. We've named it 'off' instead of 'default'
// for clarity, but you could use 'default' as the off state.
state_default
{
state_entry()
{
// When entering the 'off' state:
llOwnerSay("Light is now OFF.");
// Set the prim color to dark grey and turn off the light feature.
llSetPrimitiveParams([ PRIM_FULLBRIGHT, ALL_SIDES, FALSE,
PRIM_COLOR, ALL_SIDES, <0.2, 0.2, 0.2>, 1.0 ]);
}
touch_start(integer total_number)
{
// When touched in the 'off' state, switch to the 'on' state.
state on;
}
}
// This is our custom "on" state.
state on
{
state_entry()
{
// When entering the 'on' state:
llOwnerSay("Light is now ON.");
// Set the prim to be full-bright (glow), set its color to yellow, and turn on the light.
llSetPrimitiveParams([ PRIM_FULLBRIGHT, ALL_SIDES, TRUE,
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 0.0>, 1.0,
PRIM_POINT_LIGHT, TRUE, <1.0, 1.0, 0.0>, 1.0, 10.0, 0.75 ]);
}
touch_start(integer total_number)
{
// When touched in the 'on' state, switch back to the 'off' state.
state state_default;
}
}
How it works:
state_default. The state_entry event immediately runs, making the prim dark and ensuring the light is off. The script now waits.touch_start event in state_default fires. Its only job is to issue the command state on;.on state. This triggers the state_entry event inside the on state. This event's code runs, making the prim bright yellow and casting light. The script now waits in the on state.touch_start event in the on state fires, which sends it back to state_default, and the cycle repeats.The best way to learn is by doing! Try to complete these challenges. Create a new script for each one.