John Bradnam
Published © GPL3+

WiFi NeoPixel Cube

A colorful cube consisting of 192 RGB LEDs controlled from your phone over WiFi.

IntermediateFull instructions provided8 hours1,702
WiFi NeoPixel Cube

Things used in this project

Hardware components

8x8 WS2812B RGB Matrix Panels
×3
Wemos D1 Mini
Espressif Wemos D1 Mini
×1
LM2596 DC-DC Adjustable Buck Converter Step Down Power Supply Module
×1
Resistor 330 ohm
Resistor 330 ohm
×1
Capacitor 100 nF
Capacitor 100 nF
×1
Capacitor 1000 µF
Capacitor 1000 µF
10V or higher
×1
DC power jack panel mount
Do a search for "DC Power Plug Jack Socket Female Connector 2.1x5.5mm PCB Panel Mount"
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

STL files for your slicer. (0.2mm layer height, no supports)

Schematics

Schematic

PCB

Eagle Files

Schematic & PCB in Eagle Format

Code

NeoCubeV3.ino

Arduino
/*
  ESP8266 NeoPixel Cube Control

  This program is based upon the web interface example program from the WS2812FX library
  by Harm Aldick (www.aldick.org) which allows the mode, color, speed and brightness to
  be controlled over the web.

  This program also uses the WiFiManager library to allow WiFi credentials to be set
  via a web interface.

  This program also uses the ArduinoOTA library that allows the ESP8266 ModeMCU's program to be
  updated wirelessly. That is, without having to attach a USB cable to reprogram it. This
  is important because the NeoPixel Tree is fairly fragile.
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoOTA.h>

#include "NeoCubeFX.h"
#include "index.html.h"
#include "main.js.h"

#define LED_PIN 13   //D7
#define BTN_D1 5 //D1
#define BTN_D2 4 //D2


#define AP_NAME "NeoCubeLampAP"        // Name of AP when configuring WiFi credentials

#define WIFI_TIMEOUT 30000              // checks WiFi every ...ms. Reset after this time, if WiFi cannot reconnect.
#define HTTP_PORT    80

#define DEFAULT_COLOR      0xFF5900
#define DEFAULT_BRIGHTNESS 100
#define DEFAULT_SPEED      200
#define DEFAULT_MODE       FX_MODE_AUTO

#define BRIGHTNESS_STEP_BTN 2           // in/decrease brightness by this amount per button press
#define BRIGHTNESS_STEP     15          // in/decrease brightness by this amount per click
#define SPEED_STEP          10          // in/decrease brightness by this amount per click

unsigned long last_wifi_check_time = 0;
String modes = "";

/************************************************************************/
/*                     Object Instantiations                            */
/************************************************************************/

// Instantiate the NeoPixel driver
NeoCubeFX ws2812fx = NeoCubeFX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

// Instantiate a web server
// DELETED IN v2.4.0 => ESP8266WebServer server = ESP8266WebServer(HTTP_PORT);
ESP8266WebServer server (HTTP_PORT);

/************************************************************************/
/*                            Program Setup                             */
/************************************************************************/

void setup() 
{

  pinMode(BTN_D1, INPUT_PULLUP);
  pinMode(BTN_D2, INPUT_PULLUP);

  randomSeed(analogRead(A0));

  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println();
  Serial.println("Starting...");

  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });

  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });

  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });

  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  // Setup WS2812FX library
  Serial.println("WS2812FX setup");
  modes.reserve(5000);
  modes_setup();

  ws2812fx.init();
  ws2812fx.start();

  Serial.println("Wifi setup");

  // Local intialization. Once th WiFiManager's business is done, there is no need to keep it around
  WiFiManager wifiManager;

  // Reset saved settings for testing purposes
  // Should be commented out for normal operation
  // wifiManager.resetSettings();

  // Fetches ssid and pass from eeprom and tries to connect
  // If it does not connect it starts an access point with the specified name
  // and goes into a blocking loop awaiting configuration
  wifiManager.autoConnect(AP_NAME);

  Serial.println("HTTP server setup");
  server.on("/", srv_handle_index_html);
  server.on("/main.js", srv_handle_main_js);
  server.on("/modes", srv_handle_modes);
  server.on("/set", srv_handle_set);
  server.onNotFound(srv_handle_not_found);
  server.begin();
  Serial.println("HTTP server started.");

  ArduinoOTA.begin();

  Serial.println("ready!");
}

/************************************************************************/
/*                             Program Loop                             */
/************************************************************************/

void loop() 
{
  unsigned long now = millis();

  delay(1);

  server.handleClient();
  ws2812fx.service();
  ArduinoOTA.handle();
  checkButtons();

  if (now - last_wifi_check_time > WIFI_TIMEOUT) {
    Serial.print("Checking WiFi... ");
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi connection lost. Resetting...");
      ESP.reset();
    } else {
      Serial.println("OK");
    }
    last_wifi_check_time = now;
  }
}

/*
   Build <li> string for all modes.
*/
void modes_setup() {
  modes = "";
  for (uint8_t i = 0; i < ws2812fx.getModeCount(); i++) {
    modes += "<li><a href='#' class='m' id='";
    modes += i;
    modes += "'>";
    modes += ws2812fx.getModeName(i);
    modes += "</a></li>";
  }
}

/* #####################################################
  #  Webserver Functions
  ##################################################### */

void srv_handle_not_found() {
  server.send(404, "text/plain", "File Not Found");
}

void srv_handle_index_html() {
  server.send_P(200, "text/html", index_html);
}

void srv_handle_main_js() {
  server.send_P(200, "application/javascript", main_js);
}

void srv_handle_modes() {
  server.send(200, "text/plain", modes);
}

void srv_handle_set() 
{
  for (uint8_t i = 0; i < server.args(); i++) {
    if (server.argName(i) == "c") {
      String val = server.arg(i);
      uint32_t tmp = (uint32_t) strtol(&val[0], NULL, 16);
      if (tmp >= 0x000000 && tmp <= 0xFFFFFF) {
        ws2812fx.setColor(tmp);
      }
    }

    if (server.argName(i) == "m") {
      String val = server.arg(i);
      uint8_t tmp = (uint8_t) strtol(&val[0], NULL, 10);
      ws2812fx.setMode(tmp % ws2812fx.getModeCount());
    }

    if (server.argName(i) == "b") {
      if (server.arg(i)[0] == '-') {
        ws2812fx.decreaseBrightness(BRIGHTNESS_STEP);
      } else {
        ws2812fx.increaseBrightness(BRIGHTNESS_STEP);
      }
    }

    if (server.argName(i) == "s") {
      if (server.arg(i)[0] == '-') {
        ws2812fx.decreaseSpeed(SPEED_STEP);
      } else {
        ws2812fx.increaseSpeed(SPEED_STEP);
      }
    }
  }
  server.send(200, "text/plain", "OK");
}

//--------------------------------------------------------------------------
//Tests if buttons have been pressed

void checkButtons()
{
  if (digitalRead(BTN_D1) == LOW)
  {
    //debounce switch
    delay(10);
    if (digitalRead(BTN_D1) == LOW)
    {
      ws2812fx.increaseBrightness(BRIGHTNESS_STEP_BTN);
      delay(100);
    }
  }
  else if (digitalRead(BTN_D2) == LOW)
  {
    //debounce switch
    delay(10);
    if (digitalRead(BTN_D2) == LOW)
    {
      ws2812fx.decreaseBrightness(BRIGHTNESS_STEP_BTN);
      delay(100);
    }
  }
}

NeoCubeFX.h

C Header File
/*
  NeoCubeFX.h - Library for WS2812 LED effects.
  
  Harm Aldick - 2016
  www.aldick.org


  FEATURES
    * A lot of blinken modes and counting
    * NeoCubeFX can be used as drop-in replacement for Adafruit Neopixel Library

  NOTES
    * Uses the Adafruit Neopixel library. Get it here: 
      https://github.com/adafruit/Adafruit_NeoPixel



  LICENSE

  The MIT License (MIT)

  Copyright (c) 2016  Harm Aldick 

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.


  CHANGELOG

  2016-05-28   Initial beta release
  2016-06-03   Code cleanup, minor improvements, new modes
  2016-06-04   2 new fx, fixed setColor (now also resets _mode_color)
  2017-02-02   added external trigger functionality (e.g. for sound-to-light)
  2017-04-10   CAL - Added new modes: OFF, AUTO, LINES, LINES_RANDOM
  2019-12-19   JLB - Customised for Hard Disk Lamp - Removed modes and updated existing ones

*/


