Share your Esphome light effects

So I have a whole bunch of devices running esphome, with just the standard light effects listed on the site.
Has anyone made any cool custom effects?
If so please share - it would be really nice to build up a thread of all the custom effects people have made, especially for this time of year when lots of people are using esphome devices for cool Christmas light setups :stuck_out_tongue_winking_eye:

I’ve looked at wled which has some lovely effects, but the board never seemed reliable running that.

5 Likes

I have the following 3 I’ve collected. Honestly though, I highly recommend taking a look at WLED again. It now has HA integration and it has more effects and customization (for lighting) than esphome ever will. I wish there were a way to marry the two but since microcontrollers are like $2, I don’t lose too much sleep over having some dedicated just to lighting.
I have been thinking about running two controllers next to each other and establishing a serial link between the two so I can maintain the automation at the edge that esphome enables with the lighting effects that WLED enables and it function even if HA or wifi are down. Haven’t messed with it yet though.

- addressable_lambda:
  name: "Christmas RedGreen (Static)"
  lambda: |-

    for (int i = 1; i <  it.size(); i+=2) {
    it[i] = light::ESPColor(255, 0, 18);
    }
    for (int i = 0; i <  it.size(); i+=2) {
    it[i] = light::ESPColor(0, 179, 44);
    }
  # from reddit user thedoctor___
  # https://www.reddit.com/r/homeassistant/comments/bua3u8/esphome_what_custom_addressable_lambda_effects/


- addressable_lambda:
  name: "Bluez"
  lambda: |-

    for (int i = 0; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(255, 255, 255);
    }

    for (int i = 1; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(255, 255, 255);
    }

    for (int i = 2; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(238, 0, 255);
    }

    for (int i = 3; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(238, 0, 255);
    }

    for (int i = 4; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(255, 157, 0);
    }

    for (int i = 5; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(255, 157, 0);
    }

    for (int i = 6; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(0, 28, 209);
    }

    for (int i = 7; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(0, 28, 209);
    }

    for (int i = 8; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(183, 255, 0);
    }

    for (int i = 9; i <  it.size(); i+=10) {
    it[i] = light::ESPColor(183, 255, 0);
    }
  # from reddit user thedoctor___
  # https://www.reddit.com/r/homeassistant/comments/bua3u8/esphome_what_custom_addressable_lambda_effects/

- addressable_lambda:
  name: "Fire"
  update_interval: 15ms
  lambda: |-
    int Cooling = 55;
    int Sparking = 110;
    static byte heat[188];
    int cooldown;

    // Step 1.  Cool down every cell a little
    for( int i = 0; i < it.size(); i++) {
      cooldown = random(0, ((Cooling * 10) / it.size()) + 2);

      if(cooldown>heat[i]) {
        heat[i]=0;
      } else {
        heat[i]=heat[i]-cooldown;
      }
    }

    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= it.size() - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
    }

    // Step 3.  Randomly ignite new 'sparks' near the bottom
    if( random(255) < Sparking ) {
      int y = random(7);
      heat[y] = heat[y] + random(160,255);
    }

    // Step 4.  Convert heat to LED colors
    for( int Pixel = 0; Pixel < it.size(); Pixel++) {
      // Scale 'heat' down from 0-255 to 0-191
      byte t192 = round((heat[Pixel]/255.0)*191);

      // calculate ramp up from
      byte heatramp = t192 & 0x3F; // 0..63
      heatramp <<= 2; // scale up to 0..252

      // figure out which third of the spectrum we're in:
      //this is where you can reverse the effect by switching the commented out lines in all 3 places.
      if( t192 > 0x80) {                     // hottest
        //it[it.size() - Pixel - 1] = ESPColor(255, 255, heatramp);
        it[Pixel] = ESPColor(255, 255, heatramp);
      } else if( t192 > 0x40 ) {             // middle
        //it[it.size() - Pixel - 1] = ESPColor(255, heatramp, 0);
        it[Pixel] = ESPColor(255, heatramp, 0);
      } else {                               // coolest
        //it[it.size() - Pixel - 1] = ESPColor(heatramp, 0, 0);
        it[Pixel] = ESPColor(heatramp, 0, 0);
      }
    }

