LSL-301 Advanced The School of Creation
Lead Instructor

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

LSL Advanced - Communication, HTTP & External Data (LSL-301)

Explore the immersive 3D world of Alife Virtual - Your free virtual world alternative

Explore the immersive 3D world of Alife Virtual - Your free virtual world alternative
High-resolution image (1920×1080 pixels) from Alife Virtual World School


Part of: The School of Creation

Difficulty Level: Advanced

Prerequisites: LSL-201 (Advanced Scripting) & LSL-202 (States & Memory)

Duration: 8 Weeks

Lead Instructor: Sorin Todys

Description: Master `llHTTPRequest`, `llEmail`, link messages, sensors, and create connected experiences that bridge the virtual and real worlds.

1. COURSE OVERVIEW

Welcome to LSL-301!

Welcome, scripters, to the next stage of your LSL journey! If LSL-101 was about turning on the lights and LSL-201 was about building the house, then this course, LSL-301, is about connecting that house to the entire world. We're moving beyond the boundaries of a single object or even a single region and learning how to make our creations communicate with each other, with avatars, and with the vast expanse of the internet.

This is where your scripts evolve from simple tools into dynamic, intelligent, and connected experiences. Get ready to unlock the true potential of LSL.

What You Will Master

By the end of this 8-week course, you will have a deep, practical understanding of:

  • Intra-Object Communication: Using efficient link messages to create complex, multi-part objects that work in perfect harmony.
  • Environmental Awareness: Employing sensors to make your scripts react to the presence and actions of avatars and other objects.
  • External Web Requests: Harnessing the power of llHTTPRequest to pull live data from websites and APIs into Alife Virtual.
  • Data Parsing: Handling data returned from web requests, with a focus on parsing JSON, the standard language of the web.
  • Outbound Communication: Using llEmail to send notifications and alerts from in-world directly to your inbox.
  • Asynchronous Programming: Mastering the event-driven nature of HTTP requests and responses, a critical skill for advanced scripting.

Why is This So Powerful?

Imagine a news board in your store that displays real-world headlines. Picture a security system that emails you when an unauthorized person enters your land. Think of a game that pulls a "word of the day" from an online dictionary. These aren't just fantasies; they are tangible projects you will be able to build. By connecting LSL to external data, you are no longer limited by what's already in the virtual world. Your creative palette becomes infinite.

Prerequisites Review

This is an advanced course. We expect you to be comfortable with the concepts covered in LSL-201 (Advanced Scripting) and LSL-202 (States & Memory). You should have a solid grasp of:

  • Variables, data types (especially lists and keys), and functions.
  • Control structures like if/else, for, and while loops.
  • Events and event handlers (e.g., touch_start, timer).
  • State management and the purpose of the default state.
  • Basic vector math and prim manipulation.

Your Instructor: Sorin Todys

I'm Sorin Todys, and I'll be your guide through this course. I wrote my first "Hello, Avatar" script over 20 years ago and have been immersed in LSL and virtual world creation ever since. I've built everything from simple gadgets to complex, grid-wide game systems. My passion is demystifying advanced scripting concepts and empowering creators like you to bring their most ambitious ideas to life. I'm thrilled to have you here and can't wait to see what you'll create!

2. LESSON 1: Intra-Object Communication with Link Messages

Theory: Why Not Just Chat?

When you link several prims into a single object (a "linkset"), you often need the different parts to communicate. A button prim might need to tell the main body to change color, or a door prim needs to tell its hinge to rotate. Your first instinct might be to use llSay() and llListen(). While this works, it's inefficient and "noisy." It uses public chat channels, has a limited range, and can be triggered by anyone.

A far superior method is using Link Messages. These are private, instantaneous, and highly efficient messages sent between prims within the same linkset. They are the central nervous system of any complex object.

LSL Syntax and Structure

Communication happens in two parts: the sender and the receiver.

  1. Sending the Message: You use the llMessageLinked() function.
  2. Receiving the Message: The target prim must have a link_message() event handler.

The function is defined as:

llMessageLinked(integer link_number, integer number, string message, key id)

  • link_number: Which prim to send the message to. You can use its link number (1 for the root, 2, 3, etc. for children) or use helpful constants like LINK_ROOT, LINK_SET (sends to all prims including the sender), or LINK_ALL_OTHERS.
  • number: An integer you can use as a command code or to pass numeric data.
  • message: A string for passing text data.
  • id: A key for passing a UUID, like an avatar's key.

The receiving event is:

link_message(integer sender_num, integer number, string message, key id)

  • sender_num: The link number of the prim that sent the message.
  • The other parameters directly correspond to what was sent.

Step-by-Step Tutorial: The Color-Changing Button

Let's build a simple two-prim object. A small button prim will tell a large root prim to change color when touched.

  1. Rez a cube prim. This will be our root prim.
  2. Rez a smaller sphere prim. This will be our button.
  3. Move the sphere so it's touching the side of the cube.
  4. Select the sphere first, then hold Shift and select the cube. From the build menu, click "Link." The cube should now have the yellow outline, indicating it's the root prim.
  5. Create two new scripts.