#ifndef NeoCubeFX_h
#define NeoCubeFX_h

#include "Arduino.h"
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>

#define DEFAULT_BRIGHTNESS 50
#define DEFAULT_MODE 0
#define DEFAULT_SPEED 150
#define DEFAULT_COLOR 0xFF0000

#define SPEED_MIN 0
#define SPEED_MAX 255

#define BRIGHTNESS_MIN 0
#define BRIGHTNESS_MAX 255

#define AUTO_CHANGE_DELAY_SECS    30	// CAL
#define AUTO_CHANGE_DELAY_MS     (AUTO_CHANGE_DELAY_SECS * 1000) // CAL

#define LED_COUNT 192
#define CUBE_ROW_COUNT 15
#define SIDE_COUNT 3

/*
#define MODE_COUNT 52

#define FX_MODE_STATIC                   0
#define FX_MODE_BLINK                    1
#define FX_MODE_BREATH                   2
#define FX_MODE_COLOR_WIPE               3
#define FX_MODE_COLOR_WIPE_RANDOM        4  //0
#define FX_MODE_RANDOM_COLOR             5
#define FX_MODE_SINGLE_DYNAMIC           6
#define FX_MODE_MULTI_DYNAMIC            7 //0
#define FX_MODE_RAINBOW                  8
#define FX_MODE_RAINBOW_CYCLE            9 //1
#define FX_MODE_SCAN                    10
#define FX_MODE_DUAL_SCAN               11
#define FX_MODE_FADE                    12
#define FX_MODE_THEATER_CHASE           13
#define FX_MODE_THEATER_CHASE_RAINBOW   14
#define FX_MODE_RUNNING_LIGHTS          15
#define FX_MODE_TWINKLE                 16
#define FX_MODE_TWINKLE_RANDOM          17 //0
#define FX_MODE_TWINKLE_FADE            18
#define FX_MODE_TWINKLE_FADE_RANDOM     19 //1
#define FX_MODE_SPARKLE                 20
#define FX_MODE_FLASH_SPARKLE           21
#define FX_MODE_HYPER_SPARKLE           22
#define FX_MODE_STROBE                  23
#define FX_MODE_STROBE_RAINBOW          24
#define FX_MODE_MULTI_STROBE            25
#define FX_MODE_BLINK_RAINBOW           26
#define FX_MODE_CHASE_WHITE             27
#define FX_MODE_CHASE_COLOR             28
#define FX_MODE_CHASE_RANDOM            29
#define FX_MODE_CHASE_RAINBOW           30 //1
#define FX_MODE_CHASE_FLASH             31
#define FX_MODE_CHASE_FLASH_RANDOM      32
#define FX_MODE_CHASE_RAINBOW_WHITE     33 //1
#define FX_MODE_CHASE_BLACKOUT          34
#define FX_MODE_CHASE_BLACKOUT_RAINBOW  35
#define FX_MODE_COLOR_SWEEP_RANDOM      36 //1
#define FX_MODE_RUNNING_COLOR           37
#define FX_MODE_RUNNING_RED_BLUE        38 //1
#define FX_MODE_RUNNING_RANDOM          39 //0
#define FX_MODE_LARSON_SCANNER          40
#define FX_MODE_COMET                   41
#define FX_MODE_FIREWORKS               42
#define FX_MODE_FIREWORKS_RANDOM        43 //1
#define FX_MODE_MERRY_CHRISTMAS         44 //1
#define FX_MODE_FIRE_FLICKER            45
#define FX_MODE_FIRE_FLICKER_SOFT       46
#define FX_MODE_LINES                   47
#define FX_MODE_LINES_RANDOM            48
#define FX_MODE_LINES_RAINBOW           49
#define FX_MODE_AUTO                    50
#define FX_MODE_OFF                     51
*/

#define MODE_COUNT 10

#define FX_MODE_COLOR_SWEEP             0
#define FX_MODE_WATER_DROP              1
#define FX_MODE_RAINBOW                 2
#define FX_MODE_RAINBOW_CENTER          3
#define FX_MODE_COLOR_SWEEP_RANDOM      4
#define FX_MODE_LINES                   5
#define FX_MODE_LINES_RAINBOW           6
#define FX_MODE_FIREWORKS_RANDOM        7
#define FX_MODE_AUTO                    8
#define FX_MODE_OFF                     9


/* LED locations
ROW
014                                                   120
013                                                112   121
012                                             104   113   122
011                                          096   105   114   123
010                                       088   097   106   115   124
009                                    080   089   098   107   116   125 
008                                 072   081   090   099   108   117   126
007                              064   073   082   091   100   109   118   127
006                                 065   074   083   092   101   110   119
005                                    066   075   084   093   102   111 
004                                       067   076   085   094   103
003                                          068   077   086   095
002                                             069   078    087
001                                                070   079
000                                                   071
                                                     FRONT
                                                      TOP
                            LEFT                                             RIGHT
000                          007                                              135                      
001                       006   015                                        134   143                  
002                    005   014   023                                  133   142   151               
003                 004   013   022   031                            132   141   150   159            
004              003   012   021   030   039                      131   140   149   158   167         
005           002   011   020   029   038   047                130   139   148   157   166   175      
006        001   010   019   028   037   046   055          129   138   147   156   165   174   183   
007     000   009   018   027   036   045   054   063    128   137   146   155   164   173   182   191
008        008   017   026   035   044   053   062          136   145   154   163   172   181   190   
009           016   025   034   043   052   061                144   153   162   171   180   189      
010              024   033   042   051   060                      152   161   170   179   188         
011                 032   041   050   059                            160   169   178   187            
012                    040   049   058                                  168   177   186               
013                       048   057                                        176   185                  
014                          056                                              184                      
*/

const uint8_t cirData[] PROGMEM = {
  0,   7,  63,  56 | 0x80,
  8,   1,   6,  15,  55,  62,  57,  48 | 0x80,
   16,   9,   2,   5,  14,  23,  47,  54,  61,  58,  49,  40 | 0x80,
   24,  17,  10,   3,   4,  13,  22,  31,  39,  46,  53,  60,  59,  50,  41,  32 | 0x80,
   25,  18,  11,  12,  21,  30,  38,  45,  52,  51,  42,  33 | 0x80,
   26,  19,  20,  29,  37,  44,  43,  34 | 0x80,
   27,  28,  36,  35 | 0x80
};

const uint8_t outData[] PROGMEM = {
   28,  36,  35,  27 | 0x80,
   19,  20,  21,  29,  37,  45,  44,  43,  42,  34,  26,  18 | 0x80,
   10,  11,  12,  13,  14,  22,  30,  38,  46,  54,  53,  52,  51,  50,  49,  41,  33,  25,  17,   9 | 0x80,
  1,   2,   3,   4,   5,   6,   7,  15,  23,  31,  39,  47,  55,  63,  62,  61,  60,  59,  58,  57,  56,  48,  40,  32,  24,  16,   8,   0 | 0x80
};

const uint8_t inData[] PROGMEM = {
  7,  15,  23,  31,  39,  47,  55,  63,  62,  61,  60,  59,  58,  57,  56,  48,  40,  32,  24,  16,   8,   0,   1,   2,   3,   4,   5,   6 | 0x80,
   14,  22,  30,  38,  46,  54,  53,  52,  51,  50,  49,  41,  33,  25,  17,   9,  10,  11,  12,  13 | 0x80,
   21,  29,  37,  45,  44,  43,  42,  34,  26,  18,  19,  20 | 0x80,
   28,  36,  35,  27 | 0x80
};