-J

1 Like

I wrote this quickly last night using @JayElDubya’s Red/Green as a base

      - addressable_lambda:
          name: "Static Rainbow"
          lambda: |-
           for (int i = 1; i < it.size(); i+=7) {
               it[i] = light::ESPColor(148, 0, 211);
            }
           for (int i = 0; i < it.size(); i+=7) {
               it[i] = light::ESPColor(75, 0, 130);
            }
           for (int i = 1; i < it.size(); i+=7) {
               it[i] = light::ESPColor(0, 0, 255);
            }
           for (int i = 0; i < it.size(); i+=7) {
               it[i] = light::ESPColor(0, 255, 0);
            }
           for (int i = 1; i < it.size(); i+=7) {
               it[i] = light::ESPColor(255, 255, 0);
            }
           for (int i = 0; i < it.size(); i+=7) {
               it[i] = light::ESPColor(255, 127, 0);
            }
           for (int i = 0; i < it.size(); i+=7) {
               it[i] = light::ESPColor(255, 0, 0);
            }

It just simply makes a static rainbow on my christmas tree. I use the Rainbow animation and the wife wanted one that didn’t move at all.

Just used a google for rainbow RGB to get the color values but they work well enough for me.

Thanks both, will be giving these a try out this evening :slight_smile:

Createt by @dashdrum and works great:

- addressable_lambda:
    name: Blue Scan
    update_interval: 25ms
    lambda:


      static int step = 0;
      static int direction = 1;

      if(initial_run){
        step = 0;
      }


      it[step] = ESPColor(0,0,255);
      if(step >0 && step < it.size()){
        it[step + (direction * -1)] = ESPColor::BLACK;
      }

      step = step + direction;

      if(step >= it.size() || step < 0){
        direction = direction * -1;
        step = step + (direction * 2);
      }
1 Like

