skimi3d Grid Log in Visit the grid
All scripts
Doors and ways in

Sliding Door

A door that slides aside when touched and slides back on its own. The barn door, the shop shutter, the pocket door that vanishes into the wall.

Goes inThe door leaf, on its own or linked
Setup9 to set
Code228 lines
Open toOpen to everyone

What it does

Touching the door slides it along its own body and starts a clock; when the clock runs out it slides back. By default the distance is read as a fraction of the door's own width, so a door set up once keeps working after you resize it: make it twice as wide and it slides twice as far, ending up exactly beside the hole it was filling. Every position is measured from the shut one rather than added to the last, so the door cannot walk away from its frame over an afternoon of use.

What you can build with it

Barn doors, shop shutters, lift doors, pocket doors that disappear into a wall, a hatch that pulls back into a hull. It is the sister of Swing Door and takes the same settings, so once you have set up one you already know the other. Set WHO_MAY_OPEN to 2 and it opens for your group, which is the usual arrangement for a workshop or a back room.

How to use it

1 Build the door leaf as its own prim and stand it in the doorway, shut.
2 If you want it attached to a building, select the door first and the building last, then link. The last thing you select becomes the root, and the door needs to be a child.
3 Drop this script into the door leaf.
4 Touch it. If it slides into the floor instead of into the wall, change DIRECTION; the numbers are read in the door's own body, not the region's.
5 If it slides too far or not far enough, change DISTANCE. 1.0 is one door width, 0.9 leaves it slightly overlapping the frame.

Settings you can change

9

Edit these at the top of the script. Everything works on the defaults · change them only when you need to.

DIRECTION Which way it slides, read in the door's own body rather than the region's. The default slides it along its own X. If the door disappears into the floor, this is the line to change. <1.0, 0.0, 0.0>
DISTANCE How far it slides. Read as a fraction of the door's own width by default, so 1.0 puts it exactly beside the hole it was filling and 0.9 leaves it slightly overlapping the frame. 1.0
IN_METRES Read DISTANCE as plain metres instead of as a fraction. FALSE is the friendlier setting, because a door set up as a fraction survives being resized. FALSE
SLIDE_TIME How long the slide takes, in seconds. 0.0 snaps it aside. If the slide looks choppy, raise this rather than looking for a step count. 1.2
AUTO_CLOSE Seconds it stays open before sliding back. 0.0 means it stays open until someone touches it again. 8.0
WHO_MAY_OPEN 0 for everyone, 1 for the owner alone, 2 for anyone wearing the same group tag as the door. 0
SOUND_OPEN Name of a sound in the door's Contents, played as it opens. Empty means a silent door. (empty)
SOUND_SHUT Name of a sound in the door's Contents, played as it shuts. (empty)
VOLUME How loud those sounds are, from 0.0 to 1.0. 0.5
The code LSL · 228 lines · updated 8 Sep 2026 Download .lsl
// =====================================================================
//  Sliding Door
//  skimi3d script library · https://grid.skimi3d.com/scripts/sliding-door
//
//  A door that slides aside when touched and slides back on its own.
//  The barn door, the shop shutter, the lift door, the pocket door that
//  disappears into the wall.
//
//  It is the sister of Swing Door and takes the same shape, so once you
//  have set up one you know the other. The difference is that this one
//  moves in a straight line and never turns.
//
//  Plain LSL. Works on a lone prim and on a child prim in a linkset.
// =====================================================================


// ---------------------------------------------------------------------
//  Settings
// ---------------------------------------------------------------------

// Which way it slides, in the door's own body. <1.0, 0.0, 0.0> slides
// it along its own X. If the door disappears into the floor instead of
// into the wall, this is the line to change.
vector DIRECTION = <1.0, 0.0, 0.0>;

// How far it slides. 1.0 means "its own width along that direction",
// so the door ends up exactly beside the hole it was filling. Use 0.9
// if you want it to stay slightly overlapping, or a plain number of
// metres by setting IN_METRES below.
float DISTANCE = 1.0;

// Read DISTANCE as metres instead of as a fraction of the door.
// FALSE is the friendlier setting: a door set up as a fraction still
// works after you resize it.
integer IN_METRES = FALSE;

// How long the slide takes, in seconds. 0.0 snaps it aside.
float SLIDE_TIME = 1.2;

// Seconds it stays open before sliding back. 0.0 means it stays open
// until someone touches it again.
float AUTO_CLOSE = 8.0;

// Who may open it.
//   0 = everyone
//   1 = only the owner
//   2 = only people wearing the same group tag as the door
integer WHO_MAY_OPEN = 0;

// Sounds. Drop a sound into the door's Contents and write its name
// here. Leave empty for a silent door.
string SOUND_OPEN = "";
string SOUND_SHUT = "";
float  VOLUME     = 0.5;


