// Here's one last, really fun one from Mark Kriegsman
// https://gist.github.com/kriegsman/5adca44e14ad025e6d3b

#include <FastLED.h>

#define DATA_PIN    3
#define LED_COLS    16
#define LED_ROWS    16
#define BRIGHTNESS  255
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB

#define NUM_LEDS    LED_ROWS * LED_COLS

#define UPDATES_PER_SECOND 60

// Define the array of leds
CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
}

uint16_t XY(uint8_t x, uint8_t y) {
  return (y * LED_ROWS + x);
}

// Code above draw() only runs once, to set up our variables
uint32_t hue = 0;

const uint8_t kMatrixWidth = LED_COLS;
const uint8_t kMatrixHeight = LED_ROWS;
const uint8_t kBorderWidth = 2;

void draw() {
  // Apply some blurring to whatever's already on the matrix
  // Note that we never actually clear the matrix, we just constantly
  // blur it repeatedly. Since the blurring is 'lossy', there's
  // an automatic trend toward black -- by design.
  uint8_t blurAmount = 20; // beatsin8(2, 10, 255);
  blur2d(leds, kMatrixWidth, kMatrixHeight, blurAmount);

  // Use two out-of-sync sine waves
  uint8_t i = beatsin8(27, kBorderWidth, kMatrixHeight - kBorderWidth);
  uint8_t j = beatsin8(41, kBorderWidth, kMatrixWidth - kBorderWidth);

  // Also calculate some reflections
  uint8_t ni = (kMatrixWidth - 1) - i;
  uint8_t nj = (kMatrixWidth - 1) - j;

  // The color of each point shifts over time, each at a different speed.
  uint16_t ms = millis();

  leds[XY(i, j)] += CHSV(ms / 11, 200, 255);
  leds[XY(j, i)] += CHSV(ms / 13, 200, 255);
  leds[XY(ni, nj)] += CHSV(ms / 17, 200, 255);
  leds[XY(nj, ni)] += CHSV(ms / 29, 200, 255);
  leds[XY(i, nj)] += CHSV(ms / 37, 200, 255);
  leds[XY(ni, j)] += CHSV(ms / 41, 200, 255);

  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
}

void loop() {
  draw();
}