const uint8_t colData[] PROGMEM = {
  0 | 0x80,
  1,   8 | 0x80,
  2,   9,  16 | 0x80,
  3,  10,  17,  24 | 0x80,
  4,  11,  18,  25,  32 | 0x80,
  5,  12,  19,  26,  33,  40 | 0x80,
  6,  13,  20,  27,  34,  41,  48 | 0x80,
  7,  14,  21,  28,  35,  42,  49,  56 | 0x80,
   15,  22,  29,  36,  43,  50,  57 | 0x80,
   23,  30,  37,  44,  51,  58 | 0x80,
   31,  38,  45,  52,  59 | 0x80,
   39,  46,  53,  60 | 0x80,
   47,  54,  61 | 0x80,
   55,  62 | 0x80,
   63 | 0x80
};


const uint8_t rowData[] PROGMEM = {
    7 | 0x80,
    6,  15 | 0x80,
    5,  14,  23 | 0x80,
    4,  13,  22,  31 | 0x80,
    3,  12,  21,  30,  39 | 0x80,
    2,  11,  20,  29,  38,  47 | 0x80,
    1,  10,  19,  28,  37,  46,  55 | 0x80,
    0,   9,  18,  27,  36,  45,  54,  63 | 0x80,
    8,  17,  26,  35,  44,  53,  62 | 0x80,
   16,  25,  34,  43,  52,  61 | 0x80,
   24,  33,  42,  51,  60 | 0x80,
   32,  41,  50,  59 | 0x80,
   40,  49,  58 | 0x80,
   48,  57 | 0x80,
   56 | 0x80
};

//EEPROM handling
#define EEPROM_ADDRESS 0
#define EEPROM_MAGIC 0x0BAD0DAD
typedef struct {
  uint32_t magic;
  uint8_t mode;
  uint8_t speed;
  uint8_t brightness;
  uint32_t color;
} EEPROM_DATA;

class NeoCubeFX : public Adafruit_NeoPixel {

  typedef void (NeoCubeFX::*mode_ptr)(void);

  public:
/*
    NeoCubeFX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) {
      _mode[FX_MODE_STATIC]                = &NeoCubeFX::mode_static;
      _mode[FX_MODE_BLINK]                 = &NeoCubeFX::mode_blink;
      _mode[FX_MODE_BREATH]                = &NeoCubeFX::mode_breath;
      _mode[FX_MODE_COLOR_WIPE]            = &NeoCubeFX::mode_color_wipe;
      _mode[FX_MODE_COLOR_WIPE_RANDOM]     = &NeoCubeFX::mode_color_wipe_random;
      _mode[FX_MODE_RANDOM_COLOR]          = &NeoCubeFX::mode_random_color;
      _mode[FX_MODE_SINGLE_DYNAMIC]        = &NeoCubeFX::mode_single_dynamic;
      _mode[FX_MODE_MULTI_DYNAMIC]         = &NeoCubeFX::mode_multi_dynamic;
      _mode[FX_MODE_RAINBOW]               = &NeoCubeFX::mode_rainbow;
      _mode[FX_MODE_RAINBOW_CYCLE]         = &NeoCubeFX::mode_rainbow_cycle;
      _mode[FX_MODE_SCAN]                  = &NeoCubeFX::mode_scan;
      _mode[FX_MODE_DUAL_SCAN]             = &NeoCubeFX::mode_dual_scan;
      _mode[FX_MODE_FADE]                  = &NeoCubeFX::mode_fade;
      _mode[FX_MODE_THEATER_CHASE]         = &NeoCubeFX::mode_theater_chase;
      _mode[FX_MODE_THEATER_CHASE_RAINBOW] = &NeoCubeFX::mode_theater_chase_rainbow;
      _mode[FX_MODE_RUNNING_LIGHTS]        = &NeoCubeFX::mode_running_lights;
      _mode[FX_MODE_TWINKLE]               = &NeoCubeFX::mode_twinkle;
      _mode[FX_MODE_TWINKLE_RANDOM]        = &NeoCubeFX::mode_twinkle_random;
      _mode[FX_MODE_TWINKLE_FADE]          = &NeoCubeFX::mode_twinkle_fade;
      _mode[FX_MODE_TWINKLE_FADE_RANDOM]   = &NeoCubeFX::mode_twinkle_fade_random;
      _mode[FX_MODE_SPARKLE]               = &NeoCubeFX::mode_sparkle;
      _mode[FX_MODE_FLASH_SPARKLE]         = &NeoCubeFX::mode_flash_sparkle;
      _mode[FX_MODE_HYPER_SPARKLE]         = &NeoCubeFX::mode_hyper_sparkle;
      _mode[FX_MODE_STROBE]                = &NeoCubeFX::mode_strobe;
      _mode[FX_MODE_STROBE_RAINBOW]        = &NeoCubeFX::mode_strobe_rainbow;
      _mode[FX_MODE_MULTI_STROBE]          = &NeoCubeFX::mode_multi_strobe;
      _mode[FX_MODE_BLINK_RAINBOW]         = &NeoCubeFX::mode_blink_rainbow;
      _mode[FX_MODE_CHASE_WHITE]           = &NeoCubeFX::mode_chase_white;
      _mode[FX_MODE_CHASE_COLOR]           = &NeoCubeFX::mode_chase_color;
      _mode[FX_MODE_CHASE_RANDOM]          = &NeoCubeFX::mode_chase_random;
      _mode[FX_MODE_CHASE_RAINBOW]         = &NeoCubeFX::mode_chase_rainbow;
      _mode[FX_MODE_CHASE_FLASH]           = &NeoCubeFX::mode_chase_flash;
      _mode[FX_MODE_CHASE_FLASH_RANDOM]    = &NeoCubeFX::mode_chase_flash_random;
      _mode[FX_MODE_CHASE_RAINBOW_WHITE]   = &NeoCubeFX::mode_chase_rainbow_white;
      _mode[FX_MODE_CHASE_BLACKOUT]        = &NeoCubeFX::mode_chase_blackout;
      _mode[FX_MODE_CHASE_BLACKOUT_RAINBOW]= &NeoCubeFX::mode_chase_blackout_rainbow;
      _mode[FX_MODE_COLOR_SWEEP_RANDOM]    = &NeoCubeFX::mode_color_sweep_random;
      _mode[FX_MODE_RUNNING_COLOR]         = &NeoCubeFX::mode_running_color;
      _mode[FX_MODE_RUNNING_RED_BLUE]      = &NeoCubeFX::mode_running_red_blue;
      _mode[FX_MODE_RUNNING_RANDOM]        = &NeoCubeFX::mode_running_random;
      _mode[FX_MODE_LARSON_SCANNER]        = &NeoCubeFX::mode_larson_scanner;
      _mode[FX_MODE_COMET]                 = &NeoCubeFX::mode_comet;
      _mode[FX_MODE_FIREWORKS]             = &NeoCubeFX::mode_fireworks;
      _mode[FX_MODE_FIREWORKS_RANDOM]      = &NeoCubeFX::mode_fireworks_random;
      _mode[FX_MODE_MERRY_CHRISTMAS]       = &NeoCubeFX::mode_merry_christmas;
      _mode[FX_MODE_FIRE_FLICKER]          = &NeoCubeFX::mode_fire_flicker;
      _mode[FX_MODE_FIRE_FLICKER_SOFT]     = &NeoCubeFX::mode_fire_flicker_soft;

      _mode[FX_MODE_LINES]                 = &NeoCubeFX::mode_lines;
      _mode[FX_MODE_LINES_RANDOM]          = &NeoCubeFX::mode_lines_random;
      _mode[FX_MODE_LINES_RAINBOW]         = &NeoCubeFX::mode_lines_rainbow;
      _mode[FX_MODE_AUTO]                  = &NeoCubeFX::mode_auto;
      _mode[FX_MODE_OFF]                   = &NeoCubeFX::mode_off;

      _name[FX_MODE_STATIC]                = "Static";
      _name[FX_MODE_BLINK]                 = "Blink";
      _name[FX_MODE_BREATH]                = "Breath";
      _name[FX_MODE_COLOR_WIPE]            = "Color Wipe";
      _name[FX_MODE_COLOR_WIPE_RANDOM]     = "Color Wipe Random";
      _name[FX_MODE_RANDOM_COLOR]          = "Random Color";
      _name[FX_MODE_SINGLE_DYNAMIC]        = "Single Dynamic";
      _name[FX_MODE_MULTI_DYNAMIC]         = "Multi Dynamic";
      _name[FX_MODE_RAINBOW]               = "Rainbow";
      _name[FX_MODE_RAINBOW_CYCLE]         = "Rainbow Cycle";
      _name[FX_MODE_SCAN]                  = "Scan";
      _name[FX_MODE_DUAL_SCAN]             = "Dual Scan";
      _name[FX_MODE_FADE]                  = "Fade";
      _name[FX_MODE_THEATER_CHASE]         = "Theater Chase";
      _name[FX_MODE_THEATER_CHASE_RAINBOW] = "Theater Chase Rainbow";
      _name[FX_MODE_RUNNING_LIGHTS]        = "Running Lights";
      _name[FX_MODE_TWINKLE]               = "Twinkle";
      _name[FX_MODE_TWINKLE_RANDOM]        = "Twinkle Random";
      _name[FX_MODE_TWINKLE_FADE]          = "Twinkle Fade";
      _name[FX_MODE_TWINKLE_FADE_RANDOM]   = "Twinkle Fade Random";
      _name[FX_MODE_SPARKLE]               = "Sparkle";
      _name[FX_MODE_FLASH_SPARKLE]         = "Flash Sparkle";
      _name[FX_MODE_HYPER_SPARKLE]         = "Hyper Sparkle";
      _name[FX_MODE_STROBE]                = "Strobe";
      _name[FX_MODE_STROBE_RAINBOW]        = "Strobe Rainbow";
      _name[FX_MODE_MULTI_STROBE]          = "Multi Strobe";
      _name[FX_MODE_BLINK_RAINBOW]         = "Blink Rainbow";
      _name[FX_MODE_CHASE_WHITE]           = "Chase White";
      _name[FX_MODE_CHASE_COLOR]           = "Chase Color";
      _name[FX_MODE_CHASE_RANDOM]          = "Chase Random";
      _name[FX_MODE_CHASE_RAINBOW]         = "Chase Rainbow";
      _name[FX_MODE_CHASE_FLASH]           = "Chase Flash";
      _name[FX_MODE_CHASE_FLASH_RANDOM]    = "Chase Flash Random";
      _name[FX_MODE_CHASE_RAINBOW_WHITE]   = "Chase Rainbow White";
      _name[FX_MODE_CHASE_BLACKOUT]        = "Chase Blackout";
      _name[FX_MODE_CHASE_BLACKOUT_RAINBOW]= "Chase Blackout Rainbow";
      _name[FX_MODE_COLOR_SWEEP_RANDOM]    = "Color Sweep Random";
      _name[FX_MODE_RUNNING_COLOR]         = "Running Color";
      _name[FX_MODE_RUNNING_RED_BLUE]      = "Running Red Blue";
      _name[FX_MODE_RUNNING_RANDOM]        = "Running Random";
      _name[FX_MODE_LARSON_SCANNER]        = "Larson Scanner";
      _name[FX_MODE_COMET]                 = "Comet";
      _name[FX_MODE_FIREWORKS]             = "Fireworks";
      _name[FX_MODE_FIREWORKS_RANDOM]      = "Fireworks Random";
      _name[FX_MODE_MERRY_CHRISTMAS]       = "Merry Christmas";
      _name[FX_MODE_FIRE_FLICKER]          = "Fire Flicker";
      _name[FX_MODE_FIRE_FLICKER_SOFT]     = "Fire Flicker (soft)";

      _name[FX_MODE_LINES]                 = "Lines";
      _name[FX_MODE_LINES_RANDOM]          = "Lines Random";
      _name[FX_MODE_LINES_RAINBOW]         = "Lines Rainbow";
      _name[FX_MODE_AUTO]                  = "Auto";
      _name[FX_MODE_OFF]                   = "Off";
      
*/