Script 1: The Sender (Place in the Sphere/Button)

This script listens for a touch and sends a message to the root prim.

// SENDER SCRIPT - Place in a child prim (the button)
// This script tells the root prim to change color when touched.

default
{
    touch_start(integer total_number)
    {
        // When this prim is touched by an avatar...
        
        // Announce in local chat what we're doing. Good for debugging.
        llSay(0, "Button touched! Sending message to root prim.");
        
        // Send a message to the root prim of this linkset.
        // LINK_ROOT is a constant that always refers to the root prim.
        // We are sending the integer '1' as a command code.
        // We are sending the string "change_color" as a command message.
        // We are sending a NULL_KEY because we don't need to pass a key.
        llMessageLinked(LINK_ROOT, 1, "change_color", NULL_KEY);
    }
}

Script 2: The Receiver (Place in the Cube/Root Prim)

This script waits for a message and changes color when it receives the correct command.

// RECEIVER SCRIPT - Place in the root prim
// This script listens for a link message and acts on it.

// Define a list of colors to cycle through.
list g_colors = [<1.0, 0.0, 0.0>, <0.0, 1.0, 0.0>, <0.0, 0.0, 1.0>, <1.0, 1.0, 0.0>];
integer g_color_index = 0; // To keep track of the current color.

default
{
    state_entry()
    {
        // Announce that the receiver is ready.
        llSay(0, "Color changer ready. Touch the button prim.");
    }

    // This event fires when a link message is received.
    link_message(integer sender_num, integer num, string str, key id)
    {
        // It's good practice to check if the message is what we expect.
        if (num == 1 && str == "change_color")
        {
            // The message is correct! Let's change the color.
            llSay(0, "Message received! Changing color.");
            
            // Get the next color from our list.
            vector next_color = llList2Vector(g_colors, g_color_index);
            
            // Set the color of all faces of this prim.
            llSetColor(next_color, ALL_SIDES);
            
            // Increment the index to get the next color next time.
            g_color_index++;
            
            // If we've gone past the end of the list, loop back to the beginning.
            if (g_color_index >= llGetListLength(g_colors))
            {
                g_color_index = 0;
            }
        }
    }
}

Now, touch the small sphere. You should see the large cube change color with each touch! You've just created a robust, efficient communication system inside your object.

3. LESSON 2: Sensing the World Around You

Theory: Giving Your Scripts Eyes

How does a door know when to open? How does a security device spot an intruder? They use sensors. A sensor is a way for a script to scan a specific area for objects or avatars that match certain criteria. It's a fundamental tool for creating interactive environments.

You can scan once with llSensor() or set up a repeating scan with llSensorRepeat(). A repeating sensor is generally more useful for environmental awareness, but it's important to use it wisely to avoid causing lag.

Advanced LSL Techniques & Functions

The main function is llSensorRepeat(string name, key id, integer type, float range, float arc, float rate)

  • name: Scan for an object with this name. Use "" for any name.
  • id: Scan for a specific avatar or object key. Use NULL_KEY for any.
  • type: This is critical. What are you looking for?
    • AGENT: Avatars.
    • ACTIVE: Objects containing running scripts.
    • PASSIVE: Objects with no scripts.
    • SCRIPTED: Any object with a script (combination of ACTIVE and PASSIVE).
    • You can combine types with a pipe: AGENT | SCRIPTED.
  • range: The maximum distance to scan, in meters (max 96m).
  • arc: The width of the scan, in radians (from 0 to PI). Think of it like a radar sweep.
  • rate: How often to scan, in seconds. Don't set this too low! 2-5 seconds is usually fine.

When the sensor finds something, it triggers the sensor() event. If it was a repeating sensor and now finds nothing, it triggers the no_sensor() event.

Code Example 1: The Polite Greeter

This script will scan for any avatar within 10 meters and say hello to the first one it finds. It will say goodbye when they leave.

// POLITE GREETER SCRIPT
// This script uses a repeating sensor to greet nearby avatars.

// We need a variable to track if we've already greeted someone,
// to avoid spamming them.
key g_greeted_avatar = NULL_KEY;

default
{
    state_entry()
    {
        // Announce that the greeter is active.
        llOwnerSay("Greeter script started. Scanning for avatars.");
        
        // Set up a repeating sensor.
        // It looks for any avatar (name="", id=NULL_KEY, type=AGENT)
        // within a 10-meter range.
        // The arc is PI, which is a 180-degree semi-circle in front of the object.
        // It scans every 3 seconds.
        llSensorRepeat("", NULL_KEY, AGENT, 10.0, PI, 3.0);
    }

    // This event fires when the sensor detects something.
    sensor(integer num_detected)
    {
        // The event gives us the number of things it found.
        // We only care about the first one detected for this example.
        key detected_key = llDetectedKey(0); // Get the key of the first detected entity.
        
        // Check if this is a new avatar we haven't greeted yet.
        if (detected_key != g_greeted_avatar)
        {
            // It's a new person! Store their key so we don't greet them again.
            g_greeted_avatar = detected_key;
            
            // Get the avatar's name using their key.
            string detected_name = llDetectedName(0);
            
            // Say hello!
            llSay(0, "Welcome to Alife Virtual, " + detected_name + "!");
        }
    }
    
    // This event fires when the repeating sensor no longer detects anything.
    no_sensor()
    {
        // If we had greeted someone before (g_greeted_avatar is not NULL_KEY)...
        if (g_greeted_avatar != NULL_KEY)
        {
            // ...then they must have left.
            llSay(0, "Goodbye! Come back soon!");
            
            // Reset the variable so we're ready to greet the next person.
            g_greeted_avatar = NULL_KEY;
        }
    }
}

