HELP
Scripting Intermediate Published: 2026-03-30  |  โ† Back to School

CLASS 60: Scripting Interactive Furniture (Sit Targets)

Scripting Interactive Furniture (Sit Targets) โ€” Alife Virtual School

Scripting Interactive Furniture (Sit Targets) โ€” Free class in Alife Virtual School

Welcome, creators, to Alife Virtual School! In today's master-level workshop, we're diving deep into one of the most fundamental skills for building an immersive metaverse: Scripting Interactive Furniture (Sit Targets). Forget static, lifeless objects. We're going to teach you how to command avatars, positioning them with precision to create everything from cozy armchairs to complex, multi-person interactive experiences. This skill is the bedrock of social spaces, engaging homes, and dynamic role-playing environments. In a free 3D world like Alife Virtual, where your imagination is the only currency, mastering this technique will set your creations apart and elevate them from simple objects to truly interactive art.

The Alife Advantage: Create Without Cost

Before we write a single line of code, let's talk about the economic reality of creation in virtual worlds. Building high-quality, interactive furniture in other platforms can be a costly endeavor. You're often nickel-and-dimed for every texture, every mesh model, and every animation you upload. Worse, the very land you build on comes with staggering monthly fees. In Alife Virtual, we've removed those barriers entirely, fostering a truly free virtual economy.

Let's compare the cost of developing the project in this tutorial:

Development Aspect Alife Virtual Cost Typical "Second Life Alternative" Cost
Private Island (65,536 sqm) $0 / Forever ~$300 USD / month
Mesh Chair Model Upload $0 (Unlimited) Upload fees + Land Impact fees
Texture Upload (Wood, Fabric) $0 (Unlimited) L$10 (~$0.04) per texture
Custom Sit Animation Upload $0 (Unlimited) L$10 (~$0.04) per animation
Total First Month Cost $0 $300+

With Alife Virtual, you get a FREE Private Island to build, test, and showcase your work. You get FREE unlimited uploads for all your assets. You even start with a FREE pro mesh avatar. We are a true Open Simulator platform, fully compatible with the popular Firestorm Viewer, dedicated to empowering creators, not billing them. Now, let's use that freedom to build something amazing.

What You Will Learn in This Class

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

Prerequisites

This is an intermediate LSL scripting class. Before you begin, you should be comfortable with the following concepts:

Part 1: The Foundation - A Simple Sit Target

At its core, a "sit target" is an invisible point in space defined by an offset and rotation relative to a prim's center. When an avatar "sits" on an object, the server moves them to this exact point. Let's start with a single prim chair.

Step 1: Create Your Object and Script

  1. In-world, right-click the ground and choose Create to rez a simple cube prim.
  2. Stretch and shape it to resemble a basic chair or stool.
  3. With the object selected, go to the Content tab in your edit window.
  4. Click New Script. This will create and open a new script named "New Script". Rename it to something descriptive, like simple_sit_script.

Step 2: The Basic Sit Script

Double-click your new script to open the editor. Delete the default content and replace it with this:


// Simple Sit Target Script - Alife Virtual School Class 60

default
{
    state_entry()
    {
        // Set the sit target.
        // llSitTarget(<offset>, <rotation>);
        // <0.0, 0.0, 0.5> is 0.5 meters directly above the prim's center.
        // ZERO_ROTATION means the avatar will face the same direction as the prim.
        llSitTarget(<0.0, 0.0, 0.5>, ZERO_ROTATION);

        // Optional: Change the hover text when someone sits.
        llSetSitText("Relaxing");
    }
}

Step 3: Understanding the Code

Save the script. Now, right-click your chair and choose Sit Here. Your avatar should snap to the position you defined! If they are floating too high or sunk too low, adjust the Z-value (the third number) in the llSitTarget vector and save again. The script will reset, and you can try sitting again.