and this is mine:

      - addressable_lambda:
          name: snowflack
          update_interval: 43ms
          lambda:
    
            static int step = 0;
            
            static int startstepa = 0;
            static int startpositiona = 0;
            static int endpositiona = 0;
            static int directiona = 0;
            
            static int startstepb = 0;
            static int startpositionb = 0;
            static int endpositionb = 0;
            static int directionb = 0;
            
            
            if(initial_run){
              it.all() = ESPColor(0, 0, 0);
              step = 0;
              
              startstepa = 7;
              startpositiona = 79;
              endpositiona = 0;
              directiona = -1;
              
              startstepb = 55;
              startpositionb = 259;
              endpositionb = 180;
              directionb = -1;
            

            }

   
    
            if(step >= startstepa-3+(directiona*3) && step <= startstepa+(endpositiona-startpositiona)*directiona-3+(directiona*3)){
              it[startpositiona+((step-startstepa)*directiona)-3+(directiona*3)] = ESPColor(77, 54, 32);
            }
            if(step >= startstepa-3+(directiona*2) && step <= startstepa+(endpositiona-startpositiona)*directiona-3+(directiona*2)){
              it[startpositiona+((step-startstepa)*directiona)-3+(directiona*2)] = ESPColor(255, 181, 108);
            }
            if(step >= startstepa-3+(directiona*1) && step <= startstepa+(endpositiona-startpositiona)*directiona-3+(directiona*1)){
              it[startpositiona+((step-startstepa)*directiona)-3+(directiona*1)] = ESPColor(179, 127, 76);
            }
            if(step >= startstepa-3 && step <= startstepa+(endpositiona-startpositiona)*directiona-3){
              it[startpositiona+((step-startstepa)*directiona)-3] = ESPColor(77, 54, 32);
            }
            if(step >= startstepa-3-(directiona*1) && step <= startstepa+(endpositiona-startpositiona)*directiona-3-(directiona*1)){
              it[startpositiona+((step-startstepa)*directiona)-3-(directiona*1)] = ESPColor(51, 36, 22);
            }
            if(step >= startstepa-3-(directiona*2) && step <= startstepa+(endpositiona-startpositiona)*directiona-3-(directiona*2)){
              it[startpositiona+((step-startstepa)*directiona)-3-(directiona*2)] = ESPColor(26, 18, 11);
            }
            if(step >= startstepa-3-(directiona*3) && step <= startstepa+(endpositiona-startpositiona)*directiona-3-(directiona*3)){
              it[startpositiona+((step-startstepa)*directiona)-3-(directiona*3)] = ESPColor(0, 0, 0);
            }



            if(step >= startstepb-3+(directionb*3) && step <= startstepb+(endpositionb-startpositionb)*directionb-3+(directionb*3)){
              it[startpositionb+((step-startstepb)*directionb)-3+(directionb*3)] = ESPColor(77, 54, 32);
            }
            if(step >= startstepb-3+(directionb*2) && step <= startstepb+(endpositionb-startpositionb)*directionb-3+(directionb*2)){
              it[startpositionb+((step-startstepb)*directionb)-3+(directionb*2)] = ESPColor(255, 181, 108);
            }
            if(step >= startstepb-3+(directionb*1) && step <= startstepb+(endpositionb-startpositionb)*directionb-3+(directionb*1)){
              it[startpositionb+((step-startstepb)*directionb)-3+(directionb*1)] = ESPColor(179, 127, 76);
            }
            if(step >= startstepb-3 && step <= startstepb+(endpositionb-startpositionb)*directionb-3){
              it[startpositionb+((step-startstepb)*directionb)-3] = ESPColor(77, 54, 32);
            }
            if(step >= startstepb-3-(directionb*1) && step <= startstepb+(endpositionb-startpositionb)*directionb-3-(directionb*1)){
              it[startpositionb+((step-startstepb)*directionb)-3-(directionb*1)] = ESPColor(51, 36, 22);
            }
            if(step >= startstepb-3-(directionb*2) && step <= startstepb+(endpositionb-startpositionb)*directionb-3-(directionb*2)){
              it[startpositionb+((step-startstepb)*directionb)-3-(directionb*2)] = ESPColor(26, 18, 11);
            }
            if(step >= startstepb-3-(directionb*3) && step <= startstepb+(endpositionb-startpositionb)*directionb-3-(directionb*3)){
              it[startpositionb+((step-startstepb)*directionb)-3-(directionb*3)] = ESPColor(0, 0, 0);
            }


            step = step + 1;

            if(step >= 200 || step < 0){
              step = 0;
            }

You have to edit this section:

startstepa = 7; -> start time in the loop
startpositiona = 79; -> start position
endpositiona = 0; -> end position
directiona = -1; -> direction

just, if you like to scans/flake on the same time:
startstepb = 55;
startpositionb = 259;
endpositionb = 180;
directionb = -1;

if(step >= 200 || step < 0){ -> 200 = loop count
step = 0;
}

This is a simple one, but is a pleasing effect on an Xmas tree:

- addressable_lambda:
    name: Gold Glitter
    update_interval: 18ms
    lambda:
      static int state = 0;

      if (initial_run){
        state = 0;

        it.all() = ESPColor(218,165,32);

        ESP_LOGD("custom", "Gold Glitter");
      } else {

        it.all() = ESPColor(218,165,32);

        if(state==0){
          int i = rand() % it.size();
          it[i] = ESPColor::WHITE;
          state += 1;
        } else {
          state += 1;
          state = state % 10;
        }
      }

Hey there, even tho its a bit late and not christmas related I thought might share this :grinning:. It’s what imo is an improved rainbow effect.

