Ambient Lights

Difficulty: ● ● ● ○ ○

  • Did you know ?

    The swirling and rippling light effects are created by calculating each LED’s position relative to the center using angles and distances—turning math into mesmerizing colors!

No products found in parts_required metafield

How It Works ?

Button press detection: Button signal → pin goes LOW when pressed
Mode switching: Each press cycles through ambient, swirl, and ripple animations
Color animation: LEDs update colors based on position, angle, and time → creates smooth flowing effects
Button LED: Small NeoPixel LED shows a gentle rainbow when idle → lights up bright fun colors when pressed

GENESIS

Connection Guide

Snap your modules as shown above, copy the code into the Arduino IDE and Click upload

example.ino
#include <FastLED.h>
#include <Adafruit_NeoPixel.h>
#include <math.h>

/* ---------- MATRIX SETUP ---------- */
#define MATRIX_PIN 10
#define NUM_LEDS 289
#define BRIGHTNESS 40
CRGB leds[NUM_LEDS];

/* ---------- MATRIX MAPPING ---------- */
#define NUM_ROWS 21
const uint8_t rowCount[NUM_ROWS] = { 5, 9, 11, 13, 15, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 15, 15, 13, 11, 9, 5 };
const uint8_t rowOffset[NUM_ROWS] = { 6, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 6 };
const uint16_t prefix[NUM_ROWS] PROGMEM = { 0, 5, 14, 25, 38, 53, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 236, 251, 264, 275, 284 };
#define INVALID 0xFFFF
uint16_t XY(uint8_t x, uint8_t y) {
  if (y >= NUM_ROWS || x < rowOffset[y] || x >= rowOffset[y] + rowCount[y]) return INVALID;
  return pgm_read_word_near(prefix + y) + (x - rowOffset[y]);
}

/* ---------- BUTTON & BUTTON LED ---------- */
#define BUTTON_PIN 14
#define BUTTON_LED_PIN 39
#define BUTTON_PIXELS 1
Adafruit_NeoPixel buttonLED(BUTTON_PIXELS, BUTTON_LED_PIN, NEO_GRB + NEO_KHZ800);

/* ---------- CENTER ---------- */
const float CENTER_X = 8.0;
const float CENTER_Y = 10.0;

/* ---------- MODE STATE ---------- */
enum Mode { AMBIENT, SWIRL, RIPPLE };
Mode currentMode = AMBIENT;

/* ---------- BUTTON STATE ---------- */
bool lastButton = HIGH;
bool buttonPressed = false;
uint32_t lastDebounceTime = 0;
const uint32_t debounceDelay = 50;

/* ---------- ANIMATION VARIABLES ---------- */
uint8_t hue = 0;
float swirlAngle = 0;
float rippleTime = 0;

/* ---------- BUTTON LED ANIMATION ---------- */
uint8_t buttonRainbowHue = 0;
uint32_t buttonLastUpdate = 0;
uint8_t buttonBrightness = 20;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  buttonLED.begin();
  buttonLED.show();  // Init OFF

  FastLED.addLeds<WS2812B, MATRIX_PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);

  Serial.begin(115200);
}

/* ---------- LOOP ---------- */
void loop() {
  bool currentButton = digitalRead(BUTTON_PIN) == LOW;

  // Debounce
  if (currentButton != lastButton) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (currentButton && !buttonPressed) {
      buttonPressed = true;
      currentMode = (Mode)((currentMode + 1) % 3);  // Cycle animations

      // FUN COLORS on press
      uint32_t funColors[] = {
        buttonLED.Color(255, 105, 180),  // Hot Pink
        buttonLED.Color(0, 191, 255),    // Deep Sky Blue
        buttonLED.Color(127, 0, 255),    // Purple
        buttonLED.Color(255, 160, 0),    // Orange
        buttonLED.Color(0, 255, 180)     // Aqua Green
      };
      uint8_t pick = random(0, 5);
      buttonLED.setPixelColor(0, funColors[pick]);
      buttonLED.setBrightness(255);  // Full brightness
      buttonLED.show();
    } else if (!currentButton && buttonPressed) {
      buttonPressed = false;
      buttonBrightness = 20;  // return to dim rainbow
    }
  }

  lastButton = currentButton;

  // Run matrix animation
  switch (currentMode) {
    case AMBIENT: drawAmbient(); break;
    case SWIRL:   drawSwirl(); break;
    case RIPPLE:  drawRipple(); break;
  }

  // Rainbow on button LED if not pressed
  if (!buttonPressed && millis() - buttonLastUpdate > 30) {
    buttonLastUpdate = millis();
    buttonLED.setPixelColor(0, buttonLED.ColorHSV(buttonRainbowHue * 256));
    buttonLED.setBrightness(buttonBrightness);
    buttonLED.show();
    buttonRainbowHue += 5;  // Fast color change
  }

  FastLED.show();
  delay(20);
}

/* ---------- AMBIENT FADE ---------- */
void drawAmbient() {
  hue += 2;  // DOUBLE SPEED
  CHSV color(hue, 255, 255);
  for (int y = 0; y < NUM_ROWS; y++)
    for (int x = rowOffset[y]; x < rowOffset[y] + rowCount[y]; x++) {
      uint16_t i = XY(x, y);
      if (i != INVALID) leds[i] = color;
    }
}

/* ---------- SWIRLING WINDMILL ---------- */
void drawSwirl() {
  swirlAngle += 2.0;  // DOUBLE SPEED
  if (swirlAngle >= 360.0) swirlAngle -= 360.0;
  for (int y = 0; y < NUM_ROWS; y++)
    for (int x = rowOffset[y]; x < rowOffset[y] + rowCount[y]; x++) {
      uint16_t i = XY(x, y);
      if (i == INVALID) continue;
      float dx = x - CENTER_X;
      float dy = y - CENTER_Y;
      float angle = atan2(dy, dx) * 180.0 / PI;
      if (angle < 0) angle += 360.0;
      float dist = sqrt(dx * dx + dy * dy);
      float h = fmod(angle + swirlAngle + dist * 10, 360.0);
      leds[i] = CHSV(h / 360.0 * 255, 255, 255);
    }
}

/* ---------- RIPPLE WAVES ---------- */
void drawRipple() {
  rippleTime += 0.05;
  for (int y = 0; y < NUM_ROWS; y++)
    for (int x = rowOffset[y]; x < rowOffset[y] + rowCount[y]; x++) {
      uint16_t i = XY(x, y);
      if (i == INVALID) continue;
      float dx = x - CENTER_X;
      float dy = y - CENTER_Y;
      float dist = sqrt(dx * dx + dy * dy);
      float h = fmod((dist * 30.0) - (rippleTime * 100), 360.0);
      if (h < 0) h += 360.0;
      leds[i] = CHSV(h / 360.0 * 255, 255, 255);
    }
}