Skip to content

Development

DatuX edited this page Nov 1, 2025 · 5 revisions

Development

  • Cool ledder stuff is under ledder/

  • Webinterface stuff is under src/ Ignore it if you jusst want to play with leds and animations :)

Its much easier if you have a good editor that does good autocompletion. (Its the main reason i'm using Webstorm)

Starting ledder for development mode

Just start ledder normally: npm run start.

(DONT use npm run dev! This is actually for web development of the userinterface!)

Ledder will automaticly reload and restart and animation when it detects you've changed the animation-file on disk.

For changes in other files you will have to restart ledder. (Webinterface will automatically reconnect and stay on the same page)

Using ledder to create animations

Pixels and colors

The most basic datastructures in ledder are Pixel() and Color() objects.

A Color() is what you expect and represents a color.

A Pixel() needs a color object and has an x and y coordinate.

const color=new Color(255,0,0)
const pixel=new Pixel(3,3, color)

Pixels lists

Just a list of pixels. Its actually just a Set() with extras.

They are very important and used for all kinds of things. Adding a pixel to a list:

const pixels=new PixelList()
pixels.add(pixel)

A pixel list can even contain other pixel lists, so it becomes a pixel tree:

const otherList=new PixelList()
otherList.add(new Pixel(1,1, color))
otherList.add(pixels)
otherList.print()

Use the print() function to see whats going on:

pixeltree:
 (1, 1) (r255, g0, b0, a1)
 pixeltree:
  (3, 3) (r255, g0, b0, a1)

Its setup this way so that we can add/remove sets of huge pixelLists and still have high performance. (Since you're only adding/removing references.)

Everythings a reference

Its important to remember that variables in javascript are actually references to objects.

For example:

let color1=new Color(255,0,0)
let color2=color1;

Now color1 and color2 both point to the same Color object. If you manipulate color1, then color2 will be changed as well.

Ledder extensively makes use of this: If you add a pixel to a pixelList, you're actually adding a reference to that pixel.

If we run an effect on a Color-Object, that same color object is most likely used by multiple Pixels.

To create an actual copy you can use the copy() function on most objects.

Animators

(Note: Before you start its useful to have a good editor. I prefer Webstorm, but vscode works as well.)

To actually do stuff you need to create an Animator class.

Use Template.ts as an example to start with.

The run() function of the animator is where it all happens:

export default class Test extends Animator {

    async run(box: PixelBox, scheduler: Scheduler, controls: ControlGroup) {

        const color = new Color(255, 0, 0)
        const pixel = new Pixel(3, 3, color)
        box.add(pixel)

    }

}

It has THE 3 main parameters you will be working with:

  • The box
  • The scheduler
  • The controls

The box

A PixelBox is just a PixelList with extras:

It has has minimum and maximum x and y coordinates, which you should stay within:

console.log(box.xMin, box.xMax, box.yMin, box.yMax)

The main box is usually the one that is rendered to your Display and has the size of the display.

The size of the box is not enforced in any way, for performance and other reasons. So you still can add Pixels that are outside of it.

The scheduler

The scheduler acts almost like setInterval and setTimeout. But its frame-based instead of time based.

Use it to actually animate stuff.

An example of a moving pixel:

export default class Test extends Animator {

    async run(box: PixelBox, scheduler: Scheduler, controls: ControlGroup) {

        const color=new Color(255,0,0)
        const pixel=new Pixel(3,3, color)
        box.add(pixel)

        scheduler.interval(1, ()=>
        {
            pixel.move(1,0)
            pixel.wrap(box)

        })

    }
}

Note that you can also call move() and wrap() on the box to move all the pixels that added to the box.

NOTE: Dont use internal methods of the scheduler, they start with __

The controls

One awesome feature of ledder is that you can easily make stuff controllable by the user.

To make the example above fully controllable do something like this:

export default class Test extends Animator {

    async run(box: PixelBox, scheduler: Scheduler, controls: ControlGroup) {

        const colorControl=controls.color('Main color')
        const controlY=controls.value('Y coordinate', 3, box.yMin, box.yMax , 1, true)
        const controlInterval=controls.value('Move interval', 1, 1,60)

        const pixel=new Pixel(0,controlY.value, colorControl)
        box.add(pixel)

        scheduler.intervalControlled(controlInterval, ()=>
        {
            box.move(1,0)
            box.wrap(box)

        })

    }
}

The use can now control your animation and even make presets! Some controls are realtime, while others will restart the animation if you change them. (by specifying true at the end)

NOTE: Dont use the internal methods of the controls, they start with __

Control types

Controls are very powerful and are recursive as well. Look at the other animations how to use them optimally.

  • control.value(): Just a number, with a minimum, maximum and step size.
  • control.range(): A range between 2 numbers.
  • control.color(): Color selector
  • control.switch(): On/off switch
  • control.select(): Select box
  • control.group(): Sub ControlGroup (recursively)
  • control.input(): Text input

Other stuff

You now know the core functions of ledder.

However, another goal of ledder is to make reusable components: Classes to draw boxes or create certain effects.

Draw classes

In the draw-folder you'll find classes to "draw".

These are just PixelLists that fill themselves with pixels.

E.g. to draw a rectangle:

const rect = new DrawRectangle(0,0,3,3, color)
box.add(rect)

There are a bunch of really cool ones like DrawAsciiArt and DrawText. Look at other Animators how to use them. (The Logos for example)

Its also very easy to create new Draw-classes.

FX classes

FX classes operate on existing PixelList() and Color() objects.

They can do some really awesome stuff, look at what the Marquee Animator can do for example.

The ledder-logo you see above is just a DrawText() combined with FxColorPattern(). I've just clicked around in the controls of the Marquee animator and took a screenshot. :)

FAQ

Previews are blank / I get weird proxy errors.

If you do external async stuff, like loading files or getting an rss feed, take note:

The scheduler can run in realtime mode (60 fps usually) and "static" mode.

Static mode is used to render previews and in the future it can even pre-render static animations.

In static mode the render is running as fast as possible: This means if your animation is awaiting for some external stuff, the rest of it will probably never run.

Its also possible that it tries to do stuff too late, when the animation is already aborted. In that case you will get an error like: ".get on revoked proxy object"

To fix this, call scheduler.stop() before doing that stuff, and scheduler.resume() when your done.

(We use proxy objects to prevent interaction between a new animation and a previous one that still does async stuff)

Note that this will also happen when you use setInterval or setTimeout. Never use those in ledder!

Which functions can i use to make animations?

You shouldn't use stuff that starts with __. Thats for internal use only. (In case of the scheduler and controls this is pretty clear.)

If you DO need them for some reason, you're either doing something wrong, or we need to fix/extend the API.

You can use everything explained here, and AnimationManager. (still have to document that, look at MQTT for an example)

We probably need to be more clear whats usable from animations and what is not. Stuff outside the ledder subdirectory is definitely off limits for animations.

You can offcourse use external npm modules and do regular nodejs stuff.

Clone this wiki locally