lambda: |-
            uint8_t led_change = 24; //(higher is more change) the difference in hue for each led down the strip
            float speed = 7; //(lower is faster) the speed the first led colour changes at (therefore affecting all)
            
            if (initial_run) {
              it.all() = Color(0, 0, 0);
            }
            
            unsigned long time = millis() / speed;
            int repetitions = time / 1529;
            uint16_t hue = time - (1529 * repetitions);
            
            for (int i = 0; i < it.size(); i++) {
              if (hue >= 0 && hue < 255) {
                uint8_t green = hue;
                it[i] = Color(255, green, 0);
              } else if (hue >= 255 && hue < 510) {
                uint8_t red = hue - 255;
                it[i] = Color((255 - red), 255, 0);
              } else if (hue >= 510 && hue < 765) {
                uint8_t blue = hue - 510;
                it[i] = Color(0, 255, blue);
              } else if (hue >= 765 && hue < 1020) {
                uint8_t green = hue - 765;
                it[i] = Color(0, (255 - green), 255);
              } else if (hue >= 1020 && hue < 1275) {
                uint8_t red = hue - 1020;
                it[i] = Color(red, 0, 255);
              } else if (hue >= 1275 && hue < 1530) {
                uint8_t blue = hue - 1275;
                it[i] = Color(255, 0, (255 - blue));
              }
              hue+=led_change;
              if (hue >= 1530) {
                hue-=1530;
              }
            }

Changing the variable on the lines with comments will change aspects of the rainbow.

The default rainbow effect I found to be too dull in the mixed led colours and only bright on the single led colours. Imo this better showcases the mixed colours like yellow, cyan and pink. It’s also brighter which is nice.

1 Like

Hey guys,

Trying to add the christmas lights posted above but getting YAML Syntax errors. Can someone advise whats wrong?

