Class 4 of 8

Events

Respond to what happens in-world — touches, chat, collisions, and timers.

Step 1 of 7
Step 1

What is an Event?

We covered this briefly in Class 1, but now you're ready for the full picture. LSL is an event-driven language. A script does nothing on its own — it waits. When something happens in-world, the simulator sends an event to the script, which wakes up and handles it.

An event is always written as a function inside a state block:

LSLEvent structure
default
{
    event_name(parameters)
    {
        // Code that runs when this event fires
    }
}

The parameters vary by event — some pass the number of detected avatars, some pass a message string, some pass nothing at all. Let's work through the four most essential events.

Step 2

touch_start

touch_start fires every time an avatar left-clicks your object in-world. Its parameter tells you how many avatars clicked simultaneously — almost always 1.

Inside the event, you can use llDetectedKey(0) and llDetectedName(0) to find out who clicked. The 0 is the index — the first (and usually only) detected avatar.

LSLtouch_start with detection
default
{
    touch_start(integer num_detected)
    {
        key    toucherKey  = llDetectedKey(0);
        string toucherName = llDetectedName(0);

        llOwnerSay(toucherName + " touched this object.");
        llOwnerSay("Their key: " + (string)toucherKey);
    }
}
Related touch events
touch_start
Fires once when the click begins
touch
Fires repeatedly while the mouse button is held down
touch_end
Fires once when the click is released
Step 3

listen

The listen event fires when your script hears a chat message on a channel it's registered to. First, you register with llListen. Then, whenever a matching message is heard, the event fires.

LSLRegistering and handling a listener
integer listenHandle;

default
{
    state_entry()
    {
        // Listen on channel 42 for any speaker, any message
        listenHandle = llListen(42, "", NULL_KEY, "");
        llOwnerSay("Listening on channel 42.");
        llOwnerSay("Type /42 hello in nearby chat to test.");
    }

    listen(integer channel, string name, key id, string message)
    {
        llOwnerSay(name + " said on ch." + (string)channel + ": " + message);
    }
}

llListen returns a handle — an integer you save so you can remove the listener later. The four parameters to llListen are a filter: channel, speaker name, speaker key, and message. An empty string or NULL_KEY means "match anything."

Step 4

Removing Listeners

Each active listener consumes resources on the simulator. Best practice is to remove listeners when you're done with them using llListenRemove.

LSLOne-time listener pattern
integer listenHandle;

default
{
    state_entry()
    {
        listenHandle = llListen(42, "", NULL_KEY, "");
    }

    listen(integer channel, string name, key id, string message)
    {
        llOwnerSay("Got: " + message);

        // Remove the listener after the first message
        llListenRemove(listenHandle);
        llOwnerSay("Listener removed.");
    }
}
Performance tip

Open listeners that you never remove keep running forever and contribute to sim lag. Always remove listeners you no longer need, especially in high-traffic areas.

Step 5

collision_start

collision_start fires when a physical object (or avatar) collides with your object. It only works if your object has a physical component — either your object itself is physical, or you're using a phantom sensor trigger.

LSLCollision detection
default
{
    collision_start(integer num_detected)
    {
        string collideName = llDetectedName(0);
        string collideType = "";

        // AGENT = 1, meaning the collider is an avatar
        if (llDetectedType(0) & AGENT)
        {
            collideType = "avatar";
        }
        else
        {
            collideType = "object";
        }

        llOwnerSay(collideName + " (" + collideType + ") hit this object.");
    }
}
Collision event variants
collision_start
Fires once at the moment of first contact
collision
Fires continuously while in contact
collision_end
Fires once when contact ends
Step 6

timer

The timer event is your repeating heartbeat. You set it up with llSetTimerEvent(seconds) and it fires every N seconds until you stop it with llSetTimerEvent(0).

LSLTimer countdown
integer count = 0;

default
{
    state_entry()
    {
        llSetTimerEvent(3.0);   // Fire every 3 seconds
        llOwnerSay("Timer started.");
    }

    timer()
    {
        count++;
        llOwnerSay("Tick " + (string)count);

        if (count >= 5)
        {
            llSetTimerEvent(0);   // Stop the timer
            llOwnerSay("Timer stopped after 5 ticks.");
        }
    }
}
Tip

Each script can only have one timer running at a time. Calling llSetTimerEvent again while a timer is running replaces it with the new interval.

Step 7

Combining Events

The real power of LSL comes from combining multiple events in one script. Here's a complete example that uses touch, listen, and timer together:

LSLMulti-event script
integer listenHandle;
integer active = FALSE;

default
{
    state_entry()
    {
        listenHandle = llListen(10, "", NULL_KEY, "");
        llOwnerSay("Ready. Touch to start timer. Say /10 stop to stop.");
    }

    touch_start(integer num_detected)
    {
        if (!active)
        {
            active = TRUE;
            llSetTimerEvent(2.0);
            llOwnerSay("Timer started by " + llDetectedName(0));
        }
    }

    timer()
    {
        llOwnerSay("Tick...");
    }

    listen(integer channel, string name, key id, string message)
    {
        if (message == "stop")
        {
            active = FALSE;
            llSetTimerEvent(0);
            llOwnerSay("Timer stopped by " + name);
        }
    }
}

Notice how the events all coexist neatly inside the same default state. Each one is independent but they share script-level variables like active and listenHandle.