skimi3d Grid Log in Visit the grid
All scripts
Light a place

Flicker Flame

A light that will not sit still. The candle in a window, the torch on a wall, the campfire, the failing lamp in a corridor.

Goes inThe candle, torch or fire
Setup10 to set
Code171 lines
Open toOpen to everyone

What it does

It moves the brightness of a light about at random, within a range you set, and lets the reach and the glow waver along with it rather than on their own, so the pool of light on the floor breathes with the flame instead of arguing with it. The brightness is clamped at both ends, which matters more than it sounds: a flame allowed past full spends part of its life stuck at the ceiling, and that reads as a stutter rather than a flame. When it is put out, the clock stops as well.

What you can build with it

Candles, wall torches, a campfire, a forge, a fireplace, a paraffin lamp, a failing fluorescent tube in a corridor nobody has fixed. A steady light reads as electric even when it is orange; the unsteadiness is the entire effect.

How to use it

1 Drop this script into the candle, the torch or whatever should burn.
2 Set COLOUR. Warm orange is a candle, a redder one is a hot fire, a pale blue is a failing tube.
3 Set UNSTEADINESS. 0.25 is a candle in a still room, 0.6 is a fire in the wind, 1.0 is a fault.
4 If the flame should glow but the candlestick should not, put the flame's face number into FACE.
5 Read the note at the foot of the file before you fill a tavern with these.

Settings you can change

10

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

COLOUR The colour of the flame. Warm orange for a candle or torch, redder for a hot fire, pale blue for a failing fluorescent tube. <1.0, 0.70, 0.30>
BRIGHTNESS The brightness it hovers around, from 0.0 to 1.0. 0.6
UNSTEADINESS How far it strays, as a share of the brightness. 0.25 is a candle in a still room, 0.6 is a fire in the wind, 1.0 is a fault. 0.30
RADIUS How far the light reaches, in metres. 6.0
SPEED Seconds between changes. This is the dial that costs something. 0.25 is plenty for a candle; below 0.1 you pay for changes nobody is ever told about. 0.25
FACE Which face glows with the flame. -1 is every face; a face number lets the flame glow while the candlestick does not. -1
GLOW How much that face glows at full brightness, from 0.0 to 1.0. 0.12
START_LIT Whether it is burning as soon as it is rezzed. TRUE
TOUCH_TO_TOGGLE Touch to blow it out and light it again. TRUE
WHO_MAY_TOUCH 0 for everyone, 1 for the owner alone, 2 for anyone wearing the same group tag. 0
The code LSL · 171 lines · updated 8 Sep 2026 Download .lsl
// =====================================================================
//  Flicker Flame
//  skimi3d script library
//  https://grid.skimi3d.com/scripts/flicker-flame
//
//  A light that will not sit still. The candle in a window, the torch
//  on a wall, the campfire, the forge, the failing lamp in a corridor
//  nobody has fixed.
//
//  A steady light reads as electric even when it is orange. The
//  unsteadiness is the whole effect, and it costs more than a steady
//  light does, so the settings are honest about how much you are
//  spending.
//
//  Plain LSL.
// =====================================================================


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

// The colour of the flame.
//   <1.0, 0.70, 0.30>  candle or torch
//   <1.0, 0.45, 0.12>  a hot fire
//   <0.75, 0.85, 1.0>  a failing fluorescent tube
vector COLOUR = <1.0, 0.70, 0.30>;

// The brightness it hovers around, from 0.0 to 1.0.
float BRIGHTNESS = 0.6;

// How far it strays, as a share of the brightness. 0.25 is a candle in
// a still room; 0.6 is a fire in the wind; 1.0 is a fault.
float UNSTEADINESS = 0.30;

// How far the light reaches, in metres.
float RADIUS = 6.0;

// Seconds between changes. This is the dial that costs something.
// 0.25 is plenty for a candle. Below about 0.1 you are paying for
// changes that never reach anybody, because the region only sends
// updates around eleven times a second whatever a script does.
float SPEED = 0.25;

// Which face glows with the flame. -1 is every face, and a face number
// lets the flame glow while the candlestick does not.
integer FACE = -1;

// How much that face glows at full brightness, from 0.0 to 1.0.
float GLOW = 0.12;

// Is it burning as soon as it is rezzed?
integer START_LIT = TRUE;

// Touch to blow it out and light it again.
integer TOUCH_TO_TOGGLE = TRUE;

// Who may touch it. 0 = everyone, 1 = only the owner, 2 = same group.
integer WHO_MAY_TOUCH = 0;


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

integer gLit = FALSE;


flicker()
{
    // A share above and below the brightness, never below nothing and
    // never above full. Clamping matters: a flame that goes past 1.0
    // spends part of its life at the ceiling and stops flickering
    // there, which reads as a stutter rather than a flame.
    float swing = BRIGHTNESS * UNSTEADINESS;
    float b = BRIGHTNESS + llFrand(swing * 2.0) - swing;
    if (b < 0.0) b = 0.0;
    if (b > 1.0) b = 1.0;

    // The reach wavers with the brightness rather than on its own, so
    // the pool of light on the floor breathes with the flame instead of
    // arguing with it.
    float r = RADIUS * (0.85 + (b / BRIGHTNESS) * 0.15);

    float g = GLOW * (b / BRIGHTNESS);
    if (g > 1.0) g = 1.0;

    // Both in one write. Two calls would be two updates for one moment
    // of the flame, and the region gathers them anyway.
    llSetLinkPrimitiveParamsFast(LINK_THIS,
        [PRIM_POINT_LIGHT, TRUE, COLOUR, b, r, 0.6,
         PRIM_GLOW, FACE, g]);
}


light(integer on)
{
    gLit = on;

    if (on)
    {
        llSetTimerEvent(SPEED);
        flicker();
        return;
    }

    // Out means out: the clock stops too. A script still ticking for a
    // light nobody can see is the most common way a region ends up
    // busy for no reason.
    llSetTimerEvent(0.0);
    llSetLinkPrimitiveParamsFast(LINK_THIS,
        [PRIM_POINT_LIGHT, FALSE, COLOUR, 0.0, 1.0, 1.0,
         PRIM_GLOW, FACE, 0.0]);
}


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


default
{
    state_entry()
    {
        light(START_LIT);
    }

    timer()
    {
        flicker();
    }

    touch_start(integer total)
    {
        if (!TOUCH_TO_TOGGLE) return;

        key who = llDetectedKey(0);
        if (!mayTouch(who))
        {
            llRegionSayTo(who, 0, "This one is not yours to put out.");
            return;
        }

        light(!gLit);
    }

    on_rez(integer start)
    {
        light(START_LIT);
    }
}


// ---------------------------------------------------------------------
//  What a flickering light actually costs
//
//  Every change is a message about this object to every viewer near
//  enough to care. At the default quarter second that is four a second,
//  for as long as it burns. One candle is nothing. Forty candles in a
//  tavern is a hundred and sixty messages a second going out of that
//  region, and it will be blamed on the mesh.
//
//  Two ways to have your tavern and keep it cheap. Put the flicker on
//  one or two lights that carry the room and leave the rest as steady
//  glow, which nobody notices; or raise SPEED, because a candle looks
//  perfectly alive at half a second and half as expensive.
// ---------------------------------------------------------------------

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