├── 01_HSB_rainbow_linear └── 01_HSB_rainbow_linear.ino ├── 02_HSB_rainbow_sine └── 02_HSB_rainbow_sine.ino ├── 03_White_wave_fade └── 03_White_wave_fade.ino ├── 04_White_wave_sweep_on_color └── 04_White_wave_sweep_on_color.ino ├── 05_Lerp └── 05_Lerp.ino ├── 06_Lerp_phase_shift └── 06_Lerp_phase_shift.ino ├── 07_Fade_in_out_with_random_color └── 07_Fade_in_out_with_random_color.ino ├── 08_Fade_in_out_fragmented └── 08_Fade_in_out_fragmented.ino ├── 09_Fade_in_out_fragmented_phase └── 09_Fade_in_out_fragmented_phase.ino ├── 10_Wave_freq_shrink_and_grow └── 10_Wave_freq_shrink_and_grow.ino ├── 11_Wave_freq_shrink_and_grow_centered └── 11_Wave_freq_shrink_and_grow_centered.ino ├── 12_Wave_back_and_forth └── 12_Wave_back_and_forth.ino ├── 13_Shrink_and_grow_single └── 13_Shrink_and_grow_single.ino ├── 14_Shrink_and_grow_multiple └── 14_Shrink_and_grow_multiple.ino ├── 15_Shrink_and_grow_multiple_moving └── 15_Shrink_and_grow_multiple_moving.ino ├── 15_Shrink_and_grow_multiple_moving_v2 └── 15_Shrink_and_grow_multiple_moving_v2.ino ├── 16_snail └── 16_snail.ino ├── 17_snail_multiple └── 17_snail_multiple.ino ├── 18_scanner └── 18_scanner.ino ├── 19_phase_shift_in_place └── 19_phase_shift_in_place.ino ├── 20_interval_time_match └── 20_interval_time_match.ino ├── 21_random_blink_single └── 21_random_blink_single.ino ├── 22_random_blink_single_ragged └── 22_random_blink_single_ragged.ino ├── 23_random_blink_multiple_linked_list ├── 23_random_blink_multiple_linked_list.ino ├── Blink.h └── Blink.ino ├── 24_random_blink_multiple_simplified └── 24_random_blink_multiple_simplified.ino ├── 25_random_blink_multiple_simplified_moving └── 25_random_blink_multiple_simplified_moving.ino └── README.md /01_HSB_rainbow_linear/01_HSB_rainbow_linear.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 30 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 30 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | float fadeAnimation = 0; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, LED_AMOUNT); 14 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 15 | } 16 | 17 | void loop() { 18 | 19 | float fadeEffect = (sin(fadeAnimation) + 1) / 2; 20 | 21 | FastLED.setBrightness(fadeEffect * 255); 22 | 23 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float animation = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 127 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float fade = 0; 10 | float color = 0; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, LED_AMOUNT); 14 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 15 | } 16 | 17 | void loop() { 18 | 19 | for(int i=0; i= TWO_PI) { 27 | fade = 0; 28 | color = random(255); 29 | } 30 | 31 | FastLED.show(); 32 | 33 | fade += 0.08; 34 | } 35 | -------------------------------------------------------------------------------- /08_Fade_in_out_fragmented/08_Fade_in_out_fragmented.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 127 6 | 7 | int fragments = 6; 8 | int fragmentSize = LED_AMOUNT / fragments; 9 | 10 | CRGB leds[LED_AMOUNT]; 11 | 12 | float fade = 0; 13 | float color = 0; 14 | 15 | void setup() { 16 | FastLED.addLeds(leds, LED_AMOUNT); 17 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 18 | } 19 | 20 | int fragment = 0; 21 | 22 | 23 | void loop() { 24 | 25 | // for(int i=fragment * fragmentSize; i= TWO_PI) { 42 | fade = 0; 43 | color = random(255); 44 | fragment++; 45 | fragment = fragment % fragments; 46 | } 47 | 48 | FastLED.show(); 49 | 50 | fade += 0.08; 51 | } 52 | -------------------------------------------------------------------------------- /09_Fade_in_out_fragmented_phase/09_Fade_in_out_fragmented_phase.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | int fragmentsAmount = 1; 8 | int fragmentSize = LED_AMOUNT / fragmentsAmount; 9 | 10 | CRGB leds[LED_AMOUNT]; 11 | 12 | float fade = 0; 13 | 14 | void setup() { 15 | FastLED.addLeds(leds, LED_AMOUNT); 16 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 17 | } 18 | 19 | void loop() { 20 | for(int i=0; i 20) { 40 | fade = 0; 41 | if(fragmentsAmount >= 8) fragmentsAmount = 1; 42 | 43 | fragmentsAmount *= 2; 44 | fragmentSize = LED_AMOUNT / fragmentsAmount; 45 | FastLED.clear(); 46 | } 47 | 48 | FastLED.show(); 49 | } 50 | -------------------------------------------------------------------------------- /10_Wave_freq_shrink_and_grow/10_Wave_freq_shrink_and_grow.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float move = 0; 10 | float freq = 0; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, LED_AMOUNT); 14 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 15 | } 16 | 17 | void loop() { 18 | 19 | float shrinkage = sin(freq); 20 | shrinkage = (shrinkage + 1) / 2; 21 | 22 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float move = 0; 10 | float freq = 0; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, LED_AMOUNT); 14 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 15 | } 16 | 17 | void loop() { 18 | 19 | float shrinkage = sin(freq); 20 | shrinkage = (shrinkage + 1) / 2; 21 | 22 | int midpoint = LED_AMOUNT / 2; 23 | 24 | for(int i=0; i< midpoint; ++i){ 25 | float saturation = cos(i * shrinkage); 26 | // float saturation = sin(move - i * shrinkage); 27 | saturation = (saturation + 1) / 2; 28 | saturation *= 255; 29 | saturation = max(saturation, 30); 30 | 31 | leds[midpoint+i] = CHSV(170, saturation, 255); 32 | leds[midpoint-i] = CHSV(170, saturation, 255); 33 | } 34 | 35 | FastLED.show(); 36 | 37 | move += 0.2; 38 | freq += 0.003; 39 | } 40 | -------------------------------------------------------------------------------- /12_Wave_back_and_forth/12_Wave_back_and_forth.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float move = 0; 10 | 11 | void setup() { 12 | FastLED.addLeds(leds, LED_AMOUNT); 13 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 14 | } 15 | 16 | void loop() { 17 | 18 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | float move = 0; 10 | float period = 0; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, LED_AMOUNT); 14 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 15 | } 16 | 17 | void loop() { 18 | 19 | int midpoint = LED_AMOUNT / 2; 20 | 21 | 22 | float spread = midpoint * ((sin(move) + 1) / 2); 23 | float step = PI / (spread); 24 | 25 | FastLED.clear(); 26 | 27 | for(int i=0; i<= spread; ++i){ 28 | float brightness = cos(((i) * step)); 29 | brightness = (brightness + 1) / 2; 30 | brightness *= 255; 31 | leds[midpoint+i] = CHSV(0, 0, brightness); 32 | leds[midpoint-i] = CHSV(0, 0, brightness); 33 | } 34 | 35 | FastLED.show(); 36 | 37 | move += 0.05; 38 | 39 | if(move >= TWO_PI) move = 0; 40 | } 41 | -------------------------------------------------------------------------------- /14_Shrink_and_grow_multiple/14_Shrink_and_grow_multiple.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define FRAGMENT_AMOUNT 4 8 | #define FRAGMENT_SIZE (LED_AMOUNT / FRAGMENT_AMOUNT) 9 | #define FRAGMENT_MIDPOINT (FRAGMENT_SIZE / 2) 10 | 11 | CRGB leds[LED_AMOUNT]; 12 | 13 | float move = 0; 14 | 15 | void setup() { 16 | FastLED.addLeds(leds, LED_AMOUNT); 17 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 18 | } 19 | 20 | void loop() { 21 | 22 | float spread = FRAGMENT_MIDPOINT * ((sin(move) + 1) / 2); 23 | float step = PI / spread; 24 | 25 | FastLED.clear(); 26 | 27 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 28 | int pos = (fragment * FRAGMENT_SIZE); 29 | int midpoint = pos + FRAGMENT_MIDPOINT; 30 | 31 | for(int i=0; i<= spread; ++i){ 32 | float brightness = cos(i * step); 33 | brightness = (brightness + 1) / 2; 34 | brightness *= 255; 35 | 36 | leds[midpoint+i] = CHSV(0, 0, brightness); 37 | leds[midpoint-i] = CHSV(0, 0, brightness); 38 | } 39 | 40 | } 41 | 42 | FastLED.show(); 43 | 44 | move += 0.05; 45 | 46 | if(move >= TWO_PI) move = 0; 47 | } 48 | -------------------------------------------------------------------------------- /15_Shrink_and_grow_multiple_moving/15_Shrink_and_grow_multiple_moving.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define FRAGMENT_AMOUNT 4 8 | #define FRAGMENT_SIZE (LED_AMOUNT / FRAGMENT_AMOUNT) 9 | #define FRAGMENT_MIDPOINT (FRAGMENT_SIZE / 2) 10 | 11 | CRGB leds[LED_AMOUNT]; 12 | 13 | float move = 0; 14 | 15 | float midpoints[FRAGMENT_AMOUNT]; 16 | 17 | void setup() { 18 | FastLED.addLeds(leds, LED_AMOUNT); 19 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 20 | 21 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 22 | midpoints[fragment] = FRAGMENT_MIDPOINT + (fragment * FRAGMENT_SIZE); 23 | } 24 | } 25 | 26 | void loop() { 27 | 28 | FastLED.clear(); 29 | 30 | float spread = FRAGMENT_MIDPOINT * ((sin(move) + 1) / 2); 31 | float step = PI / spread; 32 | 33 | 34 | /* 35 | TODO: rewrite for lesser complexity. 36 | 37 | Now it computes cosine for each led in each fragment. 38 | It can compute once and then place the computation for 39 | each fragment by multiplying index. 40 | */ 41 | 42 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 43 | midpoints[fragment] += 0.05; 44 | 45 | if(midpoints[fragment] > LED_AMOUNT) midpoints[fragment] = 0; 46 | 47 | int pos = midpoints[fragment]; 48 | 49 | for(int i=0; i<= spread; ++i){ 50 | 51 | float brightness = cos(i * step); 52 | brightness = (brightness + 1) / 2; 53 | brightness *= 255; 54 | 55 | leds[(pos+i) % LED_AMOUNT] = CHSV(0, 0, brightness); 56 | 57 | if( pos - i < 0) { 58 | leds[pos - i + LED_AMOUNT] = CHSV(0, 0, brightness); 59 | } 60 | else leds[pos - i] = CHSV(0, 0, brightness); 61 | 62 | } 63 | } 64 | 65 | FastLED.show(); 66 | 67 | move += 0.05; 68 | } 69 | -------------------------------------------------------------------------------- /15_Shrink_and_grow_multiple_moving_v2/15_Shrink_and_grow_multiple_moving_v2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define FRAGMENT_AMOUNT 4 8 | #define FRAGMENT_SIZE (LED_AMOUNT / FRAGMENT_AMOUNT) 9 | #define FRAGMENT_MIDPOINT (FRAGMENT_SIZE / 2) 10 | 11 | CRGB leds[LED_AMOUNT]; 12 | 13 | float move = 0; 14 | 15 | float midpoints[FRAGMENT_AMOUNT]; 16 | 17 | void setup() { 18 | FastLED.addLeds(leds, LED_AMOUNT); 19 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 20 | 21 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 22 | midpoints[fragment] = FRAGMENT_MIDPOINT + (fragment * FRAGMENT_SIZE); 23 | } 24 | } 25 | 26 | void loop() { 27 | 28 | FastLED.clear(); 29 | 30 | float spread = FRAGMENT_MIDPOINT * ((sin(move) + 1) / 2); 31 | float step = PI / spread; 32 | 33 | 34 | /* 35 | Improved computational complexity. 36 | You can compare with v1. 37 | */ 38 | 39 | for(int i=0; i<= spread; ++i){ 40 | 41 | float brightness = cos(i * step); 42 | brightness = (brightness + 1) / 2; 43 | brightness *= 255; 44 | 45 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 46 | int pos = midpoints[fragment]; 47 | 48 | leds[(pos+i) % LED_AMOUNT] = CHSV(0, 0, brightness); 49 | 50 | if( pos - i < 0) { 51 | leds[pos - i + LED_AMOUNT] = CHSV(0, 0, brightness); 52 | } 53 | else leds[pos - i] = CHSV(0, 0, brightness); 54 | } 55 | 56 | } 57 | 58 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 59 | midpoints[fragment] += 0.05; 60 | 61 | if(midpoints[fragment] > LED_AMOUNT) midpoints[fragment] = 0; 62 | } 63 | 64 | /* 65 | for(int fragment = 0; fragment < FRAGMENT_AMOUNT; fragment++){ 66 | midpoints[fragment] += 0.05; 67 | 68 | if(midpoints[fragment] > LED_AMOUNT) midpoints[fragment] = 0; 69 | 70 | int pos = midpoints[fragment]; 71 | 72 | for(int i=0; i<= spread; ++i){ 73 | 74 | float brightness = cos(i * step); 75 | brightness = (brightness + 1) / 2; 76 | brightness *= 255; 77 | 78 | leds[(pos+i) % LED_AMOUNT] = CHSV(0, 0, brightness); 79 | 80 | if( pos - i < 0) { 81 | leds[pos - i + LED_AMOUNT] = CHSV(0, 0, brightness); 82 | } 83 | else leds[pos - i] = CHSV(0, 0, brightness); 84 | 85 | } 86 | } 87 | */ 88 | 89 | FastLED.show(); 90 | 91 | move += 0.05; 92 | } 93 | -------------------------------------------------------------------------------- /16_snail/16_snail.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define FRAGMENT_AMOUNT 4 8 | #define FRAGMENT_SIZE (LED_AMOUNT / FRAGMENT_AMOUNT) 9 | #define SNAIL_MINIMUM_SIZE 6 10 | 11 | bool isShrinking = false; 12 | 13 | float snailBegin = 0; 14 | float snailEnd = 0; 15 | 16 | CRGB leds[LED_AMOUNT]; 17 | 18 | void setup() { 19 | FastLED.addLeds(leds, LED_AMOUNT); 20 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 21 | } 22 | 23 | void loop() { 24 | 25 | int snailSize = 0; 26 | 27 | if(snailEnd >= snailBegin) snailSize = (int)snailEnd - (int)snailBegin; 28 | else snailSize = LED_AMOUNT - (int)snailBegin + (int)snailEnd; 29 | 30 | int spread = snailSize; 31 | float step = TWO_PI / (float)spread; 32 | 33 | FastLED.clear(); 34 | 35 | for(int i=0; i< spread; ++i){ 36 | 37 | float brightness = cos(PI + (i * step)); 38 | brightness = (brightness + 1) / 2; 39 | brightness *= 255; 40 | 41 | int index = ((int)snailBegin + i) % LED_AMOUNT; 42 | leds[index] = CHSV(0, 0, brightness); 43 | } 44 | 45 | FastLED.show(); 46 | 47 | if(!isShrinking){ 48 | snailEnd += 0.08; 49 | if(snailEnd >= LED_AMOUNT) snailEnd = 0; 50 | if(snailSize > FRAGMENT_SIZE) isShrinking = true; 51 | } 52 | else{ 53 | snailBegin += 0.08; 54 | if(snailBegin >= LED_AMOUNT) snailBegin = 0; 55 | if(snailSize < SNAIL_MINIMUM_SIZE) isShrinking = false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /17_snail_multiple/17_snail_multiple.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | 5 | TODO: 6 | fix parameters because last two led remain dark. 7 | 8 | */ 9 | 10 | #define LED_AMOUNT 74 11 | #define DATA_PIN 6 12 | #define GLOBAL_BRIGHTNESS 255 13 | 14 | #define FRAGMENT_AMOUNT 4 15 | #define FRAGMENT_SIZE (LED_AMOUNT / FRAGMENT_AMOUNT) 16 | #define SNAIL_MINIMUM_SIZE 6 17 | 18 | bool isShrinking = false; 19 | 20 | float snailBegin = 0; 21 | float snailEnd = 0; 22 | 23 | CRGB leds[LED_AMOUNT]; 24 | 25 | void setup() { 26 | FastLED.addLeds(leds, LED_AMOUNT); 27 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 28 | } 29 | 30 | void loop() { 31 | 32 | int snailSize = 0; 33 | 34 | if(snailEnd >= snailBegin) snailSize = (int)snailEnd - (int)snailBegin; 35 | else snailSize = FRAGMENT_SIZE - (int)snailBegin + (int)snailEnd; 36 | 37 | int spread = snailSize; 38 | float step = TWO_PI / (float)spread; 39 | 40 | FastLED.clear(); 41 | 42 | for(int i=0; i < spread; ++i){ 43 | 44 | float brightness = cos(PI + (i * step)); 45 | brightness = (brightness + 1) / 2; 46 | brightness *= 255; 47 | 48 | for(int j=0; j < FRAGMENT_AMOUNT; ++j){ 49 | int index = ((int)snailBegin + i) % FRAGMENT_SIZE; 50 | index += j * FRAGMENT_SIZE; 51 | leds[index] = CHSV(0, 0, brightness); 52 | } 53 | } 54 | 55 | FastLED.show(); 56 | 57 | if(!isShrinking){ 58 | snailEnd += 0.1; 59 | if(snailSize >= FRAGMENT_SIZE-1) isShrinking = true; 60 | if(snailEnd > FRAGMENT_SIZE) snailEnd = 0; 61 | } 62 | else{ 63 | snailBegin += 0.1; 64 | if(snailBegin >= FRAGMENT_SIZE) snailBegin = 0; 65 | if(snailSize < SNAIL_MINIMUM_SIZE) isShrinking = false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /18_scanner/18_scanner.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define SCANNER_SIZE 10 8 | 9 | CRGB leds[LED_AMOUNT]; 10 | 11 | int position = 0; 12 | bool dir = false; 13 | 14 | void setup() { 15 | FastLED.addLeds(leds, LED_AMOUNT); 16 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 17 | } 18 | 19 | void loop() { 20 | 21 | float step = TWO_PI / SCANNER_SIZE; 22 | 23 | FastLED.clear(); 24 | 25 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | float phase = 0; 9 | 10 | void setup() { 11 | FastLED.addLeds(leds, LED_AMOUNT); 12 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 13 | } 14 | 15 | void loop() { 16 | 17 | float step = TWO_PI / LED_AMOUNT; 18 | 19 | FastLED.clear(); 20 | 21 | for(int i=0; i 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 127 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | void setup() { 10 | FastLED.addLeds(leds, LED_AMOUNT); 11 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 12 | 13 | Serial.begin(115200); 14 | } 15 | 16 | int currentStep = 0; 17 | long prevTime = 0; 18 | 19 | void loop() { 20 | 21 | long time = millis(); 22 | 23 | int cycleTime = 2000; 24 | int steps = 100; 25 | int interval = cycleTime / steps; 26 | 27 | if(time - prevTime >= interval){ 28 | 29 | // currentStep += 1; 30 | // currentStep %= steps; 31 | 32 | int elapsedSteps = (time - prevTime) / interval; 33 | prevTime += elapsedSteps * interval; 34 | 35 | currentStep += elapsedSteps; 36 | currentStep %= steps; 37 | 38 | float brightness = (float)currentStep / steps; 39 | 40 | brightness = cos(PI + (TWO_PI * brightness) ); 41 | brightness = (brightness + 1) / 2; 42 | brightness *= 255; 43 | 44 | if(brightness < 10) brightness = 0; 45 | 46 | for(int i =0; i< LED_AMOUNT; i++){ 47 | leds[i] = CHSV(0, 0, brightness); 48 | } 49 | 50 | // prevTime = time; 51 | 52 | if (currentStep == 0) Serial.println(time); 53 | 54 | FastLED.show(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /21_random_blink_single/21_random_blink_single.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | void setup() { 10 | FastLED.addLeds(leds, LED_AMOUNT); 11 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 12 | } 13 | 14 | void loop() { 15 | 16 | int blinkSize = 6; 17 | int position = random(0, LED_AMOUNT - blinkSize); 18 | 19 | float spreadStep = TWO_PI / (blinkSize); 20 | float animation = 0; 21 | 22 | /* 23 | Variant 1 - Start from dark: 24 | 25 | animation < TWO_PI 26 | cos(PI + animation) 27 | 28 | Variant 2 - Start from bright and fade away: 29 | 30 | animation < PI 31 | cos(animation) 32 | */ 33 | 34 | while (animation < TWO_PI) { // TWO_PI * 3 will blink three times in same place 35 | FastLED.clear(); 36 | 37 | float fade = (cos(PI + animation) + 1) / 2; 38 | //fade = max(0.05, fade); 39 | 40 | for (int i = 0; i < blinkSize; i++) { 41 | float brightness = cos(PI + (i * spreadStep)); 42 | brightness = (brightness + 1) / 2; 43 | brightness *= 255; 44 | 45 | //brightness = max(20, brightness); 46 | brightness *= fade; 47 | 48 | leds[position + i] = CHSV(0, 0, brightness); 49 | 50 | } 51 | 52 | FastLED.show(); 53 | animation += 0.15; 54 | 55 | delay(3); // speed of blink 56 | } 57 | 58 | FastLED.clear(); 59 | FastLED.show(); 60 | 61 | //delay(100); // wait time for next blink 62 | } 63 | -------------------------------------------------------------------------------- /22_random_blink_single_ragged/22_random_blink_single_ragged.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | CRGB leds[LED_AMOUNT]; 8 | 9 | void setup() { 10 | FastLED.addLeds(leds, LED_AMOUNT); 11 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 12 | } 13 | 14 | void loop() { 15 | 16 | /* 17 | Blink pulsesAmount time and move each pulse by positionMove. 18 | */ 19 | 20 | int pulse = 0; 21 | int pulsesAmount = 3; 22 | int positionMove = 2; 23 | int blinkSize = 6; 24 | int position = random(0, LED_AMOUNT - blinkSize - (positionMove * pulsesAmount)); 25 | 26 | float spreadStep = TWO_PI / (blinkSize); 27 | float animation = 0; 28 | 29 | while (pulse < pulsesAmount) { 30 | FastLED.clear(); 31 | 32 | float fade = (cos(animation) + 1) / 2; 33 | 34 | for (int i = 0; i < blinkSize; i++) { 35 | float brightness = cos(PI + (i * spreadStep)); 36 | brightness = (brightness + 1) / 2; 37 | brightness *= 255; 38 | 39 | brightness *= fade; 40 | 41 | leds[position + i] = CHSV(0, 0, brightness); 42 | 43 | } 44 | 45 | FastLED.show(); 46 | animation += 0.15; 47 | 48 | if(animation >= PI){ 49 | animation = 0; 50 | position += positionMove; 51 | pulse++; 52 | } 53 | 54 | delay(1); 55 | } 56 | 57 | FastLED.clear(); 58 | FastLED.show(); 59 | 60 | delay(100); 61 | } 62 | -------------------------------------------------------------------------------- /23_random_blink_multiple_linked_list/23_random_blink_multiple_linked_list.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Blink.h" 3 | 4 | #define LED_AMOUNT 74 5 | #define DATA_PIN 6 6 | #define GLOBAL_BRIGHTNESS 255 7 | 8 | #define MAX_BLINK_AMOUNT 5 9 | 10 | CRGB leds[LED_AMOUNT]; 11 | 12 | LinkedList* pulses = new LinkedList(); 13 | 14 | void setup() { 15 | FastLED.addLeds(leds, LED_AMOUNT); 16 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 17 | } 18 | 19 | unsigned long prevTime = 0; 20 | 21 | int nextBlinkWaitTime = 0; 22 | 23 | void loop() { 24 | unsigned long time = millis(); 25 | 26 | FastLED.clear(); 27 | 28 | for(Blink* b = pulses->getHead(); b != nullptr; b = b->next){ 29 | 30 | blinkAnimation(b); 31 | b->animation += b->speed; 32 | 33 | if(b->animation >= TWO_PI){ 34 | pulses->remove(b); 35 | } 36 | } 37 | 38 | if(time - prevTime >= nextBlinkWaitTime){ 39 | 40 | if(!pulses->isFull()){ 41 | 42 | int size = random(3, 6); 43 | int position = random(0, LED_AMOUNT-size); 44 | //float speed = (float)random(1, 100) / 1000; 45 | float speed = 0.1; 46 | 47 | Blink* b = new Blink(position, size, speed); 48 | pulses->insert(b); 49 | 50 | } 51 | 52 | nextBlinkWaitTime = random(100); 53 | prevTime = time; 54 | } 55 | 56 | FastLED.show(); 57 | } 58 | 59 | 60 | void blinkAnimation(Blink *b){ 61 | 62 | float sinStep = TWO_PI / (b->size - 1); 63 | 64 | float fade = cos(PI + b->animation); 65 | fade = (fade + 1) / 2; 66 | 67 | for(int led=0; led < b->size; led++){ 68 | float brightness = cos(PI + (led * sinStep)); 69 | brightness = (brightness + 1) /2; 70 | brightness *= 255; 71 | 72 | brightness = max(30, brightness); 73 | //fade = max(0.05,fade); 74 | 75 | brightness *= fade; 76 | 77 | if(leds[led + b->position].b < brightness){ 78 | leds[led + b->position] = CHSV(0, 0, brightness); 79 | } 80 | 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /23_random_blink_multiple_linked_list/Blink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Blink{ 4 | Blink(int pos, int size, float speed){ 5 | this->position = pos; 6 | this->size = size; 7 | this->speed = speed; 8 | } 9 | int position; 10 | int size; 11 | float animation = 0; 12 | float speed = 0; 13 | Blink* next = nullptr; 14 | Blink* prev = nullptr; 15 | }; 16 | 17 | class LinkedList{ 18 | public: 19 | LinkedList(); 20 | ~LinkedList(); 21 | void insert(Blink* b); 22 | void remove(Blink* b); 23 | Blink* getHead(); 24 | int getLength(); 25 | bool isFull(); 26 | private: 27 | Blink* _head = nullptr; 28 | int _length = 0; 29 | }; -------------------------------------------------------------------------------- /23_random_blink_multiple_linked_list/Blink.ino: -------------------------------------------------------------------------------- 1 | LinkedList::LinkedList(){ 2 | 3 | }; 4 | 5 | LinkedList::~LinkedList(){ 6 | Blink* current = _head; 7 | 8 | while(current){ 9 | remove(current); 10 | } 11 | }; 12 | 13 | void LinkedList::insert(Blink* b) { 14 | 15 | if (_length == MAX_BLINK_AMOUNT) return; 16 | 17 | _length++; 18 | 19 | if (!_head) { 20 | _head = b; 21 | return; 22 | } 23 | 24 | Blink* temp = _head; 25 | _head = b; 26 | b->next = temp; 27 | temp->prev = _head; 28 | } 29 | 30 | void LinkedList::remove(Blink* b) { 31 | 32 | if(b == _head){ 33 | _head = b->next; 34 | if(_head){ 35 | _head->prev = nullptr; 36 | } 37 | } 38 | else{ 39 | if(b->next != nullptr){ 40 | b->prev->next = b->next; 41 | b->next->prev = b->prev; 42 | } 43 | else{ 44 | b->prev->next = nullptr; 45 | } 46 | } 47 | 48 | delete b; 49 | _length--; 50 | } 51 | 52 | int LinkedList::getLength() { 53 | return this->_length; 54 | } 55 | 56 | bool LinkedList::isFull() { 57 | return _length >= MAX_BLINK_AMOUNT; 58 | } 59 | 60 | Blink* LinkedList::getHead() { 61 | return this->_head; 62 | } -------------------------------------------------------------------------------- /24_random_blink_multiple_simplified/24_random_blink_multiple_simplified.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define BLINK_AMOUNT 5 8 | 9 | CRGB leds[LED_AMOUNT]; 10 | 11 | struct Blink{ 12 | Blink(int size, int pos, float value){ 13 | this->size = size; 14 | this->position = pos; 15 | this->value = value; 16 | } 17 | int position; 18 | int size; 19 | long count = 0; 20 | float value; 21 | }; 22 | 23 | Blink* blinks[BLINK_AMOUNT]; 24 | 25 | void setup() { 26 | FastLED.addLeds(leds, LED_AMOUNT); 27 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 28 | 29 | for(int i=0; icount){ 47 | blinkAnimation(blinks[i]); 48 | } 49 | else { 50 | blinks[i]->count--; 51 | continue; 52 | } 53 | 54 | blinks[i]->value += 0.05; 55 | 56 | if(blinks[i]->value >= TWO_PI){ 57 | blinks[i]->position = random(0, LED_AMOUNT - blinks[i]->size); 58 | blinks[i]->value = 0; 59 | blinks[i]->count = random(500); 60 | } 61 | 62 | } 63 | 64 | FastLED.show(); 65 | } 66 | 67 | 68 | void blinkAnimation(Blink *b){ 69 | 70 | float sinStep = TWO_PI / (b->size - 1); 71 | 72 | float fade = cos(PI + b->value); 73 | fade = (fade + 1) / 2; 74 | 75 | for(int led=0; led < b->size; led++){ 76 | float brightness = cos(PI + (led * sinStep)); 77 | brightness = (brightness + 1) /2; 78 | brightness *= 255; 79 | 80 | brightness = max(30, brightness); 81 | brightness *= fade; 82 | 83 | if(leds[led + b->position].b < brightness){ 84 | leds[led + b->position] = CHSV(0, 0, brightness); 85 | } 86 | 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /25_random_blink_multiple_simplified_moving/25_random_blink_multiple_simplified_moving.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_AMOUNT 74 4 | #define DATA_PIN 6 5 | #define GLOBAL_BRIGHTNESS 255 6 | 7 | #define BLINK_AMOUNT 4 8 | 9 | CRGB leds[LED_AMOUNT]; 10 | 11 | struct Blink{ 12 | Blink(float size, float pos, float value){ 13 | this->size = size; 14 | this->position = pos; 15 | this->value = value; 16 | } 17 | float position; 18 | float size; 19 | long count = 0; 20 | float value; 21 | }; 22 | 23 | Blink* blinks[BLINK_AMOUNT]; 24 | 25 | void setup() { 26 | FastLED.addLeds(leds, LED_AMOUNT); 27 | FastLED.setBrightness(GLOBAL_BRIGHTNESS); 28 | 29 | for(int i=0; icount){ 47 | blinkAnimation(blinks[i]); 48 | } 49 | else { 50 | blinks[i]->count--; 51 | continue; 52 | } 53 | 54 | blinks[i]->value += 0.1; 55 | blinks[i]->position += 0.15; 56 | blinks[i]->size += 0.2; 57 | 58 | if(blinks[i]->value >= TWO_PI){ 59 | blinks[i]->size = 5; 60 | blinks[i]->position = random(0, LED_AMOUNT - blinks[i]->size); 61 | blinks[i]->value = 0; 62 | blinks[i]->count = random(300); 63 | } 64 | 65 | } 66 | FastLED.show(); 67 | } 68 | 69 | 70 | void blinkAnimation(Blink *b){ 71 | 72 | float sinStep = TWO_PI / (b->size -1); 73 | 74 | float fade = cos(PI + b->value); 75 | fade = (fade + 1) / 2; 76 | 77 | for(int led=0; led < b->size; led++){ 78 | float brightness = cos(PI + (led * sinStep)); 79 | brightness = (brightness + 1) /2; 80 | brightness *= 255; 81 | 82 | //brightness = max(30, brightness); 83 | brightness *= fade; 84 | 85 | if((int)b->position + led > LED_AMOUNT) continue; 86 | 87 | if(leds[led + (int)b->position].b < brightness){ 88 | leds[led + (int)b->position] = CHSV(0, 0, brightness); 89 | } 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **A reference for light animations using Neopixels. During my classes I usually cover some basics of Neopixels but these examples, based on wave generation and noise function are a little more advanced and not required to pass my course. However, for some projects such effects might be necessary to achieve desired results. To understand the code, student must understand how sine wave is made and be proficient in programming.** 2 | 3 | **I decided to put them into a single repository so I don't have to search or rewrite them each semester for students project. Some of these examples you won't find anywhere else.** 4 | 5 | ### Why are some light animations better than others? 6 | - Trigonometric functions for smooth movement instead of linear increment/decrement, especially in fade-in and fade-out effects. However, there are non-smooth effects that looks great. 7 | - Sine and wave generation for continuous effects. 8 | - Perlin Noise instead of random. 9 | - Non-linear speed and acceleration of animation. 10 | - Testing animation perceived timing. 11 | - Choosing LED density based on perceived distance between light and observator. 12 | - Proper color selection instead of using rainbow everywhere and avoiding non-smooth transition (unless we want that). 13 | - Understanding differences between RGB and HSB color palette. 14 | - The color theory and perception of different colors or temperatures and how it affects illuminated objects. *It does matter where you put your light installation, its background and color or surface of nearby objects.* 15 | 16 | ### Notes: 17 | **About code examples:**: 18 | - *I'm using FastLED library (https://fastled.io/). The library includes 16 bit operations, wave and noise generation, HSV colors and is more efficient than Adafruit Neopixel Library. It's easy to use and highly recommended.* 19 | - *Most of examples require at least 1 meter strip to see the effects. Some of them looks better on denser strip, depending on distance from which you are watching the animation.* 20 | - *Presented effects are quite common. The inspiration were my observations of light effects during large-scale indoor musical events I've attended (there were a lot of them in Poland and Nederlands :)) or live streams like this : https://www.youtube.com/watch?v=wwCX_ywSqFc* or https://www.youtube.com/watch?v=vitt03C7S5o Probably there was no single line of code written for these installations (they used dedicated DMX software) but searching such video can be great inspiration. 21 | - *Examples can work on 16 MHz Arduino UNO but depending on amount of LED you might need something faster. Arduino's drawback is not allowing to use timers efficiently with interrupts so updating the strip is a blocking operation.* 22 | - *I tried to mix some of my animations but mixing more than two becomes difficult and probably you should create one single effect from scratch. Also it's not good when too many things happens at once.* 23 | 24 | ### Tips and tricks: 25 | **These trick will improve performance of your code or solve some common issues. I didn't put all of them directly into code examples because it's out of scope for my classes at Industrial Design Specialization where simplicity of code is main factor. The performance optimalization makes sense if we have 5 meter strip and slow Arduino board. It might be wiser to just buy a newer Arduino board.** 26 | - Custom `map` function implementation solves approximation problems when scaling from or to small values which is common when working with trigonometric functions. The reason is because Arduino map implementation is integer based. It can be easily found on arduino reference page of map. Remember that map function is slow. 27 | - Easiest way to normalize range -1 - 1 into 0 - 1 is `value = (sine + 1 / 2) * 255`. You can also ommit the division (which takes 14 procesor cycles) and use formula `value = (sine + 1) * 127`. Getting absolute (unsigned) value will produce different results which can be used for animations. 28 | - Incorrectly coverting between floats and integers can cause difficult to track approximation issues. 29 | - Normalized to 0 - 1 sine and cosine wave in range 0 - TWO_PI won't start from 0. You have to add PI or HALF_PI. 30 | - Sine and cosine operates in radian (1 Radian = 2PI = 360 degrees). 31 | - FastLed trigonometric functions have better bit resolution and optimized performance. I'm not using them but be aware of that. 32 | - Simple program in Processing can help you understand how your wave behaves. 33 | - WS2812 and WS2812b data transfer frequency is 800kHz. You can't go faster. Maybe check other, faster strips supported by FastLed Library? 34 | - Remove unnecessary Serial communication. 35 | - For extreme performance avoid Float operations if your microcontroller doesn't support hardware float-point unit (FPU). 36 | - It is possible to reduce computational complexity by changing structure of code to compute trigonometric functions only when you have to without repetition. 37 | - Major problem with Arduino libraries is blocking way of updating the strip. You can't do anything else during data transmission. You can't do anything about that unless you want do develop a bare-metal driver. 38 | - For powering neopixels refer to Adafruit Neopixels Uberguide. 39 | 40 | ## List of animations: 41 | 42 | ### 1-19: Basic efects with waves. 43 | They use sine and cosine wave, HSB color palette, lerp. 44 | 45 | ### 20: Interval based example. 46 | This one is important because it shows how to do a non-blocking animation which takes a certain amount of time. You can use it as a reference if you have to match BPM in your project. For example, one of project used it for breathing at defined intervals. Remember that this method is not 100% precise but enough for most scenarios. 47 | 48 | ### 21-24 Random blinking. 49 | Using sine wave blink produces more interesting and natural blink effect than just randomly triggering LED on and off. *The first example is a blocking while loop approach but it's easier to understand if you aren't familiar with structs.* 50 | 51 | Multiple blinks are quite difficult to create, the simplified version is prefered. However, it gives lesser flexibility. There are various possible implementations, here I'm using Linked List but Queue can also be used. Linked List makes sense when, for example, each pulse has different duration. Simple multiple blinks without sine wave animation is much easier to implement. For in-place removal LinkedList stores previous Node pointer to reduce O-notation. 52 | 53 | ### ?-? Perlin noise. 54 | Perlin noise, as an algorithm for generating procedural textures can be used to produce natural-behaving randomness. 55 | 56 | ### ? - ? Mixing effects. 57 | A basic example of mixing effects. Just to show how such thing can be made. 58 | 59 | --------------------------------------------------------------------------------