4. LESSON 3: Reaching Out - llHTTPRequest

Theory: Connecting LSL to the Web

This is the game-changer. llHTTPRequest allows your LSL script to send a request to a web server on the internet and receive a response. This opens up a universe of possibilities: displaying real-time weather, fetching news headlines, connecting to a game scoreboard, interacting with social media, and so much more.

The most important concept to understand is that HTTP requests are asynchronous. This means your script does NOT pause and wait for the web server to reply. It sends the request and continues running. When the reply eventually comes back (milliseconds or seconds later), a separate event, http_response(), is triggered. You must place your logic for handling the data inside that event.

LSL Library Functions

The sending function is key llHTTPRequest(string url, list parameters, string body)

  • url: The full web address (URL) you want to contact.
  • parameters: A list of constants and values to define the request. The most common are:
    • [HTTP_METHOD, "GET"]: To retrieve data (most common).
    • [HTTP_METHOD, "POST"]: To send data.
    • [HTTP_MIMETYPE, "application/json"]: To specify the format of data you are sending or expect.
  • body: The data you want to send with a POST request. For GET requests, this is an empty string "".
  • The function immediately returns a key, which is a unique ID for this specific request.

The receiving event is http_response(key request_id, integer status, list metadata, string body)

  • request_id: The key that llHTTPRequest returned. This lets you match a response to its request.
  • status: The HTTP status code. 200 means "OK". Others like 404 (Not Found) or 500 (Server Error) indicate problems.
  • metadata: Information about the response, usually not needed for simple requests.
  • body: The actual data returned by the server! This is often a block of text, HTML, or JSON.

Real-World Example: The Daily Cat Fact Dispenser

Let's create an object that, when touched, fetches a random cat fact from a public API and displays it.

// DAILY CAT FACT DISPENSER
// This script fetches data from an external web API.

// The URL of the public API we're using.
string g_api_url = "https://catfact.ninja/fact";

// We'll store the request ID to make sure the response we get is the one we're expecting.
key g_request_id;

default
{
    state_entry()
    {
        llSetText("Click me for a cat fact!", <1,1,1>, 1.0);
    }
    
    touch_start(integer total_number)
    {
        llSetText("Fetching fact...", <1,1,0>, 1.0);
        
        // Define the parameters for our request. We are doing a "GET" request.
        list http_params = [HTTP_METHOD, "GET"];
        
        // Send the request!
        // The body is empty because we are only getting data, not sending it.
        // We store the returned key in our global variable.
        g_request_id = llHTTPRequest(g_api_url, http_params, "");
    }
    
    // This event fires when the web server responds.
    http_response(key request_id, integer status, list metadata, string body)
    {
        // First, check if this response matches our request.
        // This is important if your script makes multiple different requests.
        if (request_id == g_request_id)
        {
            // Next, check if the request was successful. Status 200 means OK.
            if (status == 200)
            {
                // Success! The 'body' variable now contains the data.
                // For this API, it's a JSON string like:
                // {"fact":"A cat's brain is more similar to a human's brain than a dog's.","length":78}
                
                // We will learn to parse this properly in the next lesson.
                // For now, let's just show the raw body.
                llOwnerSay("Raw response body: " + body);
                
                // For a slightly cleaner display, we'll parse it simply in the next lesson.
                // For now, let's just set the object text.
                llSetText("API responded. See chat for data.", <0,1,0>, 1.0);
                
                // We'll add the JSON parsing here next time!
            }
            else
            {
                // The request failed! Let's report the error.
                llSetText("Error: " + (string)status, <1,0,0>, 1.0);
                llOwnerSay("HTTP request failed with status: " + (string)status);
            }
        }
    }
}

5. LESSON 4: Parsing External Data & Sending Email

Combining Concepts: From Data to Action

Receiving a block of data from an API is only half the battle. You need to extract the specific information you want. Most modern APIs use a format called JSON (JavaScript Object Notation). LSL has a built-in function, llJsonGetValue(), to help you parse it.

We'll also learn about llEmail(), a simple but powerful function for sending information out of the virtual world to a real-world email address. It's perfect for notifications and alerts.

Part 1: Parsing JSON with `llJsonGetValue`

The function is: string llJsonGetValue(string json_text, list specifiers)