This is just my full effects list for easy readability.

    effects:
       - lambda:
           name: Breathing Red
           update_interval: 16s #Finetune to your liking with the transition lenght below
           lambda: |-
            #define Color1 1.0, 0.0, 0.0 //These are the colors defined, feel free to change or extend the list
                                         //if you extend the list, dont forget to add them in the switch loop below
                                         //and remember to adjust the reset counter at the bottom
            static int state = 0;
            static int color = 1;
            auto call = id(printer_lamp).turn_on(); //put the id for your light in here
            call.set_transition_length(15000);
            if (state == 0) 
            {
             call.set_brightness(0.01);
             
            }
             else if (state == 1)
            {
              call.set_rgb(Color1); 
              call.set_brightness(1.0);
            }
             
             state ++;
             if (state == 2){
             state = 0;
             }
             call.perform();
        
        - addressable_lambda:
           name: "Christmas RedGreen (Static)"
           lambda: |-

             for (int i = 1; i <  it.size(); i+=2) {
             it[i] = light::ESPColor(255, 0, 18);
             }
             for (int i = 0; i <  it.size(); i+=2) {
             it[i] = light::ESPColor(0, 179, 44);

Hi all,

I have converted a really great looking fastled based fireplace effect to lambda:

I’m using it with a home made 21*15 addressable led matrix made of a WS2811 addressable led strip controlled by an ESP32 WROOM. The matrix is installed inside an old cole stove in the living room and I’m very satisfied with the results.

My C++ knowledge is very rusty (basing myself on what I recall of my C++ courses in school 20 years ago), but I have attempted to rewrite it without the need of additional helpers and using a single function.

Configurable through te relevant consts:

  • Matrix rows and columns
  • Amount of flares and intensity
  • Colors

It goes without saying all credits go to the original author:

light:
  - platform: fastled_clockless
    chipset: WS2811
    pin: GPIO13
    num_leds: 315
    rgb_order: GRB
    id: ${ha_id}
    name: ${ha_name}
    effects:
      - addressable_lambda:
          name: Fire
          update_interval: 70ms
          lambda: |-
            const bool colmajor = false;
            const bool mattop = true;
            const bool matleft = true;
            const bool zigzag = true;
            const uint16_t rows = 15;
            const uint16_t cols = 21;
            const uint16_t offsetx = 0;
            const uint16_t offsety = 0;
            const uint8_t maxflare = 3;
            const uint8_t flarerows = 7;
            const uint8_t flarechance = 30;
            const uint8_t flaredecay = 14;
            const uint32_t colors[] = {0x000000,0x100000,0x300000,0x600000,0x800000,0xA00000,0xC02000,0xC04000,0xC06000,0xC08000,0x807080};
            const uint8_t NCOLORS = (sizeof(colors)/sizeof(colors[0]));

            static uint8_t nflare = 0;
            static uint32_t flare[maxflare];
            static uint8_t pix[rows][cols];
            static bool needsinit = true;
            static long t = 0;
            
            uint16_t b, d, i, j, k, l, n, x, y, z;
            uint16_t phy_w = cols;
            uint16_t phy_h = rows;
            uint16_t phy_x = 0;
            uint16_t phy_y = 0;
            
            
            if ( needsinit == true ) {
              needsinit = false;
              for ( i=0; i<rows; ++i ) {
                for ( j=0; j<cols; ++j ) {
                  if ( i == 0 ) pix[i][j] = NCOLORS - 1;
                  else pix[i][j] = 0;
                }
              }
            }
            
            // First, move all existing heat points up the display and fade
            for ( i=rows-1; i>0; --i ) {
              for ( j=0; j<cols; ++j ) {
                uint8_t n = 0;
                if ( pix[i-1][j] > 0 )
                  n = pix[i-1][j] - 1;
                pix[i][j] = n;
              }
            }
          
            // Heat the bottom row
            for ( j=0; j<cols; ++j ) {
              i = pix[0][j];
              if ( i > 0 ) {
                pix[0][j] = random(NCOLORS-6, NCOLORS-2);
              }
            }

            // Update existing flares
            for ( i=0; i<nflare; ++i ) {
              x = flare[i] & 0xff;
              y = (flare[i] >> 8) & 0xff;
              z = (flare[i] >> 16) & 0xff;
              b = z * 10 / flaredecay + 1;
              for ( k=(y-b); k<(y+b); ++k ) {
                for ( int l=(x-b); l<(x+b); ++l ) {
                  if ( k >=0 && l >= 0 && k < rows && l < cols ) {
                    d = ( flaredecay * sqrt16((x-l)*(x-l) + (y-k)*(y-k)) + 5 ) / 10;
                    n = 0;
                    if ( z > d ) n = z - d;
                    if ( n > pix[k][l] ) { // can only get brighter
                      pix[k][l] = n;
                    }
                  }
                }
              }
              if ( z > 1 ) {
                flare[i] = (flare[i] & 0xffff) | ((z-1)<<16);
              } else {
                // This flare is out
                for ( j=i+1; j<nflare; ++j ) {
                  flare[j-1] = flare[j];
                }
                --nflare;
              }
            }
            // New Flare
            if ( nflare < maxflare && random(1,101) <= flarechance ) {
              x = random(0, cols);
              y = random(0, flarerows);
              z = NCOLORS - 1;
              b = z * 10 / flaredecay + 1;
              flare[nflare++] = (z<<16) | (y<<8) | (x&0xff);
              for ( k=(y-b); k<(y+b); ++k ) {
                for ( int l=(x-b); l<(x+b); ++l ) {
                  if ( k >=0 && l >= 0 && k < rows && l < cols ) {
                    d = ( flaredecay * sqrt16((x-l)*(x-l) + (y-k)*(y-k)) + 5 ) / 10;
                    n = 0;
                    if ( z > d ) n = z - d;
                    if ( n > pix[k][l] ) { // can only get brighter
                      pix[k][l] = n;
                    }
                  }
                }
              }
            }
            // Draw
            if ( colmajor == true ) {
              phy_w = rows;
              phy_h = cols;
            }
            for ( uint16_t row=0; row<rows; ++row ) {
              for ( uint16_t col=0; col<cols; ++col ) {
                if ( colmajor == true ) {
                    phy_x = offsetx + (uint16_t) row;
                    phy_y = offsety + (uint16_t) col;
                } else {
                    phy_x = offsetx + (uint16_t) col;
                    phy_y = offsety + (uint16_t) row;
                }
                if ( matleft == true && zigzag == true ) {
                  if ( ( phy_y & 1 ) == 1 ) {
                    phy_x = phy_w - phy_x - 1;
                  }
                } else if ( matleft == false && zigzag == true ) {
                  if ( ( phy_y & 1 ) == 0 ) {
                    phy_x = phy_w - phy_x - 1;
                  }
                } else if ( matleft == false ) {
                  phy_x = phy_w - phy_x - 1;
                }
                if ( mattop == true && colmajor == true ) {
                  phy_x = phy_w - phy_x - 1;
                } else if (mattop) {
                  phy_y = phy_h - phy_y - 1;
                }
                it[phy_x + phy_y * phy_w] = ESPColor(colors[pix[row][col]]);
              }
            }
11 Likes

I know this thread is a bit older, but I was wondering if anyone had a nice list of effects that could be used with old fashioned Analogue RGB strips. I looked at the ones here, and they are all for addressable LED’s.
I have a couple of RGBW analogue strips I’d like to make some effects for

3 Likes

hello i would like to create a light scene on esphome with neopixelbus like this:

Someone can help me please?

I have tried converting the StormCloud effect to adressable_lambda code. This is my take on it:

      - addressable_lambda:
          name: "Lightning Animation"
          update_interval: 5ms

          lambda: |-
            static float speed_multiplier = 0.003;
            static uint32_t lightning_end = 0;
            static uint32_t lightning_wait = 0;
            static uint32_t lightning_modifier = 0;
            for(uint8_t led_index = 0; led_index < it.size(); led_index++) {
              uint32_t elapsed = millis() + (float(led_index) * 650);  // MOVEMENT_MODIFIER

              // Calculate blue color component
              uint8_t b = uint8_t((pow(sin((elapsed * speed_multiplier) / 4.0), 3.0) + 1.0) * 80.0 + 90.0);

              uint8_t r, g;

              // Check if lightning is active
              if (lightning_end == 0) {
                // No lightning - normal animation
                r = uint8_t((sin((elapsed * speed_multiplier) / 5.0) + 1.0) * 40.0);
                g = uint8_t(pow(sin(((elapsed * speed_multiplier) / 8.0) + PI / 2.0), 4.0) * 50.0);

                // Lightning initiation condition
                if (millis() >= lightning_wait && random_uint32() % 1500 == 1) {
                  lightning_end = millis() + (rand() % (500 - 100) + 100);  // Random duration between 100 and 500ms
                  lightning_modifier = rand() % (35 - 20) + 20;             // Random speed modifier between 20 and 35
                }
              } else {
                // Lightning is active - brighter white-ish light
                r = g = uint8_t((sin(elapsed / lightning_modifier) + 1.0) * 60);

                if (millis() >= lightning_end) {
                  lightning_end = 0;
                  lightning_wait = millis() + 3000;  // Lightning cooldown
                }
              }

              // Set the LED color
              it[led_index] = esphome::Color(r, g, b);
            }

The original code goes over each pixel one-by-one while I do it in a for-loop. If I do it one-by-one within the lambda function the update is too slow.
By doing so, I think I changed the lightning effect to work a bit differently. Still, it looks impressing and should work.

Having said that, can anyone help me converting the Pacifica-code from FastLED to ESPHome Adressable Lambda?

1 Like