    NeoCubeFX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) {
      _mode[FX_MODE_COLOR_SWEEP]           = &NeoCubeFX::mode_color_sweep;
      _mode[FX_MODE_WATER_DROP]            = &NeoCubeFX::mode_water_drop;
      _mode[FX_MODE_RAINBOW]               = &NeoCubeFX::mode_rainbow;
      _mode[FX_MODE_RAINBOW_CENTER]        = &NeoCubeFX::mode_rainbow_center;
      _mode[FX_MODE_COLOR_SWEEP_RANDOM]    = &NeoCubeFX::mode_color_sweep_random;
      _mode[FX_MODE_LINES]                 = &NeoCubeFX::mode_lines;
      _mode[FX_MODE_LINES_RAINBOW]         = &NeoCubeFX::mode_lines_rainbow;
      _mode[FX_MODE_FIREWORKS_RANDOM]      = &NeoCubeFX::mode_fireworks_random;
      _mode[FX_MODE_AUTO]                  = &NeoCubeFX::mode_auto;
      _mode[FX_MODE_OFF]                   = &NeoCubeFX::mode_off;

      _name[FX_MODE_COLOR_SWEEP]           = "Color Sweep";
      _name[FX_MODE_WATER_DROP]            = "Water Drop";
      _name[FX_MODE_RAINBOW]               = "Rainbow";
      _name[FX_MODE_RAINBOW_CENTER]        = "Rainbow Center";
      _name[FX_MODE_COLOR_SWEEP_RANDOM]    = "Color Sweep Random";
      _name[FX_MODE_LINES]                 = "Lines";
      _name[FX_MODE_LINES_RAINBOW]         = "Lines Rainbow";
      _name[FX_MODE_FIREWORKS_RANDOM]      = "Fireworks Random";
      _name[FX_MODE_AUTO]                  = "Auto";
      _name[FX_MODE_OFF]                   = "Off";
      

      _mode_index = DEFAULT_MODE;
      _speed = DEFAULT_SPEED;
      _brightness = DEFAULT_BRIGHTNESS;
      _running = false;
      _led_count = n;
      _mode_last_call_time = 0;
      _mode_delay = 0;
      _color = DEFAULT_COLOR;
      _mode_color = DEFAULT_COLOR;
      _counter_mode_call = 0;
      _counter_mode_step = 0;
    }

    void
      init(void),
      service(void),
      start(void),
      stop(void),
      setMode(uint8_t m),
      setSpeed(uint8_t s),
      increaseSpeed(uint8_t s),
      decreaseSpeed(uint8_t s),
      setColor(uint8_t r, uint8_t g, uint8_t b),
      setColor(uint32_t c),
      setBrightness(uint8_t b),
      increaseBrightness(uint8_t s),
      decreaseBrightness(uint8_t s),
      trigger(void);

    boolean 
      isRunning(void);

    uint8_t
      getMode(void),
      getSpeed(void),
      getBrightness(void),
      getModeCount(void);

    uint32_t
      getColor(void);

    const char*
      getModeName(uint8_t m);

  private:

    void
      set_panel_row_color(int led, uint32_t color),
      set_panel_row_color(int led, uint8_t red, uint8_t grn,  uint8_t blu ),
      strip_off(void),
      mode_static(void),
      mode_blink(void),
      mode_color_wipe(void),
      mode_color_wipe_random(void),
      mode_random_color(void),
      mode_single_dynamic(void),
      mode_multi_dynamic(void),
      mode_breath(void),
      mode_fade(void),
      mode_scan(void),
      mode_dual_scan(void),
      mode_theater_chase(void),
      mode_theater_chase_rainbow(void),
      mode_rainbow(void),
      mode_rainbow_center(void),
      mode_running_lights(void),
      mode_twinkle(void),
      mode_twinkle_random(void),
      mode_twinkle_fade(void),
      mode_twinkle_fade_random(void),
      mode_sparkle(void),
      mode_flash_sparkle(void),
      mode_hyper_sparkle(void),
      mode_strobe(void),
      mode_strobe_rainbow(void),
      mode_multi_strobe(void),
      mode_blink_rainbow(void),
      mode_chase_white(void),
      mode_chase_color(void),
      mode_chase_random(void),
      mode_chase_rainbow(void),
      mode_chase_flash(void),
      mode_chase_flash_random(void),
      mode_chase_rainbow_white(void),
      mode_chase_blackout(void),
      mode_chase_blackout_rainbow(void),
      mode_color_sweep_random(void),
      mode_running_color(void),
      mode_running_red_blue(void),
      mode_running_random(void),
      mode_larson_scanner(void),
      mode_comet(void),
      mode_fireworks(void),
      mode_fireworks_random(void),
      mode_merry_christmas(void),
      mode_fire_flicker(void),
      mode_fire_flicker_soft(void),
      mode_fire_flicker_int(int),
      mode_color_sweep(void),
      mode_water_drop(void),

      draw_lines(byte index, uint32_t color),
      mode_lines(void),
      mode_lines_random(void),
      mode_lines_rainbow(void),
      mode_auto(void),
      mode_off(void),
      initialiseTable(const uint8_t* a, uint8_t* t, int r),
      displayTable(const uint8_t* a, uint8_t* t, int y, uint32_t l, uint32_t m, uint32_t r),
      displayTable(const uint8_t* a, uint8_t* t, int m, int r, uint32_t c),
      readEepromData(void),
      writeEepromData(void);

    boolean
      _running,
      _triggered,
      _auto_mode;     // CAL

    uint8_t
      get_random_wheel_index(uint8_t),
      _mode_index,
      _speed,
      _brightness,
      _color_index;

    uint16_t
      _led_count;

    uint32_t
      color_wheel(uint8_t),
      _color,
      _counter_mode_call,
      _counter_mode_step,
      _mode_color,
      _mode_delay;

    unsigned long
      _mode_last_call_time,
      _auto_mode_change_time;    // CAL

    const char*
      _name[MODE_COUNT];

    mode_ptr
      _mode[MODE_COUNT];

    uint8_t 
      cirs[7],    //Used to store index of start of each circle in curData. Initialised in setup
      outs[15],   //Used to store index of start of each square in sqrData. Initialised in setup
      ins[15],    //Used to store index of start of each square in sqrData. Initialised in setup
      cols[15],   //Used to store index of start of each column in colData. Initialised in setup
      rows[15];   //Used to store index of start of each row in rowData. Initialised in setup

    EEPROM_DATA EepromData;       //Current EEPROM settings
};

