Class 6 of 8

Loops

Repeat actions without duplicating code.

Step 1 of 7
Step 1

Why Use Loops?

Imagine you want to say a countdown from 10 to 1. Without loops you'd write ten separate llOwnerSay calls. With a loop, you write the action once and repeat it automatically.

Every loop has three parts:

Loop anatomy
Initialisation
Set up a counter or starting condition before the loop begins
Condition
Checked before (or after) each iteration. When FALSE, the loop stops.
Body
The code that runs each iteration
Increment
Update the counter so the condition eventually becomes FALSE

LSL has three loop types: while, for, and do-while. They all do the same thing in slightly different styles.

Step 2

while Loop

The while loop checks its condition before each iteration. If the condition starts as FALSE, the body never runs.

LSLwhile loop countdown
default
{
    touch_start(integer num_detected)
    {
        integer i = 5;

        while (i > 0)
        {
            llOwnerSay((string)i + "...");
            i--;   // Decrement — without this, loop runs forever!
        }

        llOwnerSay("Liftoff!");
    }
}
Danger: infinite loop

If your loop condition never becomes FALSE, the script runs forever and the simulator will eventually kill it. Always make sure your loop can end — check that the variable being tested actually changes inside the loop.

Step 3

for Loop

The for loop bundles initialisation, condition, and increment into one tidy header. It's the go-to choice when you know exactly how many times you want to loop.

LSLfor loop — sum 1 to 10
default
{
    touch_start(integer num_detected)
    {
        integer total = 0;
        integer i;

        for (i = 1; i <= 10; i++)
        {
            total += i;
        }

        llOwnerSay("Sum of 1–10 = " + (string)total);
        // Outputs: Sum of 1–10 = 55
    }
}

The three parts of the for header are separated by semicolons:

  1. i = 1 — initialise counter
  2. i <= 10 — keep going while this is true
  3. i++ — increment after each iteration
Step 4

do-while Loop

The do-while loop checks its condition after each iteration. This guarantees the body runs at least once, even if the condition is already FALSE.

LSLdo-while — always runs at least once
default
{
    state_entry()
    {
        integer attempts = 0;

        do
        {
            attempts++;
            llOwnerSay("Attempt " + (string)attempts);
        }
        while (attempts < 3);

        llOwnerSay("Finished after " + (string)attempts + " attempt(s).");
    }
}

When should you use do-while? Whenever you must perform an action at least once before checking whether to continue. A common example: validating user input where you need to collect it before you can check it.

Step 5

No break — Using Flags Instead

Most languages have a break statement that exits a loop early. LSL does not. Instead, use a flag variable to control early exit:

LSLEarly exit with a flag
default
{
    touch_start(integer num_detected)
    {
        integer i    = 0;
        integer done = FALSE;

        while (i < 100 && !done)
        {
            i++;

            if (i == 7)
            {
                llOwnerSay("Found 7! Stopping.");
                done = TRUE;   // Flag causes the while condition to fail next check
            }
        }

        llOwnerSay("Stopped at i = " + (string)i);
    }
}
Tip

LSL does have a jump / @label mechanism for gotos, but flags are cleaner and easier to read. Save jump for very specific cases.

Step 6

Loops and Performance

LSL scripts run on shared simulator hardware. A loop that takes too long can freeze your script and cause lag for everyone nearby. There are two rules:

Performance rules
Keep loops short
Hundreds of iterations are fine. Tens of thousands may cause a script-time error. If you need that many, split the work across multiple timer events.
Avoid llSleep in loops
llSleep(n) pauses your script completely for n seconds, blocking all events. Use the timer event for delays instead.
LSLTimer instead of sleep in a loop
// BAD — blocks all events for 50 seconds
// integer i;
// for (i = 0; i < 10; i++) { llOwnerSay(...); llSleep(5.0); }

// GOOD — uses the timer event
integer step = 0;

default
{
    state_entry()
    {
        llSetTimerEvent(5.0);
    }

    timer()
    {
        step++;
        llOwnerSay("Step " + (string)step);

        if (step >= 10)
        {
            llSetTimerEvent(0);
            llOwnerSay("Done.");
        }
    }
}
Step 7

Practical: Multiplication Table

Here's a practical example using a nested for loop. It generates a simple 3×3 multiplication table and prints it to owner chat:

LSLNested for loop — multiplication table
default
{
    touch_start(integer num_detected)
    {
        integer row;
        integer col;
        string  line;

        for (row = 1; row <= 3; row++)
        {
            line = (string)row + " x:  ";

            for (col = 1; col <= 3; col++)
            {
                line = line + (string)(row * col) + "  ";
            }

            llOwnerSay(line);
        }
        // Output:
        // 1 x:  1  2  3
        // 2 x:  2  4  6
        // 3 x:  3  6  9
    }
}

Nested loops are powerful — the inner loop completes fully for each single iteration of the outer loop. Just keep both loops small to stay within script time limits.