cookbook/basic-patterns/chases.md
Difficulty Level: ⭐ Beginner Time to Complete: 25-30 minutes Prerequisites: Animations, Timing Concepts, Solid Colors
You'll Learn:
fadeToBlackBy() to create trailing effects behind moving LEDsChase and scanner effects create the illusion of movement along your LED strip. The classic "Cylon" or "Knight Rider" scanner is an iconic example that teaches essential animation techniques.
The Cylon effect features a LED that bounces back and forth with a fading trail:
void cylon() {
static uint8_t pos = 0;
static int8_t direction = 1;
// Fade all LEDs
fadeToBlackBy(leds, NUM_LEDS, 20);
// Set current position
leds[pos] = CRGB::Red;
// Move position
pos += direction;
if (pos == 0 || pos == NUM_LEDS - 1) {
direction = -direction;
}
}
pos tracks the current LED positiondirection determines forward (1) or backward (-1) movementfadeToBlackBy() creates a trailing fadefadeToBlackBy(leds, NUM_LEDS, 20);
This function fades all LEDs toward black by the specified amount (0-255):
Track and update position with boundaries:
pos += direction;
if (pos == 0 || pos == NUM_LEDS - 1) {
direction = -direction;
}
This creates a bouncing effect at strip ends.
#include <FastLED.h>
#define LED_PIN 5
#define NUM_LEDS 60
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
void setup() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(50);
}
void cylon() {
static uint8_t pos = 0;
static int8_t direction = 1;
// Fade all LEDs
fadeToBlackBy(leds, NUM_LEDS, 20);
// Set current position
leds[pos] = CRGB::Red;
// Move position
pos += direction;
if (pos == 0 || pos == NUM_LEDS - 1) {
direction = -direction;
}
}
void loop() {
cylon();
FastLED.show();
delay(30); // Control speed
}
Create multiple scanning dots:
void multiCylon() {
static uint8_t pos1 = 0;
static uint8_t pos2 = NUM_LEDS / 2;
static int8_t direction1 = 1;
static int8_t direction2 = -1;
fadeToBlackBy(leds, NUM_LEDS, 20);
// First scanner
leds[pos1] = CRGB::Red;
pos1 += direction1;
if (pos1 == 0 || pos1 == NUM_LEDS - 1) {
direction1 = -direction1;
}
// Second scanner
leds[pos2] = CRGB::Blue;
pos2 += direction2;
if (pos2 == 0 || pos2 == NUM_LEDS - 1) {
direction2 = -direction2;
}
}
Cycle through colors as it scans:
void colorCylon() {
static uint8_t pos = 0;
static int8_t direction = 1;
static uint8_t hue = 0;
fadeToBlackBy(leds, NUM_LEDS, 20);
leds[pos] = CHSV(hue, 255, 255);
pos += direction;
if (pos == 0 || pos == NUM_LEDS - 1) {
direction = -direction;
hue += 32; // Change color at each bounce
}
}
A dot that continuously moves in one direction:
void chase() {
static uint8_t pos = 0;
fadeToBlackBy(leds, NUM_LEDS, 30);
leds[pos] = CRGB::Green;
pos++;
if (pos >= NUM_LEDS) {
pos = 0;
}
}
Create a wider scanning effect:
void wideCylon() {
static uint8_t pos = 0;
static int8_t direction = 1;
fadeToBlackBy(leds, NUM_LEDS, 20);
// Light up multiple LEDs for width
int width = 5;
for (int i = 0; i < width; i++) {
int ledPos = pos + i - (width / 2);
if (ledPos >= 0 && ledPos < NUM_LEDS) {
leds[ledPos] = CRGB::Red;
}
}
pos += direction;
if (pos == 0 || pos == NUM_LEDS - 1) {
direction = -direction;
}
}
Change speed based on position:
void acceleratingCylon() {
static uint8_t pos = 0;
static int8_t direction = 1;
static uint8_t speed = 1;
fadeToBlackBy(leds, NUM_LEDS, 20);
leds[pos] = CRGB::Red;
// Calculate speed based on distance from center
uint8_t distFromCenter = abs(pos - NUM_LEDS / 2);
speed = map(distFromCenter, 0, NUM_LEDS / 2, 1, 3);
pos += direction * speed;
if (pos <= 0 || pos >= NUM_LEDS - 1) {
direction = -direction;
pos = constrain(pos, 0, NUM_LEDS - 1);
}
}
fadeToBlackBy() value to control trail length (20-50 works well)delay() to control scan speed (20-50ms is typical)blur1d() instead of fadeToBlackBy() for smoother trailsSee the full Cylon example in the FastLED repository for more details.
Explore Rainbow Effects to learn how to create smooth color cycling patterns using HSV color space.