#endif

NeoCubeFX.cpp

C/C++
/*
  NeoCubeFX.cpp - Library for WS2812 LED effects.

  Harm Aldick - 2016
  www.aldick.org


  FEATURES
    * A lot of blinken modes and counting
    * NeoCubeFX can be used as drop-in replacement for Adafruit Neopixel Library

  NOTES
    * Uses the Adafruit Neopixel library. Get it here: 
      https://github.com/adafruit/Adafruit_NeoPixel



  LICENSE

  The MIT License (MIT)

  Copyright (c) 2016  Harm Aldick 

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.


  CHANGELOG

  2016-05-28   Initial beta release
  2016-06-03   Code cleanup, minor improvements, new modes
  2016-06-04   2 new fx, fixed setColor (now also resets _mode_color)
  2017-02-02   added external trigger functionality (e.g. for sound-to-light)
  2017-02-02   removed "blackout" on mode, speed or color-change
  2017-04-10   CAL - Added new modes: OFF and AUTO
  2021-01-08   JLB - Modified modes for NeoCube, added new modes, added EEPROM support

*/

#include "Arduino.h"
#include "NeoCubeFX.h"

#define CALL_MODE(n) (this->*_mode[n])();

void NeoCubeFX::init() 
{
  //Setup rows, cols and square arrays with starting indexes
  initialiseTable(rowData, rows, 15);
  initialiseTable(colData, cols, 15);
  initialiseTable(inData, ins, 4);
  initialiseTable(outData, outs, 4);
  initialiseTable(cirData, cirs, 7);

  readEepromData();
  _mode_index = EepromData.mode;
  _color = EepromData.color;
  _speed = EepromData.speed;
  _brightness = EepromData.brightness;

  Adafruit_NeoPixel::begin();
  NeoCubeFX::setBrightness(_brightness);
  Adafruit_NeoPixel::show();
}

void NeoCubeFX::service() {
  if(_running || _triggered) {
    unsigned long now = millis();

    // Begin new auto mode - CAL
    // Is auto_mode enabled ?
    if (_auto_mode && (now > _auto_mode_change_time)) {
      // Calculate next mode change time
      _auto_mode_change_time = now + AUTO_CHANGE_DELAY_MS;

      // Turn off all LEDs      
      strip_off();

      // Setup to call random mode with random color, speed and brightness
      _counter_mode_call = 0;
      _counter_mode_step = 0;
      _mode_last_call_time = 0;

      // Select a random mode
      _mode_index = random(MODE_COUNT);
  
      // Select a random color
      _color = _mode_color = color_wheel(random(256));
  
      // Select a random brightness
      _brightness = random(20, 125);
      Adafruit_NeoPixel::setBrightness(_brightness);

      // Select a random speed
      _speed = random(SPEED_MIN, SPEED_MAX);
    }
    // End new auto mode - CAL

    if(now - _mode_last_call_time > _mode_delay || _triggered) {
      CALL_MODE(_mode_index);
      _counter_mode_call++;
      _mode_last_call_time = now;
      _triggered = false;
    }
  }
}

void NeoCubeFX::start() {
  _counter_mode_call = 0;
  _counter_mode_step = 0;
  _mode_last_call_time = 0;
  _running = true;
}

void NeoCubeFX::stop() {
  _running = false;
  strip_off();
}

void NeoCubeFX::trigger() {
  _triggered = true;
}

void NeoCubeFX::setMode(uint8_t m) {

  _auto_mode = false;		// CAL
  _auto_mode_change_time= 0;	// CAL

  _counter_mode_call = 0;
  _counter_mode_step = 0;
  _mode_last_call_time = 0;
  _mode_index = constrain(m, 0, MODE_COUNT-1);
  writeEepromData();
  _mode_color = _color;
  Adafruit_NeoPixel::setBrightness(_brightness);
  //strip_off();
}

void NeoCubeFX::setSpeed(uint8_t s) {
  _counter_mode_call = 0;
  _counter_mode_step = 0;
  _mode_last_call_time = 0;
  _speed = constrain(s, SPEED_MIN, SPEED_MAX);
  writeEepromData();
  Serial.println("Speed = " + String(_speed));
  //strip_off();
}

void NeoCubeFX::increaseSpeed(uint8_t s) {
  s = constrain(_speed + s, SPEED_MIN, SPEED_MAX);
  setSpeed(s);
}

void NeoCubeFX::decreaseSpeed(uint8_t s) {
  s = constrain(_speed - s, SPEED_MIN, SPEED_MAX);
  setSpeed(s);
}

void NeoCubeFX::setColor(uint8_t r, uint8_t g, uint8_t b) {
  setColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
}

void NeoCubeFX::setColor(uint32_t c) {
  _color = c;
  writeEepromData();
  _counter_mode_call = 0;
  _counter_mode_step = 0;
  _mode_last_call_time = 0;
  _mode_color = _color;
  Adafruit_NeoPixel::setBrightness(_brightness);
  //strip_off();
}