Pro Tip: The "Helper Prim" Technique
Getting the perfect offset and rotation can be tricky. A classic method is to use a helper prim. Rez a small cube, color it brightly, and set its transparency to 50%. Manually position this helper prim exactly where you want your avatar's hips to be. Then, link it to your main chair (selecting the chair last to make it the root). Use a separate script or the build tool's info to get the helper prim's local position and rotation. These values are your perfect offset and rotation for llSitTarget! You can delete the helper prim afterward.

Part 2: Multi-Prim Objects and llSetLinkPrimitiveParamsFast

What if your furniture is complex? A sofa is made of many prims. The llSitTarget function only works on the root prim of a linked set. To set a sit target on a child prim (like the second cushion of a bench), we need a more powerful tool.

Enter llSetLinkPrimitiveParamsFast.

Step 1: Build a Two-Prim Bench

  1. Rez two cube prims. Position them side-by-side to form a simple bench.
  2. Select both prims, ensuring you select the one you want to be the "root" prim last. The last selected prim will have a yellow outline, while the others will be blue.
  3. Press Ctrl+L to link them. The entire object will now be outlined in yellow. The root is link number 1, and the child prim is link number 2.
  4. Create a new script in the root prim's inventory called linked_sit_script.

Step 2: Scripting a Child Prim Sit Target

We want to place a sit target on the second prim (link number 2). Here's how:


// Linked Sit Target Script - Alife Virtual School Class 60

default
{
    state_entry()
    {
        // Define the sit target parameters for the child prim.
        vector sit_offset = <0.0, 0.0, 0.25>; // Adjust as needed for your build
        rotation sit_rotation = ZERO_ROTATION;

        // Apply the sit target to LINK NUMBER 2.
        // PRIM_SIT_TARGET is the rule we are applying.
        llSetLinkPrimitiveParamsFast(2, [PRIM_SIT_TARGET, sit_offset, sit_rotation]);

        // It's good practice to also set a sit target on the root prim,
        // even if it's "off" the object, to avoid default sit behavior.
        llSitTarget(<0.0, 0.0, -5.0>, ZERO_ROTATION); // Puts it 5m below ground
    }
}

Step 3: Understanding the Code

Save the script. Now, right-click the second cushion of your bench. You should see the "Sit Here" option, and selecting it will place your avatar on that specific prim.

Part 3: The Holy Grail - A Multi-Seat, Multi-Pose Sofa

Now we combine everything to build a truly dynamic piece of furniture. We will create a three-seat sofa where a user can touch it to sit in the next available spot, and each spot will have its own sitting animation.

Step 1: The Build and Asset Prep

  1. Build a three-cushion sofa. Link all the prims together, making the central base prim the root (link number 1). Let's assume the cushions are link numbers 2, 3, and 4.
  2. Find or create three sitting animations. For this example, we'll call them "sit_casual", "sit_reading", and "sit_crosslegged".
  3. Place a new script (e.g., sofa_controller) and the three animation files into the root prim's inventory.

Step 2: The Advanced Controller Script

This script is more complex. It needs to track seats, handle users sitting and standing, and manage animation permissions. Read the comments carefully.


// Multi-Seat Sofa Controller - Alife Virtual School Class 60

// --- CONFIGURATION ---
list g_sit_targets_prims = [2, 3, 4]; // Link numbers of the seat prims
list g_sit_targets_offsets = [<0,0,0.2>, <0,0,0.2>, <0,0,0.2>]; // One offset per prim
list g_sit_targets_rots = [ZERO_ROTATION, ZERO_ROTATION, ZERO_ROTATION]; // One rotation per prim
list g_sit_animations = ["sit_casual", "sit_reading", "sit_crosslegged"]; // Animations from inventory

// --- GLOBALS ---
list g_seat_status; // Will store the KEY of the avatar in each seat, or NULL_KEY if empty.
integer g_num_seats;

