Effects
Effects
StarLight comes with a range of built in lighting effects. Users can also create their own effects in the LedEffects.h file.
A list of effects can be found here StarLight Effects
For programming palettes, see this page: 🚧
Effect Types
Effects can be specific to a type of fixture, such as 2D or 3D effects. Effects can be synchronized between multiple instances of StarLight.
Creating New Effects
The procedure for creating a new effect is easy:
- Copy the Effect Template found on this page into "src/App/LedEffects.h".
- Rename the "ExampleEffect" in both locations (2 top, 1 bottom) within the template.
- Add it to the appropriate location "class Effects {}" near the bottom of the file.
This is sufficient for the new effect to run and be selectable.
Effects Basics
Addressing LEDs
- "leds.size" is the virtual size of the effect.
- "leds[pos] = color" gives a virtual pixel a color value. Identical to "leds.setPixelColor(pos, color)"
- "color = leds[pos]"
- Colors can be an HSV or RGB value. RGB is intuitive as it relates to the LED hardware. HSV is often simpler for programming effects.
- Colors can be applied directly from a palette: "ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)"
FastLED commands
StarLight runs on FastLED. FastLED's documentation has more information about the wide range of options available for programming LEDs.
General Approaches to Creating an Effect
It is possible to program effects in many different ways. Sometimes it is easiest to simple loop through every pixel, and other times it is advantageous to draw objects to specific locations, without addressing every LED. These can also be combined.
Effect Example
This template can be used to create new effects. The settings are explained below:
class ExampleEffect: public Effect {
public:
const char * name() {return "ExampleEffect";}
uint8_t dim() {return _2D;}
const char * tags() {return "♪♫⚡💡💫";}
struct Map_t {
uint8_t angle;
uint8_t radius;
};
void setup(Leds &leds) {
leds.fill_solid(CRGB::Black);
}
void loop(Leds &leds) {
//Binding of controls. Keep before binding of vars and keep in same order as in controls()
uint8_t speed = leds.sharedData.read<uint8_t>();
uint8_t legs = leds.sharedData.read<uint8_t>();
Coord3D testCoord3D = leds.sharedData.read<Coord3D>();
//binding of loop persistent values (pointers) tbd: aux0,1,step etc can be renamed to meaningful names
Map_t *rMap = leds.sharedData.readWrite<Map_t>(leds.size.x * leds.size.y); //array
uint8_t *offsX = leds.sharedData.readWrite<uint8_t>();
uint16_t *aux0 = leds.sharedData.readWrite<uint16_t>();
unsigned long *step = leds.sharedData.readWrite<unsigned long>();
unsigned long beatTimer = sys->now - *step;
Coord3D pos = {0,0,0};
for (pos.x = 0; pos.x < leds.size.x; pos.x++) {
for (pos.y = 0; pos.y < leds.size.y; pos.y++) {
CRGB color = ColorFromPalette(leds.palette, random8());
leds[pos] = color; // or setPixelColor(pos, color);
}
}
if (leds.projectionDimension == _3D)
//do something special for 3D projections/fixtures
if (!leds.isMapped(leds.XYZ(1,2,3)))
//do something special if there is no physical led in the current projection
ppf("test %d\n", beatTimer);
}
void controls(Leds &leds, JsonObject parentVar) {
Effect::controls(leds, parentVar);
ui->initSlider(parentVar, "speed", leds.sharedData.write<uint8_t>(128), 1, 255);
ui->initSlider(parentVar, "legs", leds.sharedData.write<uint8_t>(4), 1, 8);
ui->initCoord3D(parentVar, "testCoord3D", leds.sharedData.write<Coord3D>({1,2,3}));
}
}; // ExampleEffect
Remarks
- Updated!: An effect should run fine on any dimension of a fixture (1D/2D/3D) of any size (including 0,0,0 or 1,1,1 as exotic projections do rely on this).
- There is no notion of the physical shape of the fixture the effect will be projected on.
- The only information an effect has is leds.projectionDimension and leds.isMapped(Coord3D).
- An effect has a dimension (dim()) but the projection functions in StarLight will deal with properly displaying an effect on a fixture.
- An effect can use leds.projectionDimension to make it optimized for 1D, 2D and 3D, no need to create different effects for different dimensions (e.g. fire 1D, 2D and 3D can all be in one effect).
- See the Game of Life effect for an advanced effect using leds.projectionDimension and leds.isMapped(Coord3D)
- See also Projections And Mappings
- name(), dim() and tags() provide effect metadata
- leds.sharedData preserves data over multiple loops, in case of array add the number of elements. data allocation and management will be done transparent from the effect code.
- leds.size is the virtual size of the effect
- leds[pos] = color gives a virtual pixel a value
- leds[pos] = color is identical to leds.setPixelColor(pos, color);
- color = leds[pos] is identical to color = leds.getPixelColor(pos);
- FastLed library commands can be used. e.g. leds.fill_solid(CRGB::Black); operations will be executed in the virtual leds context
- Effect class should not contain public or private variables, use return functions instead to minimize memory foodprint
- use sys->now instead of millis() as it supports syncing with other instances, random16_set_seed(sys->now) is provided under the hood for predictable execution of effects using the random function (however wip)