void NeoCubeFX::setBrightness(uint8_t b) {
  _brightness = constrain(b, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  writeEepromData();
  Adafruit_NeoPixel::setBrightness(_brightness);
  Adafruit_NeoPixel::show();
}

void NeoCubeFX::increaseBrightness(uint8_t s) {
  s = constrain(_brightness + s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  setBrightness(s);
}

void NeoCubeFX::decreaseBrightness(uint8_t s) {
  s = constrain(_brightness - s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  setBrightness(s);
}

boolean NeoCubeFX::isRunning() {
  return _running; 
}

uint8_t NeoCubeFX::getMode(void) {
  return _mode_index;
}

uint8_t NeoCubeFX::getSpeed(void) {
  return _speed;
}

uint8_t NeoCubeFX::getBrightness(void) {
  return _brightness;
}

uint8_t NeoCubeFX::getModeCount(void) {
  return MODE_COUNT;
}

uint32_t NeoCubeFX::getColor(void) {
  return _color; 
}

const char* NeoCubeFX::getModeName(uint8_t m) {
  if(m < MODE_COUNT) {
    return _name[m];
  } else {
    return "";
  }
}

void NeoCubeFX::set_panel_row_color(int led, uint32_t color)
{
  if (led < CUBE_ROW_COUNT)
  {
    displayTable(rowData, rows, led, color, color, color);
  }
}

void NeoCubeFX::set_panel_row_color(int led, uint8_t red, uint8_t grn,  uint8_t blu )
{
  if (led < CUBE_ROW_COUNT)
  {
    uint32_t color = Color(red, grn, blu);
    displayTable(rowData, rows, led, color, color, color);
  }
}

/* #####################################################
#
#  Color and Blinken Functions
#
##################################################### */

/*
 * Turns everything off. Doh.
 */
void NeoCubeFX::strip_off() {
  Adafruit_NeoPixel::clear();
  Adafruit_NeoPixel::show();
}


/*
 * Put a value 0 to 255 in to get a color value.
 * The colours are a transition r -> g -> b -> back to r
 * Inspired by the Adafruit examples.
 */
uint32_t NeoCubeFX::color_wheel(uint8_t pos) 
{
  pos = 255 - pos;
  if(pos < 85) 
  {
    return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
  } 
  else if(pos < 170) 
  {
    pos -= 85;
    return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
  } 
  else 
  {
    pos -= 170;
    return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
  }
}


/*
 * Returns a new, random wheel index with a minimum distance of 42 from pos.
 */
uint8_t NeoCubeFX::get_random_wheel_index(uint8_t pos) 
{
  uint8_t r = 0;
  uint8_t x = 0;
  uint8_t y = 0;
  uint8_t d = 0;

  while(d < 42) 
  {
    r = random(256);
    x = abs(pos - r);
    y = 255 - x;
    d = min(x, y);
  }

  return r;
}


/*
 * No blinking. Just plain old static light.
 */
void NeoCubeFX::mode_static(void) 
{
  for(uint16_t i=0; i < _led_count; i++) 
  {
    Adafruit_NeoPixel::setPixelColor(i, _color);
  }
  Adafruit_NeoPixel::show();

  _mode_delay = 50;
}


/*
 * Normal blinking. 50% on/off time.
 */
void NeoCubeFX::mode_blink(void) 
{
  if(_counter_mode_call % 2 == 1) 
  {
    for(uint16_t i=0; i < _led_count; i++) 
    {
      Adafruit_NeoPixel::setPixelColor(i, _color);
    }
    Adafruit_NeoPixel::show();
  } 
  else 
  {
    strip_off();
  }

  _mode_delay = 100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Lights all LEDs after each other up. Then turns them in
 * that order off. Repeat.
 */
void NeoCubeFX::mode_color_wipe(void) 
{
  if(_counter_mode_step < CUBE_ROW_COUNT) 
  {
    set_panel_row_color(_counter_mode_step, _color);
  } 
  else 
  {
    set_panel_row_color(_counter_mode_step - _led_count, 0);
  }
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % (_led_count * 2);

  _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count);
}


/*
 * Turns all LEDs after each other to a random color.
 * Then starts over with another color.
 */
void NeoCubeFX::mode_color_wipe_random(void) 
{
  if(_counter_mode_step == 0) 
  {
    _mode_color = get_random_wheel_index(_mode_color);
  }

  set_panel_row_color(_counter_mode_step, color_wheel(_mode_color));
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;

  _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * Lights all LEDs in one random color up. Then switches them
 * to the next random color.
 */
void NeoCubeFX::mode_random_color(void) 
{
  _mode_color = get_random_wheel_index(_mode_color);

  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) 
  {
    set_panel_row_color(i, color_wheel(_mode_color));
  }
  
  Adafruit_NeoPixel::show();
  _mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Lights every LED in a random color. Changes one random LED after the other
 * to another random color.
 */
void NeoCubeFX::mode_single_dynamic(void) 
{
  if(_counter_mode_call == 0) 
  {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) 
    {
      set_panel_row_color(i, color_wheel(random(256)));
    }
  }

  set_panel_row_color(random(CUBE_ROW_COUNT), color_wheel(random(256)));
  Adafruit_NeoPixel::show();
  _mode_delay = 10 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Lights every LED in a random color. Changes all LED at the same time
 * to new random colors.
 */
void NeoCubeFX::mode_multi_dynamic(void) {
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, color_wheel(random(256)));
  }
  Adafruit_NeoPixel::show();
  _mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Does the "standby-breathing" of well known i-Devices. Fixed Speed.
 * Use mode "fade" if you like to have something similar with a different speed.
 */
void NeoCubeFX::mode_breath(void) {
  //                                      0    1    2   3   4   5   6    7   8   9  10  11   12   13   14   15   16    // step
  uint16_t breath_delay_steps[] =     {   7,   9,  13, 15, 16, 17, 18, 930, 19, 18, 15, 13,   9,   7,   4,   5,  10 }; // magic numbers for breathing LED
  uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16,  15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers!

  if(_counter_mode_call == 0) {
    _mode_color = breath_brightness_steps[0] + 1;
  }

  uint8_t breath_brightness = _mode_color; // we use _mode_color to store the brightness

  if(_counter_mode_step < 8) {
    breath_brightness--;
  } else {
    breath_brightness++;
  }

  // update index of current delay when target brightness is reached, start over after the last step
  if(breath_brightness == breath_brightness_steps[_counter_mode_step]) {
    _counter_mode_step = (_counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t));
  }
  
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, _color);           // set all LEDs to selected color
  }
  int b = map(breath_brightness, 0, 255, 0, _brightness);  // keep brightness below brightness set by user
  Adafruit_NeoPixel::setBrightness(b);                     // set new brightness to leds
  Adafruit_NeoPixel::show();

  _mode_color = breath_brightness;                         // we use _mode_color to store the brightness
  _mode_delay = breath_delay_steps[_counter_mode_step];
}


/*
 * Fades the LEDs on and (almost) off again.
 */
void NeoCubeFX::mode_fade(void) {
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, _color);
  }

  int b = _counter_mode_step - 127;
  b = 255 - (abs(b) * 2);
  b = map(b, 0, 255, min(25, (int)_brightness), _brightness);
  Adafruit_NeoPixel::setBrightness(b);
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % 256;
  _mode_delay = 5 + ((15 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Runs a single pixel back and forth.
 */
void NeoCubeFX::mode_scan(void) {
  if(_counter_mode_step > (CUBE_ROW_COUNT*2) - 2) {
    _counter_mode_step = 0;
  }
  _counter_mode_step++;

  int i = _counter_mode_step - (CUBE_ROW_COUNT - 1);
  i = abs(i);

  Adafruit_NeoPixel::clear();
  set_panel_row_color(abs(i), _color);
  Adafruit_NeoPixel::show();

  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * Runs two pixel back and forth in opposite directions.
 */
void NeoCubeFX::mode_dual_scan(void) {
  if(_counter_mode_step > (CUBE_ROW_COUNT*2) - 2) {
    _counter_mode_step = 0;
  }
  _counter_mode_step++;

  int i = _counter_mode_step - (CUBE_ROW_COUNT - 1);
  i = abs(i);

  Adafruit_NeoPixel::clear();
  set_panel_row_color(i, _color);
  set_panel_row_color(CUBE_ROW_COUNT - (i+1), _color);
  Adafruit_NeoPixel::show();

  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * Cycles all LEDs at once through a rainbow.
 */
void NeoCubeFX::mode_rainbow(void) {
  uint32_t color = color_wheel(_counter_mode_step);
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, color);
  }
  Adafruit_NeoPixel::show();
  _counter_mode_step = (_counter_mode_step + 1) % 256;
  _mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Cycles a rainbow over the entire string of LEDs.
 */
void NeoCubeFX::mode_rainbow_center(void) 
{
  for(uint16_t i=0; i < ((CUBE_ROW_COUNT + 1) / 2); i++) 
  {
    uint32_t c = color_wheel(((i * 256 / CUBE_ROW_COUNT) + _counter_mode_step) % 256);
    set_panel_row_color(i, c);
    if ((14 - i) != i)
    {
      set_panel_row_color(14 - i, c);
    }
  }
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % 256;
  _mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Theatre-style crawling lights.
 * Inspired by the Adafruit examples.
 */
void NeoCubeFX::mode_theater_chase(void) {
  uint8_t j = _counter_mode_call % 6;
  if(j % 2 == 0) {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
      set_panel_row_color(i+(j/2), _color);
    }
    Adafruit_NeoPixel::show();
    _mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  } else {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
      set_panel_row_color(i+(j/2), 0);
    }
    _mode_delay = 1;
  }
}


/*
 * Theatre-style crawling lights with rainbow effect.
 * Inspired by the Adafruit examples.
 */
void NeoCubeFX::mode_theater_chase_rainbow(void) {
  uint8_t j = _counter_mode_call % 6;
  if(j % 2 == 0) {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
      set_panel_row_color(i+(j/2), color_wheel((i+_counter_mode_step) % 256));
    }
    Adafruit_NeoPixel::show();
    _mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  } else {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
      set_panel_row_color(i+(j/2), 0);
    }
    _mode_delay = 1;
  }
  _counter_mode_step = (_counter_mode_step + 1) % 256;
}