// --- FUNCTIONS ---
f_init()
{
    g_num_seats = llGetListLength(g_sit_targets_prims);
    g_seat_status = []; // Clear the list
    integer i;
    for (i = 0; i < g_num_seats; ++i)
    {
        // Initialize all seats as empty
        g_seat_status += [NULL_KEY];

        // Set up the sit target for each prim using SLPPF
        integer link_num = llList2Integer(g_sit_targets_prims, i);
        vector offset = llList2Vector(g_sit_targets_offsets, i);
        rotation rot = llList2Rot(g_sit_targets_rots, i);
        llSetLinkPrimitiveParamsFast(link_num, [PRIM_SIT_TARGET, offset, rot, PRIM_SIT_TARGET_VOLUME, <0.5, 0.5, 0.5>]);
    }
    // Set the text to show how many seats are free
    llSetText("Click to Sit (" + (string)g_num_seats + " seats free)", <1,1,1>, 1.0);
}

// --- EVENTS ---
default
{
    state_entry()
    {
        f_init();
    }

    on_rez(integer start_param)
    {
        llResetScript();
    }

    touch_start(integer total_number)
    {
        key toucher = llDetectedKey(0); // Who touched the object?

        // Check if the toucher is already sitting
        if (llListFindList(g_seat_status, [toucher]) != -1)
        {
            // Avatar is already sitting, so let's make them stand up
            llUnSit(toucher);
            return;
        }

        // Find the first available seat
        integer free_seat_index = llListFindList(g_seat_status, [NULL_KEY]);

        if (free_seat_index == -1)
        {
            llSay(0, "Sorry, this sofa is full.");
            return;
        }

        // A seat is free! Let's sit the avatar there.
        // We use llAvatarOnSitTarget() which returns the key of the sitter.
        // This is better than llSitOnLink because it respects the defined sit target.
        llAvatarOnSitTarget(toucher, llList2Integer(g_sit_targets_prims, free_seat_index));
    }

    changed(integer change)
    {
        // This event is key! It fires when an avatar sits or stands.
        if (change & CHANGED_LINK)
        {
            integer i;
            integer free_seats_count = 0;
            for (i = 0; i < g_num_seats; ++i)
            {
                integer link_num = llList2Integer(g_sit_targets_prims, i);
                key sitter = llGetLinkKey(link_num); // Get key of avatar on this link

                if (sitter != NULL_KEY) // Seat is now occupied
                {
                    if (llList2Key(g_seat_status, i) == NULL_KEY) // Was it previously empty?
                    {
                        // A new person just sat down. Store their key.
                        g_seat_status = llListReplaceList(g_seat_status, [sitter], i, i);

                        // Request permission to animate them
                        llRequestPermissions(sitter, PERMISSION_TRIGGER_ANIMATION);
                    }
                }
                else // Seat is now empty
                {
                    if (llList2Key(g_seat_status, i) != NULL_KEY) // Was it previously occupied?
                    {
                        // Person stood up. Stop their animation and clear the seat.
                        key old_sitter = llList2Key(g_seat_status, i);
                        llStopAnimation(llList2String(g_sit_animations, i), old_sitter);
                        g_seat_status = llListReplaceList(g_seat_status, [NULL_KEY], i, i);
                    }
                    free_seats_count++;
                }
            }
            llSetText("Click to Sit (" + (string)free_seats_count + " seats free)", <1,1,1>, 1.0);
        }
    }

    run_time_permissions(integer perm)
    {
        if (perm & PERMISSION_TRIGGER_ANIMATION)
        {
            // We have permission! Now find which avatar gave it and animate them.
            integer i;
            for (i = 0; i < g_num_seats; ++i)
            {
                key sitter = llList2Key(g_seat_status, i);
                // Check if this sitter is the one who just granted permission
                if (llGetPermissionsKey() == sitter)
                {
                    // Stop any other animations they might be playing
                    llStopAnimation("sit", sitter);
                    // Start our custom animation
                    llStartAnimation(llList2String(g_sit_animations, i), sitter);
                }
            }
        }
    }
}

