Skip to content

Instantly share code, notes, and snippets.

@kriegsman
Last active December 3, 2024 18:20
Show Gist options
  • Save kriegsman/756ea6dcae8e30845b5a to your computer and use it in GitHub Desktop.
Save kriegsman/756ea6dcae8e30845b5a to your computer and use it in GitHub Desktop.

Revisions

  1. kriegsman revised this gist Dec 22, 2015. 1 changed file with 13 additions and 5 deletions.
    18 changes: 13 additions & 5 deletions TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -222,17 +222,25 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    {
    uint16_t ticks = ms >> (8-TWINKLE_SPEED);
    uint8_t fastcycle8 = ticks;
    uint8_t slowcycle8 = (ticks >> 8) + salt;
    uint16_t slowcycle16 = (ticks >> 8) + salt;
    slowcycle16 += sin8( slowcycle16);
    slowcycle16 = (slowcycle16 * 2053) + 1384;
    uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);

    uint8_t bright = 0;
    if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
    bright = attackDecayWave8( fastcycle8);
    }

    uint8_t hue = (slowcycle8 * 16) + salt;
    CRGB c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    if( COOL_LIKE_INCANDESCENT == 1 ) {
    coolLikeIncandescent( c, fastcycle8);
    uint8_t hue = slowcycle8 - salt;
    CRGB c;
    if( bright > 0) {
    c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    if( COOL_LIKE_INCANDESCENT == 1 ) {
    coolLikeIncandescent( c, fastcycle8);
    }
    } else {
    c = CRGB::Black;
    }
    return c;
    }
  2. kriegsman revised this gist Dec 22, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -222,7 +222,7 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    {
    uint16_t ticks = ms >> (8-TWINKLE_SPEED);
    uint8_t fastcycle8 = ticks;
    uint8_t slowcycle8 = (ticks >> 8) ^ salt;
    uint8_t slowcycle8 = (ticks >> 8) + salt;

    uint8_t bright = 0;
    if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
  3. kriegsman revised this gist Dec 22, 2015. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -242,10 +242,10 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    // symmetrical up-and-down triangle sawtooth waveform, except that this
    // function produces a triangle wave with a faster attack and a slower decay:
    //
    // / \
    // / \
    // / \
    // / \
    // / \
    // / \
    // / \
    // / \
    //

    uint8_t attackDecayWave8( uint8_t i)
  4. kriegsman revised this gist Dec 22, 2015. 1 changed file with 154 additions and 36 deletions.
    190 changes: 154 additions & 36 deletions TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -5,12 +5,13 @@
    #endif


    #define NUM_LEDS 60
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define DATA_PIN 3
    //#define CLK_PIN 4

    #define NUM_LEDS 100
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define DATA_PIN 3
    //#define CLK_PIN 4
    #define VOLTS 12
    #define MAX_MA 4000

    // TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
    // Colors are chosen from a palette; a few palettes are provided.
    @@ -72,37 +73,49 @@
    //
    // -Mark Kriegsman, December 2015

    CRGB leds[NUM_LEDS];
    CRGBArray<NUM_LEDS> leds;

    // Overall twinkle speed.
    // 0 (VERY slow) to 8 (VERY fast).
    // 4, 5, and 6 are recommended, default is 5.
    #define TWINKLE_SPEED 5
    // 4, 5, and 6 are recommended, default is 4.
    #define TWINKLE_SPEED 4

    // Overall twinkle density.
    // 0 (NONE lit) to 8 (ALL lit at once).
    // Default is 5.
    #define TWINKLE_DENSITY 5

    // Background color for 'unlit' pixels
    // Can be set to CRGB::Black if desired.
    CRGB gBackgroundColor = CRGB(3,0,6);

    // How often to change color palettes.
    #define SECONDS_PER_PALETTE 20

    #define SECONDS_PER_PALETTE 30
    // Also: toward the bottom of the file is an array
    // called "ActivePaletteList" which controls which color
    // palettes are used; you can add or remove color palettes
    // from there freely.

    // Background color for 'unlit' pixels
    // Can be set to CRGB::Black if desired.
    CRGB gBackgroundColor = CRGB::Black;
    // Example of dim incandescent fairy light background color
    // CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16);

    // If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
    // then for any palette where the first two entries
    // are the same, a dimmed version of that color will
    // automatically be used as the background color.
    #define AUTO_SELECT_BACKGROUND_COLOR 0

    // If COOL_LIKE_INCANDESCENT is set to 1, colors will
    // fade out slighted 'reddened', similar to how
    // incandescent bulbs change color as they get dim down.
    #define COOL_LIKE_INCANDESCENT 1


    CRGBPalette16 gCurrentPalette;
    CRGBPalette16 gTargetPalette;

    void setup() {
    delay( 3000 ); //safety startup delay

    FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
    FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
    .setCorrection(TypicalLEDStrip);

    @@ -119,9 +132,9 @@ void loop()
    EVERY_N_MILLISECONDS( 10 ) {
    nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12);
    }

    drawTwinkles( leds, NUM_LEDS);

    drawTwinkles( leds);

    FastLED.show();
    }

    @@ -131,7 +144,7 @@ void loop()
    // "CalculateOneTwinkle" on each pixel. It then displays
    // either the twinkle color of the background color,
    // whichever is brighter.
    void drawTwinkles( CRGB* L, uint16_t N)
    void drawTwinkles( CRGBSet& L)
    {
    // "PRNG16" is the pseudorandom number generator
    // It MUST be reset to the same starting value each time
    @@ -140,10 +153,30 @@ void drawTwinkles( CRGB* L, uint16_t N)
    uint16_t PRNG16 = 11337;

    uint32_t clock32 = millis();

    // Set up the background color, "bg".
    // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of
    // the current palette are identical, then a deeply faded version of
    // that color is used for the background color
    CRGB bg;
    if( (AUTO_SELECT_BACKGROUND_COLOR == 1) &&
    (gCurrentPalette[0] == gCurrentPalette[1] )) {
    bg = gCurrentPalette[0];
    uint8_t bglight = bg.getAverageLight();
    if( bglight > 64) {
    bg.nscale8_video( 16); // very bright, so scale to 1/16th
    } else if( bglight > 16) {
    bg.nscale8_video( 64); // not that bright, so scale to 1/4th
    } else {
    bg.nscale8_video( 86); // dim, scale to 1/3rd.
    }
    } else {
    bg = gBackgroundColor; // just use the explicitly defined background color
    }

    uint8_t backgroundBrightness = bg.getAverageLight();

    uint8_t backgroundBrightness = gBackgroundColor.getAverageLight();

    for( uint16_t i = 0; i < N; i++) {
    for( CRGB& pixel: L) {
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    uint16_t myclockoffset16= PRNG16; // use that number as clock offset
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    @@ -157,11 +190,20 @@ void drawTwinkles( CRGB* L, uint16_t N)
    // on the "brightness = f( time )" idea.
    CRGB c = computeOneTwinkle( myclock30, myunique8);

    // If the new pixel is brighter than the background color, use it.
    if( c.getAverageLight() > backgroundBrightness) {
    L[i] = c;
    } else {
    L[i] = gBackgroundColor;
    uint8_t cbright = c.getAverageLight();
    int16_t deltabright = cbright - backgroundBrightness;
    if( deltabright >= 32 || (!bg)) {
    // If the new pixel is significantly brighter than the background color,
    // use the new color.
    pixel = c;
    } else if( deltabright > 0 ) {
    // If the new pixel is just slightly brighter than the background color,
    // mix a blend of the new color and the background color
    pixel = blend( bg, c, deltabright * 8);
    } else {
    // if the new pixel is not at all brighter than the background color,
    // just use the background color.
    pixel = bg;
    }
    }
    }
    @@ -184,14 +226,50 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)

    uint8_t bright = 0;
    if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
    bright = triwave8( fastcycle8);
    bright = attackDecayWave8( fastcycle8);
    }

    uint8_t hue = (slowcycle8 * 16) + salt;
    return ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    CRGB c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    if( COOL_LIKE_INCANDESCENT == 1 ) {
    coolLikeIncandescent( c, fastcycle8);
    }
    return c;
    }


    // This function is like 'triwave8', which produces a
    // symmetrical up-and-down triangle sawtooth waveform, except that this
    // function produces a triangle wave with a faster attack and a slower decay:
    //
    // / \
    // / \
    // / \
    // / \
    //

    uint8_t attackDecayWave8( uint8_t i)
    {
    if( i < 86) {
    return i * 3;
    } else {
    i -= 86;
    return 255 - (i + (i/2));
    }
    }

    // This function takes a pixel, and if its in the 'fading down'
    // part of the cycle, it adjusts the color a little bit like the
    // way that incandescent bulbs fade toward 'red' as they dim.
    void coolLikeIncandescent( CRGB& c, uint8_t phase)
    {
    if( phase < 128) return;

    uint8_t cooling = (phase - 128) >> 4;
    c.g = qsub8( c.g, cooling);
    c.b = qsub8( c.b, cooling * 2);
    }

    // A mostly red palette with green accents and white trim.
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
    @@ -200,6 +278,16 @@ const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
    CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
    CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green };

    // A mostly (dark) green palette with red berries.
    #define Holly_Green 0x00580c
    #define Holly_Red 0xB00402
    const TProgmemRGBPalette16 Holly_p FL_PROGMEM =
    { Holly_Green, Holly_Green, Holly_Green, Holly_Green,
    Holly_Green, Holly_Green, Holly_Green, Holly_Green,
    Holly_Green, Holly_Green, Holly_Green, Holly_Green,
    Holly_Green, Holly_Green, Holly_Green, Holly_Red
    };

    // A red and white striped palette
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM =
    @@ -227,22 +315,52 @@ const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM =

    // A palette of soft snowflakes with the occasional bright one
    const TProgmemRGBPalette16 Snow_p FL_PROGMEM =
    { 0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0xFFFFFF };
    { 0x304048, 0x304048, 0x304048, 0x304048,
    0x304048, 0x304048, 0x304048, 0x304048,
    0x304048, 0x304048, 0x304048, 0x304048,
    0x304048, 0x304048, 0x304048, 0xE0F0FF };

    // A palette reminiscent of large 'old-school' C9-size tree lights
    // in the five classic colors: red, orange, green, blue, and white.
    #define C9_Red 0xB80400
    #define C9_Orange 0x902C02
    #define C9_Green 0x046002
    #define C9_Blue 0x070758
    #define C9_White 0x606820
    const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM =
    { C9_Red, C9_Orange, C9_Red, C9_Orange,
    C9_Orange, C9_Red, C9_Orange, C9_Red,
    C9_Green, C9_Green, C9_Green, C9_Green,
    C9_Blue, C9_Blue, C9_Blue,
    C9_White
    };

    // A cold, icy pale blue palette
    #define Ice_Blue1 0x0C1040
    #define Ice_Blue2 0x182080
    #define Ice_Blue3 0x5080C0
    const TProgmemRGBPalette16 Ice_p FL_PROGMEM =
    {
    Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
    Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
    Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
    Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3
    };


    // Add or remove palette names from this list to control which color
    // palettes are used, and in what order.
    const TProgmemRGBPalette16* ActivePaletteList[] = {
    &RedGreenWhite_p,
    &RetroC9_p,
    &BlueWhite_p,
    &RainbowColors_p,
    &FairyLight_p,
    &RedWhite_p,
    &RedGreenWhite_p,
    &PartyColors_p,
    &Snow_p
    &RedWhite_p,
    &Snow_p,
    &Holly_p,
    &Ice_p
    };


  5. kriegsman revised this gist Dec 18, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -183,7 +183,7 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    uint8_t slowcycle8 = (ticks >> 8) ^ salt;

    uint8_t bright = 0;
    if( (slowcycle8 & 0x07) < TWINKLE_DENSITY) {
    if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
    bright = triwave8( fastcycle8);
    }

  6. kriegsman revised this gist Dec 18, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -183,7 +183,7 @@ CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    uint8_t slowcycle8 = (ticks >> 8) ^ salt;

    uint8_t bright = 0;
    if( (slowcycle8 & 0x0E) < TWINKLE_DENSITY) {
    if( (slowcycle8 & 0x07) < TWINKLE_DENSITY) {
    bright = triwave8( fastcycle8);
    }

  7. kriegsman revised this gist Dec 17, 2015. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    #include "FastLED.h"
    #include "Ramekin.h"

    #if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
    #warning "Requires FastLED 3.1 or later; check github for latest code."
  8. kriegsman revised this gist Dec 17, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    #define NUM_LEDS 60
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define DATA_PIN 13
    #define DATA_PIN 3
    //#define CLK_PIN 4


  9. kriegsman revised this gist Dec 17, 2015. 1 changed file with 10 additions and 1 deletion.
    11 changes: 10 additions & 1 deletion TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    #define NUM_LEDS 60
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define DATA_PIN 3
    #define DATA_PIN 13
    //#define CLK_PIN 4


    @@ -201,6 +201,14 @@ const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
    CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
    CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green };

    // A red and white striped palette
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM =
    { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
    CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray,
    CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
    CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray };

    // A mostly blue palette with white accents.
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM =
    @@ -233,6 +241,7 @@ const TProgmemRGBPalette16* ActivePaletteList[] = {
    &BlueWhite_p,
    &RainbowColors_p,
    &FairyLight_p,
    &RedWhite_p,
    &PartyColors_p,
    &Snow_p
    };
  10. kriegsman created this gist Dec 17, 2015.
    249 changes: 249 additions & 0 deletions TwinkleFOX.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,249 @@
    #include "FastLED.h"
    #include "Ramekin.h"

    #if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
    #warning "Requires FastLED 3.1 or later; check github for latest code."
    #endif


    #define NUM_LEDS 60
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define DATA_PIN 3
    //#define CLK_PIN 4


    // TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
    // Colors are chosen from a palette; a few palettes are provided.
    //
    // This December 2015 implementation improves on the December 2014 version
    // in several ways:
    // - smoother fading, compatible with any colors and any palettes
    // - easier control of twinkle speed and twinkle density
    // - supports an optional 'background color'
    // - takes even less RAM: zero RAM overhead per pixel
    // - illustrates a couple of interesting techniques (uh oh...)
    //
    // The idea behind this (new) implementation is that there's one
    // basic, repeating pattern that each pixel follows like a waveform:
    // The brightness rises from 0..255 and then falls back down to 0.
    // The brightness at any given point in time can be determined as
    // as a function of time, for example:
    // brightness = sine( time ); // a sine wave of brightness over time
    //
    // So the way this implementation works is that every pixel follows
    // the exact same wave function over time. In this particular case,
    // I chose a sawtooth triangle wave (triwave8) rather than a sine wave,
    // but the idea is the same: brightness = triwave8( time ).
    //
    // Of course, if all the pixels used the exact same wave form, and
    // if they all used the exact same 'clock' for their 'time base', all
    // the pixels would brighten and dim at once -- which does not look
    // like twinkling at all.
    //
    // So to achieve random-looking twinkling, each pixel is given a
    // slightly different 'clock' signal. Some of the clocks run faster,
    // some run slower, and each 'clock' also has a random offset from zero.
    // The net result is that the 'clocks' for all the pixels are always out
    // of sync from each other, producing a nice random distribution
    // of twinkles.
    //
    // The 'clock speed adjustment' and 'time offset' for each pixel
    // are generated randomly. One (normal) approach to implementing that
    // would be to randomly generate the clock parameters for each pixel
    // at startup, and store them in some arrays. However, that consumes
    // a great deal of precious RAM, and it turns out to be totally
    // unnessary! If the random number generate is 'seeded' with the
    // same starting value every time, it will generate the same sequence
    // of values every time. So the clock adjustment parameters for each
    // pixel are 'stored' in a pseudo-random number generator! The PRNG
    // is reset, and then the first numbers out of it are the clock
    // adjustment parameters for the first pixel, the second numbers out
    // of it are the parameters for the second pixel, and so on.
    // In this way, we can 'store' a stable sequence of thousands of
    // random clock adjustment parameters in literally two bytes of RAM.
    //
    // There's a little bit of fixed-point math involved in applying the
    // clock speed adjustments, which are expressed in eighths. Each pixel's
    // clock speed ranges from 8/8ths of the system clock (i.e. 1x) to
    // 23/8ths of the system clock (i.e. nearly 3x).
    //
    // On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels
    // smoothly at over 50 updates per seond.
    //
    // -Mark Kriegsman, December 2015

    CRGB leds[NUM_LEDS];

    // Overall twinkle speed.
    // 0 (VERY slow) to 8 (VERY fast).
    // 4, 5, and 6 are recommended, default is 5.
    #define TWINKLE_SPEED 5

    // Overall twinkle density.
    // 0 (NONE lit) to 8 (ALL lit at once).
    // Default is 5.
    #define TWINKLE_DENSITY 5

    // Background color for 'unlit' pixels
    // Can be set to CRGB::Black if desired.
    CRGB gBackgroundColor = CRGB(3,0,6);

    // How often to change color palettes.
    #define SECONDS_PER_PALETTE 20

    // Also: toward the bottom of the file is an array
    // called "ActivePaletteList" which controls which color
    // palettes are used; you can add or remove color palettes
    // from there freely.


    CRGBPalette16 gCurrentPalette;
    CRGBPalette16 gTargetPalette;

    void setup() {
    delay( 3000 ); //safety startup delay

    FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
    .setCorrection(TypicalLEDStrip);

    chooseNextColorPalette(gTargetPalette);
    }


    void loop()
    {
    EVERY_N_SECONDS( SECONDS_PER_PALETTE ) {
    chooseNextColorPalette( gTargetPalette );
    }

    EVERY_N_MILLISECONDS( 10 ) {
    nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12);
    }

    drawTwinkles( leds, NUM_LEDS);

    FastLED.show();
    }


    // This function loops over each pixel, calculates the
    // adjusted 'clock' that this pixel should use, and calls
    // "CalculateOneTwinkle" on each pixel. It then displays
    // either the twinkle color of the background color,
    // whichever is brighter.
    void drawTwinkles( CRGB* L, uint16_t N)
    {
    // "PRNG16" is the pseudorandom number generator
    // It MUST be reset to the same starting value each time
    // this function is called, so that the sequence of 'random'
    // numbers that it generates is (paradoxically) stable.
    uint16_t PRNG16 = 11337;

    uint32_t clock32 = millis();

    uint8_t backgroundBrightness = gBackgroundColor.getAverageLight();

    for( uint16_t i = 0; i < N; i++) {
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    uint16_t myclockoffset16= PRNG16; // use that number as clock offset
    PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
    // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
    uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
    uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16;
    uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel

    // We now have the adjusted 'clock' for this pixel, now we call
    // the function that computes what color the pixel should be based
    // on the "brightness = f( time )" idea.
    CRGB c = computeOneTwinkle( myclock30, myunique8);

    // If the new pixel is brighter than the background color, use it.
    if( c.getAverageLight() > backgroundBrightness) {
    L[i] = c;
    } else {
    L[i] = gBackgroundColor;
    }
    }
    }


    // This function takes a time in pseudo-milliseconds,
    // figures out brightness = f( time ), and also hue = f( time )
    // The 'low digits' of the millisecond time are used as
    // input to the brightness wave function.
    // The 'high digits' are used to select a color, so that the color
    // does not change over the course of the fade-in, fade-out
    // of one cycle of the brightness wave function.
    // The 'high digits' are also used to determine whether this pixel
    // should light at all during this cycle, based on the TWINKLE_DENSITY.
    CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
    {
    uint16_t ticks = ms >> (8-TWINKLE_SPEED);
    uint8_t fastcycle8 = ticks;
    uint8_t slowcycle8 = (ticks >> 8) ^ salt;

    uint8_t bright = 0;
    if( (slowcycle8 & 0x0E) < TWINKLE_DENSITY) {
    bright = triwave8( fastcycle8);
    }

    uint8_t hue = (slowcycle8 * 16) + salt;
    return ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
    }


    // A mostly red palette with green accents and white trim.
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
    { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
    CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
    CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
    CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green };

    // A mostly blue palette with white accents.
    // "CRGB::Gray" is used as white to keep the brightness more uniform.
    const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM =
    { CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
    CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
    CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
    CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray };

    // A pure "fairy light" palette with some brightness variations
    #define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2)
    #define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4)
    const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM =
    { CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight,
    HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight,
    QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight,
    CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight };

    // A palette of soft snowflakes with the occasional bright one
    const TProgmemRGBPalette16 Snow_p FL_PROGMEM =
    { 0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0x404040,
    0x404040, 0x404040, 0x404040, 0xFFFFFF };


    // Add or remove palette names from this list to control which color
    // palettes are used, and in what order.
    const TProgmemRGBPalette16* ActivePaletteList[] = {
    &RedGreenWhite_p,
    &BlueWhite_p,
    &RainbowColors_p,
    &FairyLight_p,
    &PartyColors_p,
    &Snow_p
    };


    // Advance to the next color palette in the list (above).
    void chooseNextColorPalette( CRGBPalette16& pal)
    {
    const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
    static uint8_t whichPalette = -1;
    whichPalette = addmod8( whichPalette, 1, numberOfPalettes);

    pal = *(ActivePaletteList[whichPalette]);
    }