// ---------------------------------------------------------------------
//  Everything below here is the machinery
// ---------------------------------------------------------------------

// One step every tenth of a second, and not one step more.
//
// OpenSim gathers object updates and sends them out in bundles, about
// eleven times a second. Writing the door's position more often than
// that does not make the slide smoother, because the extra positions
// never reach anyone's viewer. It only costs the region work. If the
// slide looks choppy, give it more TIME, not more steps.
float STEP = 0.1;

integer gOpen   = FALSE;   // is it standing open right now?
integer gMoving = FALSE;   // mid-slide, ignore further touches
vector   gShutPos;         // where it sits when shut
rotation gShutRot;         // how it sits when shut
vector   gTravel;          // the whole journey, in the parent's frame


// Put the door at some fraction of its travel. 0.0 is shut, 1.0 is
// fully open.
place(float f)
{
    // Always measured from the shut position, never from the last step.
    // A door that added a little each time would walk away from its
    // frame over an afternoon of use, a few millimetres at a time.
    llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, gShutPos + gTravel * f]);
}


slide(integer open)
{
    gMoving = TRUE;

    integer steps = (integer)(SLIDE_TIME / STEP);
    if (steps > 1)
    {
        integer i;
        for (i = 1; i < steps; ++i)
        {
            float f = (float)i / (float)steps;
            if (!open) f = 1.0 - f;
            place(f);
            llSleep(STEP);
        }
    }

    // Always land exactly on the end state, so a door left on the
    // second-to-last step cannot sit a finger's width ajar.
    float end = 0.0;
    if (open) end = 1.0;
    place(end);

    gOpen   = open;
    gMoving = FALSE;
}


play(string name)
{
    if (name == "") return;

    if (llGetInventoryType(name) != INVENTORY_SOUND)
    {
        // The owner can fix this. A visitor cannot, and does not need
        // to hear about it.
        llOwnerSay("There is no sound called \"" + name + "\" in my Contents.");
        return;
    }

    llPlaySound(name, VOLUME);
}


integer mayOpen(key who)
{
    if (WHO_MAY_OPEN == 1) return who == llGetOwner();
    if (WHO_MAY_OPEN == 2) return llSameGroup(who);
    return TRUE;
}


default
{
    state_entry()
    {
        // The door learns its shut position from wherever it stands at
        // this moment. Put it where it belongs BEFORE you drop the
        // script in.
        list p = llGetLinkPrimitiveParams(LINK_THIS, [PRIM_POS_LOCAL, PRIM_ROT_LOCAL]);
        gShutPos = llList2Vector(p, 0);
        gShutRot = llList2Rot(p, 1);

        // DIRECTION is given in the door's own body, so that a door
        // mounted at an angle still slides along itself rather than
        // along the region's axes. Turned into the parent's frame here,
        // once, because that is the frame we can actually set.
        vector dir = llVecNorm(DIRECTION);
        float  far = DISTANCE;

        if (!IN_METRES)
        {
            // A fraction of the door, measured along the direction it
            // travels. This is what keeps the setting true after a
            // resize: a door twice as wide slides twice as far.
            vector s = llGetScale();
            vector along = <dir.x * s.x, dir.y * s.y, dir.z * s.z>;
            far = DISTANCE * llVecMag(along);
        }

        gTravel = (dir * far) * gShutRot;

        gOpen   = FALSE;
        gMoving = FALSE;
        llSetTimerEvent(0.0);
    }

    touch_start(integer total)
    {
        if (gMoving) return;

        key who = llDetectedKey(0);
        if (!mayOpen(who))
        {
            llRegionSayTo(who, 0, "This door does not open for you.");
            return;
        }

        llSetTimerEvent(0.0);

        if (gOpen)
        {
            play(SOUND_SHUT);
            slide(FALSE);
            return;
        }

        play(SOUND_OPEN);
        slide(TRUE);

        if (AUTO_CLOSE > 0.0) llSetTimerEvent(AUTO_CLOSE);
    }

    timer()
    {
        llSetTimerEvent(0.0);
        if (!gOpen) return;

        play(SOUND_SHUT);
        slide(FALSE);
    }

    on_rez(integer start)
    {
        // A rezzed copy stands somewhere new, so its idea of shut is
        // worthless. Learn it again.
        llResetScript();
    }

    changed(integer what)
    {
        // Linking or unlinking changes what a local position means, so
        // the door has to measure itself again. Only when it is shut and
        // still, though: reset it mid-slide and it would take its
        // half-open position for the shut one and never close properly.
        if (what & CHANGED_LINK)
        {
            if (!gOpen && !gMoving) llResetScript();
        }
    }
}

Copy it, make a new script inworld, paste over what is there and save. Nothing here phones home.