/*
 * Running lights effect with smooth sine transition.
 */
void NeoCubeFX::mode_running_lights(void) {
  uint8_t r = ((_color >> 16) & 0xFF);
  uint8_t g = ((_color >> 8) & 0xFF);
  uint8_t b = (_color & 0xFF);

  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    int s = (sin(i+_counter_mode_call) * 127) + 128;
     set_panel_row_color(i, (((uint32_t)(r * s)) / 255), (((uint32_t)(g * s)) / 255), (((uint32_t)(b * s)) / 255));
  }

  Adafruit_NeoPixel::show();

  _mode_delay = 35 + ((350 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Blink several LEDs on, reset, repeat.
 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
 */
void NeoCubeFX::mode_twinkle(void) {
  if(_counter_mode_step == 0) {
    strip_off();
    uint16_t min_leds = max(1, _led_count/5); // make sure, at least one LED is on
    uint16_t max_leds = max(1, _led_count/2); // make sure, at least one LED is on
    _counter_mode_step = random(min_leds, max_leds);
  }

  Adafruit_NeoPixel::setPixelColor(random(_led_count), _mode_color);
  Adafruit_NeoPixel::show();

  _counter_mode_step--;
  _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Blink several LEDs in random colors on, reset, repeat.
 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
 */
void NeoCubeFX::mode_twinkle_random(void) {
  _mode_color = color_wheel(random(256));
  mode_twinkle();
}


/*
 * Blink several LEDs on, fading out.
 */
void NeoCubeFX::mode_twinkle_fade(void) 
{
  for(uint16_t i=0; i < _led_count; i++) 
  {
    uint32_t px_rgb = Adafruit_NeoPixel::getPixelColor(i);

    byte px_r = (px_rgb & 0x00FF0000) >> 16;
    byte px_g = (px_rgb & 0x0000FF00) >>  8;
    byte px_b = (px_rgb & 0x000000FF) >>  0;

    // fade out (divide by 2)
    px_r = px_r >> 1;
    px_g = px_g >> 1;
    px_b = px_b >> 1;

    Adafruit_NeoPixel::setPixelColor(i, px_r, px_g, px_b);
  }

  if(random(3) == 0) 
  {
    Adafruit_NeoPixel::setPixelColor(random(_led_count), _mode_color);
  }

  Adafruit_NeoPixel::show();

  _mode_delay = 100 + ((100 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Blink several LEDs in random colors on, fading out.
 */
void NeoCubeFX::mode_twinkle_fade_random(void) 
{
  _mode_color = color_wheel(random(256));
  mode_twinkle_fade();
}


/*
 * Blinks one LED at a time.
 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
 */
void NeoCubeFX::mode_sparkle(void) {
  Adafruit_NeoPixel::clear();
  Adafruit_NeoPixel::setPixelColor(random(_led_count),_color);
  Adafruit_NeoPixel::show();
  _mode_delay = 10 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * Lights all LEDs in the _color. Flashes single white pixels randomly.
 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
 */
void NeoCubeFX::mode_flash_sparkle(void) {
  for(uint16_t i=0; i < _led_count; i++) {
    Adafruit_NeoPixel::setPixelColor(i, _color);
  }

  if(random(10) == 7) {
    Adafruit_NeoPixel::setPixelColor(random(_led_count), 255, 255, 255);
    _mode_delay = 20;
  } else {
    _mode_delay = 20 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  }

  Adafruit_NeoPixel::show();
}


/*
 * Like flash sparkle. With more flash.
 * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
 */
void NeoCubeFX::mode_hyper_sparkle(void) {
  for(uint16_t i=0; i < _led_count; i++) {
    Adafruit_NeoPixel::setPixelColor(i, _color);
  }

  if(random(10) < 4) {
    for(uint16_t i=0; i < max(1, _led_count/3); i++) {
      Adafruit_NeoPixel::setPixelColor(random(_led_count), 255, 255, 255);
    }
    _mode_delay = 20;
  } else {
    _mode_delay = 15 + ((120 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  }

  Adafruit_NeoPixel::show();
}


/*
 * Classic Strobe effect.
 */
void NeoCubeFX::mode_strobe(void) {
  if(_counter_mode_call % 2 == 0) {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
      set_panel_row_color(i, _color);
    }
    _mode_delay = 20;
  } else {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
      set_panel_row_color(i, 0);
    }
    _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  }
  Adafruit_NeoPixel::show();
}


/*
 * Strobe effect with different strobe count and pause, controled by _speed.
 */
void NeoCubeFX::mode_multi_strobe(void) {
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, 0);
  }

  if(_counter_mode_step < (2 * ((_speed / 10) + 1))) {
    if(_counter_mode_step % 2 == 0) {
      for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
        set_panel_row_color(i, _color);
      }
      _mode_delay = 20;
    } else {
      _mode_delay = 50;
    }

  } else {
    _mode_delay = 100 + ((9 - (_speed % 10)) * 125);
  }

  Adafruit_NeoPixel::show();
  _counter_mode_step = (_counter_mode_step + 1) % ((2 * ((_speed / 10) + 1)) + 1);
}


/*
 * Classic Strobe effect. Cycling through the rainbow.
 */
void NeoCubeFX::mode_strobe_rainbow(void) {
  if(_counter_mode_call % 2 == 0) {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
      set_panel_row_color(i, color_wheel(_counter_mode_call % 256));
    }
    _mode_delay = 20;
  } else {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
      set_panel_row_color(i, 0);
    }
    _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
  }
  Adafruit_NeoPixel::show();
}


/*
 * Classic Blink effect. Cycling through the rainbow.
 */
void NeoCubeFX::mode_blink_rainbow(void) {
  if(_counter_mode_call % 2 == 1) {
    for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
      set_panel_row_color(i, color_wheel(_counter_mode_call % 256));
    }
    Adafruit_NeoPixel::show();
  } else {
    strip_off();
  }

  _mode_delay = 100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}


/*
 * _color running on white.
 */
void NeoCubeFX::mode_chase_white(void) {
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, 255, 255, 255);
  }

  uint16_t n = _counter_mode_step;
  uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  set_panel_row_color(n, _color);
  set_panel_row_color(m, _color);
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * White running on _color.
 */
void NeoCubeFX::mode_chase_color(void) {
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, _color);
  }

  uint16_t n = _counter_mode_step;
  uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  set_panel_row_color(n, 255, 255, 255);
  set_panel_row_color(m, 255, 255, 255);
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * White running followed by random color.
 */
void NeoCubeFX::mode_chase_random(void) {
  if(_counter_mode_step == 0) {
    set_panel_row_color(CUBE_ROW_COUNT-1, color_wheel(_mode_color));
    _mode_color = get_random_wheel_index(_mode_color);
  }

  for(uint16_t i=0; i < _counter_mode_step; i++) {
    set_panel_row_color(i, color_wheel(_mode_color));
  }

  uint16_t n = _counter_mode_step;
  uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  set_panel_row_color(n, 255, 255, 255);
  set_panel_row_color(m, 255, 255, 255);

  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * White running on rainbow.
 */
void NeoCubeFX::mode_chase_rainbow(void) 
{
  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) 
  {
    set_panel_row_color(i, color_wheel(((i * 256 / CUBE_ROW_COUNT) + (_counter_mode_call % 256)) % 256));
  }

  uint16_t n = _counter_mode_step;
  uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  set_panel_row_color(n, 255, 255, 255);
  set_panel_row_color(m, 255, 255, 255);
  Adafruit_NeoPixel::show();

  _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
  _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}


