cookbook/intermediate/math.md
Difficulty Level: ⭐⭐ Intermediate Time to Complete: 50-60 minutes Prerequisites:
You'll Learn:
FastLED provides a rich set of mathematical functions optimized for LED animations. These functions are essential for creating precise, rhythmic, and mathematically beautiful patterns.
Fast 8-bit scaling, essential for brightness control:
// Scale value by multiplier (both 0-255)
uint8_t scaled = scale8(value, 128); // value * 128 / 255
// Example: Dim a color to 50%
CRGB color = CRGB::Red;
color.r = scale8(color.r, 128);
color.g = scale8(color.g, 128);
color.b = scale8(color.b, 128);
Common scale8 values:
255 = 100% (no change)128 = 50%64 = 25%32 = 12.5%For 16-bit values when you need more precision:
uint16_t scaled16 = scale16(value, 32768); // value * 32768 / 65535
Scale in-place (modifies the original):
CRGB color = CRGB::Blue;
color.nscale8(128); // Dims to 50%
Map a value from one range to another:
// Map analog reading (0-1023) to LED brightness (0-255)
uint8_t brightness = map(analogRead(A0), 0, 1023, 0, 255);
// Map LED position to hue range
uint8_t hue = map(i, 0, NUM_LEDS-1, 0, 255);
Similar to scale8 but preserves more detail in darker values:
// Better for dimming without losing too much color
uint8_t videoScaled = scale8_video(brightness, 64);
These functions prevent overflow/underflow:
Add with saturation (max 255):
uint8_t result = qadd8(200, 100); // Results in 255, not overflow
// Example: Brighten color safely
leds[0] = CRGB::Red;
leds[0].r = qadd8(leds[0].r, 50); // Won't overflow past 255
Subtract with saturation (min 0):
uint8_t result = qsub8(50, 100); // Results in 0, not underflow
// Example: Dim color safely
leds[0].r = qsub8(leds[0].r, 30); // Won't go below 0
16-bit versions for larger values:
uint16_t sum = qadd16(60000, 10000); // Saturates at 65535
uint16_t diff = qsub16(100, 1000); // Saturates at 0
Fast 8-bit sine wave (0-255 input, 0-255 output):
void sineWave() {
for (int i = 0; i < NUM_LEDS; i++) {
// Create sine wave pattern
uint8_t brightness = sin8(i * 255 / NUM_LEDS);
leds[i] = CHSV(160, 255, brightness);
}
}
Key points:
sin() or sinf()Cosine version (90° phase shift from sin8):
uint8_t cosValue = cos8(angle);
16-bit sine for higher precision:
// Input: 0-65535, Output: -32768 to +32767
int16_t sineValue = sin16(angle);
void animatedSine() {
static uint8_t phase = 0;
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t brightness = sin8(phase + (i * 10));
leds[i] = CHSV(160, 255, brightness);
}
phase += 4; // Wave speed
}
Create rhythmic patterns with beat generators:
Sawtooth wave that increases from 0 to 255:
// Returns value that increases over time based on BPM
uint8_t beat = beat8(60); // 60 beats per minute
void beatEffect() {
uint8_t hue = beat8(20); // Slow color change
fill_solid(leds, NUM_LEDS, CHSV(hue, 255, 255));
}
16-bit version (0-65535):
uint16_t beat = beat16(60);
Sine wave that oscillates smoothly:
// beatsin8(BPM, min_value, max_value)
uint8_t brightness = beatsin8(10, 50, 200); // Oscillates between 50-200
void pulseEffect() {
uint8_t brightness = beatsin8(12); // 12 BPM, 0-255
fill_solid(leds, NUM_LEDS, CHSV(160, 255, brightness));
}
Create wave patterns across LEDs:
void sineWaveAcrossStrip() {
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t brightness = beatsin8(
60, // Beats per minute
0, // Minimum value
255, // Maximum value
0, // Time offset
i * (255 / NUM_LEDS) // Phase offset per LED
);
leds[i] = CHSV(160, 255, brightness);
}
}
16-bit precision oscillation:
uint16_t value = beatsin16(60, 0, 1000); // Oscillates 0-1000
Fast random number generation:
uint8_t randomValue = random8(); // 0-255
uint8_t randomRange = random8(10, 50); // 10-49
uint8_t randomMax = random8(100); // 0-99
16-bit random numbers:
uint16_t random16Value = random16(); // 0-65535
uint16_t randomPos = random16(NUM_LEDS); // 0 to NUM_LEDS-1
void randomTwinkle() {
if (random8() < 50) { // 50/255 chance (~20%)
int pos = random16(NUM_LEDS);
leds[pos] = CHSV(random8(), 255, 255);
}
}
void breathe() {
// Smooth breathing using beatsin8
uint8_t brightness = beatsin8(12, 30, 255); // 12 BPM, range 30-255
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV(160, 255, brightness);
}
}
void waveChaser() {
static uint8_t offset = 0;
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t brightness = sin8(offset + (i * 20));
leds[i] = CHSV(0, 255, brightness);
}
offset += 5;
}
void multiWave() {
for (int i = 0; i < NUM_LEDS; i++) {
// Combine three waves with different speeds
uint8_t wave1 = beatsin8(10, 0, 255, 0, i * 8);
uint8_t wave2 = beatsin8(15, 0, 255, 0, i * 12);
uint8_t wave3 = beatsin8(20, 0, 255, 0, i * 16);
uint8_t combined = (wave1 + wave2 + wave3) / 3;
leds[i] = CHSV(combined, 255, 255);
}
}
void positionGradient() {
for (int i = 0; i < NUM_LEDS; i++) {
// Map position to full hue range
uint8_t hue = map(i, 0, NUM_LEDS-1, 0, 255);
leds[i] = CHSV(hue, 255, 255);
}
}
void dimEnds() {
CRGB baseColor = CRGB::Blue;
for (int i = 0; i < NUM_LEDS; i++) {
// Brighter in center, dimmer at ends
uint8_t distanceFromCenter = abs(i - NUM_LEDS/2);
uint8_t brightness = 255 - scale8(distanceFromCenter, 4);
leds[i] = baseColor;
leds[i].nscale8(brightness);
}
}
Create natural acceleration/deceleration:
uint8_t easeInQuad(uint8_t t) {
// Accelerating from zero velocity
uint16_t t16 = t;
return ((t16 * t16) >> 8);
}
uint8_t easeOutQuad(uint8_t t) {
// Decelerating to zero velocity
uint16_t t16 = 255 - t;
return 255 - ((t16 * t16) >> 8);
}
uint8_t easeInOutCubic(uint8_t t) {
if (t < 128) {
// Accelerate (first half)
return ((uint16_t)t * t * t) >> 14;
} else {
// Decelerate (second half)
uint16_t t2 = 255 - t;
return 255 - (((t2 * t2 * t2) >> 14));
}
}
void easedMovement() {
static uint8_t time = 0;
// Apply easing to position
uint8_t easedTime = easeInOutCubic(time);
uint8_t pos = map(easedTime, 0, 255, 0, NUM_LEDS-1);
FastLED.clear();
leds[pos] = CRGB::White;
time += 2;
}
// Division by powers of 2
uint8_t half = value >> 1; // Divide by 2
uint8_t quarter = value >> 2; // Divide by 4
uint8_t eighth = value >> 3; // Divide by 8
// Multiplication by powers of 2
uint8_t doubled = value << 1; // Multiply by 2
uint8_t quadruple = value << 2; // Multiply by 4
// Fast modulo for powers of 2
uint8_t wrapped = index & 0xFF; // Same as % 256
uint8_t masked = index & 0x0F; // Same as % 16
void circularMotion() {
static uint8_t angle = 0;
// Calculate position using sine and cosine
int8_t x = sin8(angle) - 128; // -128 to 127
int8_t y = cos8(angle) - 128; // -128 to 127
// Map to LED position
uint8_t pos = map(x, -128, 127, 0, NUM_LEDS-1);
FastLED.clear();
leds[pos] = CRGB::Cyan;
angle += 4;
}
void distanceFade() {
uint8_t center = NUM_LEDS / 2;
for (int i = 0; i < NUM_LEDS; i++) {
// Calculate distance from center
uint8_t distance = abs(i - center);
// Fade based on distance
uint8_t brightness = qsub8(255, scale8(distance, 20));
leds[i] = CHSV(160, 255, brightness);
}
}
void harmonicMotion() {
// Combine multiple sine waves (harmonics)
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t fundamental = beatsin8(10, 0, 255, 0, i * 16);
uint8_t harmonic2 = beatsin8(20, 0, 128, 0, i * 16);
uint8_t harmonic3 = beatsin8(30, 0, 64, 0, i * 16);
uint8_t combined = qadd8(qadd8(fundamental, harmonic2), harmonic3);
leds[i] = CHSV(i * 4, 255, combined);
}
}
// SLOW: Floating point in loop
void slowVersion() {
for (int i = 0; i < NUM_LEDS; i++) {
float angle = (i * 360.0) / NUM_LEDS;
float brightness = sin(angle * PI / 180.0) * 255;
leds[i].b = (uint8_t)brightness;
}
}
// FAST: Integer math with sin8
void fastVersion() {
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t angle = (i * 255) / NUM_LEDS;
uint8_t brightness = sin8(angle);
leds[i].b = brightness;
}
}
// Pulse
uint8_t pulse = beatsin8(BPM, MIN, MAX);
// Sawtooth
uint8_t saw = beat8(BPM);
// Triangle wave
uint8_t triangle = triwave8(angle);
// Square wave
uint8_t square = angle < 128 ? 0 : 255;
// Smooth step
uint8_t smoothed = ease8InOutCubic(linear);
#include <FastLED.h>
#define NUM_LEDS 60
#define DATA_PIN 6
CRGB leds[NUM_LEDS];
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(100);
}
void loop() {
multiWaveEffect();
FastLED.show();
FastLED.delay(1000 / 60);
}
void multiWaveEffect() {
// Create complex pattern using multiple wave functions
for (int i = 0; i < NUM_LEDS; i++) {
// Wave 1: Slow brightness oscillation
uint8_t brightness = beatsin8(8, 100, 255, 0, i * 4);
// Wave 2: Color rotation
uint8_t hue = beat8(20) + (i * 4);
// Wave 3: Position-based sine wave
uint8_t wave = sin8(i * 20 + beat8(30));
// Combine for final effect
uint8_t finalBrightness = scale8(brightness, wave);
leds[i] = CHSV(hue, 255, finalBrightness);
}
}