Common Mistake: Incorrect Link Numbers
The most frequent error in multi-prim builds is getting the link numbers wrong. If your script isn't working, use a simple debug script that, on touch, does llSay(0, (string)llGetLinkNumber());. Place this script in each prim of your linkset. When you click a prim, it will tell you its link number, allowing you to verify the numbers in your g_sit_targets_prims list.

Code Breakdown

Advanced Applications & Ideas

Mastering this system unlocks a vast range of creative possibilities:

Practice Exercise

Your assignment is to solidify your new skills.

  1. Build a four-person picnic table.
  2. The table itself should be the root prim. The four seats should be child prims.
  3. Modify the sofa script to work for four seats.
  4. Give each seat a unique animation (you can use the same one four times if you don't have many).
  5. Add a feature: When an avatar touches a seat that is already occupied, have the script use llInstantMessage to send a message to the toucher saying, "Sorry, this seat is taken by [Name of Sitter]." You will need to use llKey2Name() to get the sitter's name from their key.

Frequently Asked Questions (FAQ)

1. Why is my avatar floating above or sinking into the furniture?
Your sit target's offset vector is incorrect. The Z value (the third number) is the most common culprit. A positive value moves the avatar up; a negative value moves them down. Tweak this value in small increments (e.g., 0.05) until the position is perfect.
2. What's the difference between llSitTarget and llAvatarOnSitTarget?
llSitTarget and llSetLinkPrimitiveParamsFast are used to define where the sit position is. They set up the target. llAvatarOnSitTarget is an action; it sends a specific avatar to a sit target that has already been defined. You use the former in setup and the latter in response to a user action like a touch.
3. My animation isn't playing when I sit. What's wrong?
There are three common reasons:
  1. Permissions: The user must click "Yes" on the permission request dialog. Your script must correctly handle the run_time_permissions event.
  2. Inventory: The animation file must be inside the object's inventory, and its name must exactly match the name in your script (it's case-sensitive).
  3. Built-in AO: The user's own Animation Overrider (AO) might be overriding your script's animation. A well-written script will stop the default "sit" animation (as ours does) which helps, but some AOs can be aggressive. This is a universal challenge in LSL scripting.
4. Can I have multiple animations per seat?
Absolutely! Instead of playing an animation on sit, you can have the changed event trigger a llDialog menu for the sitter, offering them a list of animations. When they make a selection, the dialog event will fire, and you can play the chosen animation.
5. Why use this instead of just linking invisible "pose balls"?
This method is vastly superior. It's more efficient, using a single script instead of many. It's cleaner, with no ugly transparent balls to select or bump into. It provides a seamless user experience ("Touch the sofa to sit") instead of a confusing one ("Which of these invisible things do I sit on?"). It is the modern, professional standard for creating interactive furniture in any Open Simulator grid.

Your Creative Journey Starts Now. For Free.

You've just learned a technique that is fundamental to creating a living, breathing virtual world. In other metaverses, the cost of the virtual land and asset uploads to practice this skill would already be setting you back hundreds of dollars. Here, in Alife Virtual, it costs you nothing.

This is your invitation. Stop paying to create. Stop being a renter in someone else's world. Become an owner. Become a pioneer in a metaverse built on the principle of creative freedom.

Join Alife Virtual today, claim your FREE 65,536 sqm private island, and start building your dreams with unlimited uploads and zero fees. The world is waiting for what you'll create.


๐ŸŽ“ Ready to Practice In-World?

Get your FREE island and practice everything you just learned โ€” no credit card, no monthly fees.

Claim Your Free Island Now โ†’

No credit card required ยท Takes 2 minutes ยท Your island is FREE forever


Published: 2026-03-30 ยท Difficulty: Intermediate ยท Category: Scripting  |  Questions? Contact us  |  โ† Back to School