/*
 * White flashes running on _color.
 */
void NeoCubeFX::mode_chase_flash(void) {
  const static uint8_t flash_count = 4;
  uint8_t flash_step = _counter_mode_call % ((flash_count * 2) + 1);

  for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
    set_panel_row_color(i, _color);
  }

  if(flash_step < (flash_count * 2)) {
    if(flash_step % 2 == 0) {
      uint16_t n = _counter_mode_step;
      uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
      set_panel_row_color(n, 255, 255, 255);
      set_panel_row_color(m, 255, 255, 255);
      _mode_delay = 20;
    } else {
      _mode_delay = 30;
    }
  } else {
    _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
    _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
  }

  Adafruit_NeoPixel::show();
}


/*
 * White flashes running, followed by random color.
 */
void NeoCubeFX::mode_chase_flash_random(void) {
  const static uint8_t flash_count = 4;
  uint8_t flash_step = _counter_mode_call % ((flash_count * 2) + 1);

  for(uint16_t i=0; i < _counter_mode_step; i++) {
    set_panel_row_color(i, color_wheel(_mode_color));
  }

  if(flash_step < (flash_count * 2)) {
    uint16_t n = _counter_mode_step;
    uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
    if(flash_step % 2 == 0) {
      set_panel_row_color(n, 255, 255, 255);
      set_panel_row_color(m, 255, 255, 255);
      _mode_delay = 20;
    } else {
      set_panel_row_color(n, color_wheel(_mode_color));
      set_panel_row_color(m, 0, 0, 0);
      _mode_delay = 30;
    }
  } else {
    _counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
    _mode_delay = 1 + ((10 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);

    if(_counter_mode_step == 0) {
      _mode_color = get_random_wheel_index(_mode_color);
    }
  }

  Adafruit_NeoPixel::show();
}


/*
 * Rainbow running on white.
...

This file has been truncated, please download it to see its full contents.

index.html.h

C Header File
const char index_html[] PROGMEM = R"=====(
<!DOCTYPE html>
<html lang='en'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <meta name='viewport' content='width=device-width' />

  <title>Hard Disk Lamp Control</title>

  <script type='text/javascript' src='main.js'></script>

  <style>
  * {
    font-family:sans-serif;
    margin:0;
    padding:0;
  }

  body {
    width:100%;
    max-width:675px;
    background-color:#202020;
  }
  
  h1 {
    margin:25px 0 25px 0;
    color:#4545FF;
    text-align:center;
    font-size: 16pt;
  }
  
  #colorbar {
    float:left;
    display:none;
  }
  
  #adjust {
    width:30%;
    display:inline-block;
    float:right;
  }

  #controls {
    width:65%;
    display:inline-block;
    padding-left:5px;
  }

  p {
    align:center;
    font-size: 8px;
    font-family: Tahoma;
    color: white;
  }

  ul {
    text-align:center;
  }

  ul#mode li {
    display:block;
  }

  ul.brightness li, ul.speed li {
    display:block;
    _width:30%;
  }

  ul li a {
    display:block;
    margin:3px;
    padding:10px 5px;
    border:2px solid #454545;
    border-radius:5px;
    color:#4545FF;
    font-weight:bold;
    text-decoration:none;
  }

  ul li a.active {
    border:2px solid #909090;
    color:#909090;
  }
  </style>
</head>
<body>
  <h1>NeoCube Lamp Control</h1>
  <canvas id='colorbar' width='75' height='1080'></canvas>
  <div id='adjust'>
    <p class='brightness'>Brightness</p>
    <ul class='brightness'>
      <li><a href='#' class='b' id='+'>&#9728;</a></li>
    </ul>
    <ul class='brightness' id='brightness'>
      <li><a href='#' class='b' id='-'>&#9788;</a></li>
    </ul>
    <p>&nbsp;</p>
    <p class='speed'>Speed</p>
    <ul class='speed' id='speed'>
      <li><a href='#' class='s' id='+'>&#43;</a></li>
    </ul>
    <ul class='speed' id='speed'>
      <li><a href='#' class='s' id='-'>&#8722;</a></li>
    </ul>
  </div>
  <div id='controls'>
    <ul id='mode'></ul>
  </div>
</body>
</html>
)=====";

main.js.h

C Header File
const char main_js[] PROGMEM = R"=====(
window.addEventListener('load', setup);
window.addEventListener('resize', drawColorbar);

function handle_M_B_S(e) {
  e.preventDefault();
  var name = e.target.className;
  var val = e.target.id;
  if(e.target.className.indexOf('m') > -1) {
    elems = document.querySelectorAll('#mode li a');
    [].forEach.call(elems, function(el) {
      el.classList.remove('active');
      name = e.target.className;
    });
    e.target.classList.add('active');
  }
  submitVal(name, val);
}

function submitVal(name, val) {
  var xhttp = new XMLHttpRequest();
  xhttp.open('GET', 'set?' + name + '=' + val, true);
  xhttp.send();
}

function compToHex(c) {
  hex = c.toString(16);
  return hex.length == 1 ? '0' + hex : hex;
}

function getMousePos(can, evt) {
  r = can.getBoundingClientRect();
  return {
    x: evt.clientX - r.left,
    y: evt.clientY - r.top
  };
}

function Touch(e) {
  e.preventDefault();
  pos = {
    x: Math.round(e.targetTouches[0].pageX),
    y: Math.round(e.targetTouches[0].pageY)
  };
  rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
  drawColorbar(rgb);
  submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}

function Click(e) {
  pos = getMousePos(can, e);
  rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
  drawColorbar(rgb);
  submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}

// Thanks to the backup at http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
function rgbToHsl(r, g, b){
  r = r / 255;
  g = g / 255;
  b = b / 255;
  var max = Math.max(r, g, b);
  var min = Math.min(r, g, b);
  var h, s, l = (max + min) / 2;
  if(max == min) {
    h = s = 0;
  } else {
    var d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch(max) {
      case r: h = (g - b) / d + (g < b ? 6 : 0); break;
      case g: h = (b - r) / d + 2; break;
      case b: h = (r - g) / d + 4; break;
    }
    h = h / 6;
  }
  return [h, s, l];
}

function drawColorbar(rgb = [0, 0, 0]) {
  can = document.getElementById('colorbar');
  ctx = can.getContext('2d');
  can.width = document.body.clientWidth * 0.25;
  var h = can.height / 360;
  
  var hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]);
  
  for(var i=0; i<=360; i++) {
    ctx.fillStyle = 'hsl('+i+', 100%, 50%)';
    ctx.fillRect(0, i * h, can.width/2, h);
    ctx.fillStyle = 'hsl(' + hsl[0] * 360 + ', 100%, ' + i * (100/360) + '%)';
    ctx.fillRect(can.width/2, i * h, can.width/2, h);
  }
}

function setup(){
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (xhttp.readyState == 4 && xhttp.status == 200) {
      document.getElementById('mode').innerHTML = xhttp.responseText;
      elems = document.querySelectorAll('ul li a'); // adds listener also to existing s and b buttons
      [].forEach.call(elems, function(el) {
        el.addEventListener('touchstart', handle_M_B_S, false);
        el.addEventListener('click', handle_M_B_S, false);
      });
    }
  };
  xhttp.open('GET', 'modes', true);
  xhttp.send();
 
  var can = document.getElementById('colorbar');
  var ctx = can.getContext('2d');

  drawColorbar();
  
  can.addEventListener('touchstart', Touch, false);
  can.addEventListener('click', Click, false);
}
)=====";

Credits

John Bradnam

John Bradnam

141 projects • 167 followers
Thanks to IeC.

Comments