diff --git a/examples/ws2812_rainbow/Makefile b/examples/ws2812_rainbow/Makefile
new file mode 100644
index 0000000..dad3155
--- /dev/null
+++ b/examples/ws2812_rainbow/Makefile
@@ -0,0 +1,6 @@
+# Makefile for the ws2812 Rainbow example
+
+PROGRAM=ws2812_rainbow
+EXTRA_COMPONENTS = extras/ws2812
+
+include ../../common.mk
diff --git a/examples/ws2812_rainbow/ws2812_rainbow.c b/examples/ws2812_rainbow/ws2812_rainbow.c
new file mode 100644
index 0000000..0c4f053
--- /dev/null
+++ b/examples/ws2812_rainbow/ws2812_rainbow.c
@@ -0,0 +1,155 @@
+/**
+ * @file   es2812_rainbow.c
+ * @author Ondřej Hruška, 2016
+ *
+ * @brief  Example of a rainbow effect with
+ *         WS2812 connected to GPIO2.
+ *
+ * This demo is in the public domain.
+ */
+
+#include "espressif/esp_common.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "esp/uart.h" // uart_set_baud
+#include <stdio.h> // printf
+#include <stdint.h>
+
+#include "ws2812.h"
+
+
+#define delay_ms(ms) vTaskDelay((ms) / portTICK_RATE_MS)
+
+
+/** GPIO number used to control the RGBs */
+static const uint8_t pin = 2;
+
+
+/**
+ * @brief "rainbow" animation with a single RGB led.
+ */
+void demo_single(void)
+{
+    // duration between color changes
+    const uint8_t delay = 25;
+
+    ws2812_rgb_t x = {.num = 0xFF0000}; // RED color
+
+    while (1) {
+        // iterate through the spectrum
+
+        // note: This would be _WAY_ easier with HSL
+
+        while(x.c.g < 0xFF) { x.c.g++; ws2812_set(pin, x.num); delay_ms(delay); } // R->RG
+        while(x.c.r > 0x00) { x.c.r--; ws2812_set(pin, x.num); delay_ms(delay); } // RG->G
+        while(x.c.b < 0xFF) { x.c.b++; ws2812_set(pin, x.num); delay_ms(delay); } // G->GB
+        while(x.c.g > 0x00) { x.c.g--; ws2812_set(pin, x.num); delay_ms(delay); } // GB->B
+        while(x.c.r < 0xFF) { x.c.r++; ws2812_set(pin, x.num); delay_ms(delay); } // B->BR
+        while(x.c.b > 0x00) { x.c.b--; ws2812_set(pin, x.num); delay_ms(delay); } // BR->R
+    }
+}
+
+
+/**
+ * @brief "rainbow" effect on a RGB strip (30 pixels - can be adjusted)
+ *
+ * This example shows how to use the "procedural generation" of colors.
+ *
+ * The pixel colors are calculated on the fly, which saves RAM
+ * (especially with large displays).
+ */
+void demo_strip(void *pvParameters)
+{
+    const uint8_t anim_step = 10;
+    const uint8_t anim_max = 250;
+
+    // Number of your "pixels"
+    const uint8_t pixel_count = 30;
+
+    // duration between color changes
+    const uint8_t delay = 25;
+
+    ws2812_rgb_t color = WS2812_RGB(anim_max, 0, 0);
+    uint8_t step = 0;
+
+    ws2812_rgb_t color2 = WS2812_RGB(anim_max, 0, 0);
+    uint8_t step2 = 0;
+
+    while (1) {
+
+        color = color2;
+        step = step2;
+
+        // Start a data sequence (disables interrupts)
+        ws2812_seq_start();
+
+        for (uint8_t i = 0; i < pixel_count; i++) {
+
+            // send a color
+            ws2812_seq_rgb(pin, color.num);
+
+            // now we have a few hundred nanoseconds
+            // to calculate the next color
+
+            if (i == 1) {
+                color2 = color;
+                step2 = step;
+            }
+
+            switch (step) {
+                case 0: color.c.g += anim_step; if (color.c.g >= anim_max) step++;  break;
+                case 1: color.c.r -= anim_step; if (color.c.r == 0) step++; break;
+                case 2: color.c.b += anim_step; if (color.c.b >= anim_max) step++; break;
+                case 3: color.c.g -= anim_step; if (color.c.g == 0) step++; break;
+                case 4: color.c.r += anim_step; if (color.c.r >= anim_max) step++; break;
+                case 5: color.c.b -= anim_step; if (color.c.b == 0) step = 0; break;
+            }
+        }
+
+        // End the data sequence, display colors (interrupts are restored)
+        ws2812_seq_end();
+
+        // wait a bit
+        delay_ms(delay);
+    }
+}
+
+
+
+
+void user_init(void)
+{
+    uart_set_baud(0, 115200);
+    printf("--- RGB Rainbow demo ---");
+
+    // Configure the GPIO
+    gpio_enable(pin, GPIO_OUTPUT);
+
+    // Select a demo function:
+
+#if true
+# define demo demo_strip
+#else
+# define demo demo_single
+#endif
+
+
+    // Choose how to run it:
+
+#if true
+
+    // Blocking function - works OK, because WiFi isn't
+    // initialized yet & we're hogging the CPU.
+
+    printf("Starting a blocking function.\r\n");
+    demo(NULL);
+
+#else
+
+    // Start a task. This is a real-life example,
+    // notice the glitches due to NMI.
+
+    printf("Starting a task. There may be glitches!\r\n");
+    xTaskCreate(&demo, (signed char *)"strip demo", 256, NULL, 10, NULL);
+#endif
+}
diff --git a/extras/ws2812/component.mk b/extras/ws2812/component.mk
new file mode 100644
index 0000000..df2fc59
--- /dev/null
+++ b/extras/ws2812/component.mk
@@ -0,0 +1,8 @@
+# Component makefile for extras/ws2812
+
+INC_DIRS += $(ws2812_ROOT)
+
+# args for passing into compile rule generation
+ws2812_SRC_DIR =  $(ws2812_ROOT)
+
+$(eval $(call component_compile_rules,ws2812))
\ No newline at end of file
diff --git a/extras/ws2812/ws2812.c b/extras/ws2812/ws2812.c
new file mode 100644
index 0000000..c1e04be
--- /dev/null
+++ b/extras/ws2812/ws2812.c
@@ -0,0 +1,44 @@
+/**
+ * @file   ws2812b.c
+ * @brief  ESP8266 driver for WS2812B
+ * @author Ondřej Hruška, (c) 2016
+ *
+ * MIT License
+ */
+
+#include "espressif/esp_common.h" // sdk_os_delay_us
+#include "FreeRTOS.h"
+#include "task.h"
+#include <stdint.h>
+
+#include "ws2812.h"
+
+
+/** Set one RGB LED color */
+void ws2812_set(uint8_t gpio_num, uint32_t rgb)
+{
+    ws2812_seq_start();
+    ws2812_seq_rgb(gpio_num, rgb);
+    ws2812_seq_end();
+}
+
+
+/** Set many RGBs */
+void ws2812_set_many(uint8_t gpio_num, uint32_t *rgbs, size_t count)
+{
+    ws2812_seq_start();
+
+    for (size_t i = 0; i < count; i++) {
+        uint32_t rgb = *rgbs++;
+        ws2812_seq_rgb(gpio_num, rgb);
+    }
+
+    ws2812_seq_end();
+}
+
+
+/** Set one RGB to black (when used as indicator) */
+void ws2812_off(uint8_t gpio_num)
+{
+    ws2812_set(gpio_num, 0x000000);
+}
diff --git a/extras/ws2812/ws2812.h b/extras/ws2812/ws2812.h
new file mode 100644
index 0000000..d6b2d36
--- /dev/null
+++ b/extras/ws2812/ws2812.h
@@ -0,0 +1,208 @@
+/**
+ * @file   ws2812.h
+ * @brief  ESP8266 driver for WS2812
+ *
+ * This is a simple bit-banging driver for WS2812.
+ *
+ * It will work for WS2812, WS2812B and possibly others,
+ * with small alterations.
+ *
+ * The WS2812 protocol takes roughly 1µs per bit,
+ * thus ~24µs per "pixel", plus 50µs to indicate
+ * the end of data.
+ *
+ * @note
+ * The GPIO must be configured for output before trying
+ * to set colors!
+ *
+ * @attention
+ * Due to the precise timing required, interrupts are
+ * disabled until the data is sent. However, we currently
+ * can't disable the NMI, so under some conditions
+ * (heavy network load), the timing may be disturbed.
+ *
+ * @author Ondřej Hruška, (c) 2016
+ *
+ * MIT License
+ */
+
+#ifndef WS2812_DRV_H
+#define WS2812_DRV_H
+
+#include <stdint.h>
+#include <stddef.h> // size_t
+
+#include "espressif/esp_common.h" // sdk_os_delay_us
+#include "esp/gpio.h"
+
+/**
+ * @brief Struct for easy manipulation of RGB colors.
+ *
+ * Set components in the xrgb.c.r (etc.) and you will get
+ * the hex in xrgb.num.
+ */
+typedef union {
+
+    /** Struct for access to individual color components */
+    struct __attribute__((packed)) {
+        uint8_t b;
+        uint8_t g;
+        uint8_t r;
+    } c;
+
+    /** RGB color as a single uint32_t */
+    uint32_t num;
+
+} ws2812_rgb_t;
+
+
+/**
+ * @brief Macro to compose the RGB struct.
+ *
+ * You can also use {.num = 0xFF0000} to set the hex directly.
+ */
+#define WS2812_RGB(r, g, b) {.num = (((r) & 0xFF) << 16) | (((g) & 0xFF) << 8) | ((b) & 0xFF)}
+
+
+/**
+ * @brief Set a single RGB LED color, and display it.
+ *
+ * @param gpio_num : data line GPIO number
+ * @param rgb : RGB color - 0x00RRGGBB
+ */
+void ws2812_set(uint8_t gpio_num, uint32_t rgb);
+
+
+/**
+ * @brief Set color of multiple RGB LEDs, and display it.
+ *
+ * @note
+ * Due to the precise timing required, interrupts are
+ * disabled until all data is sent (roughly 25*count + 50µs)
+ *
+ * @param gpio_num : data line GPIO number
+ * @param rgbs  : array of RGB colors in the 0x00RRGGBB format
+ * @param count : number of elements in the array
+ */
+void ws2812_set_many(uint8_t gpio_num, uint32_t *rgbs, size_t count);
+
+
+/**
+ * @brief Turn a single WS2812B off
+ *
+ * This is a companion function to ws2812_set().
+ *
+ * It sets the color to BLACK, thus effectively
+ * turning the RGB LED off.
+ *
+ * @param gpio_num : data line GPIO number
+ */
+void ws2812_off(uint8_t gpio_num);
+
+
+
+///////////////////////////////////////////////////////////////////
+///
+/// The following part of the driver is used internally, and can
+/// also be used for "procedural generation" of colors.
+///
+/// - first call ws2812b_seq_start(),
+/// - then repeatedly ws2812b_seq_rgb() with your colors
+/// - and end the sequence with ws2812b_seq_end()
+///
+///////////////////////////////////////////////////////////////////
+
+
+
+// Ugly way to get short delays. Works for 80 MHz.
+
+// 400 ns
+#ifndef WS2812_SHORT_DELAY
+#define WS2812_SHORT_DELAY() for (volatile uint32_t __j = 1; __j > 0; __j--)
+#endif
+
+// 800 ns
+#ifndef WS2812_LONG_DELAY
+#define WS2812_LONG_DELAY()  for (volatile uint32_t __j = 3; __j > 0; __j--)
+#endif
+
+
+/**
+ * @brief Send a byte on the data line.
+ *
+ * The WS2812B is a form of PWM:
+ * - each bit takes roughly 1 µs (but can be longer)
+ * - the duration of the ON part determines the value
+ * - 800 ns -> "1"
+ * - 400 ns -> "0"
+ *
+ * The timing must be very precise, you may need to adjust
+ * it according for particular RGB model.
+ *
+ * @param gpio_num : data line GPIO number
+ * @param byte : byte to send
+ */
+static inline
+void ws2812_byte(uint8_t gpio_num, uint8_t byte)
+{
+    for (register volatile uint8_t i = 0; i < 8; i++) {
+        gpio_write(gpio_num, 1);
+
+        // duty cycle determines the bit value
+
+        if (byte & 0x80) {
+            WS2812_LONG_DELAY();
+            gpio_write(gpio_num, 0);
+            WS2812_SHORT_DELAY();
+        } else {
+            WS2812_SHORT_DELAY();
+            gpio_write(gpio_num, 0);
+            WS2812_LONG_DELAY();
+        }
+
+        byte <<= 1; // shift to next bit
+    }
+}
+
+
+/**
+ * @brief Send a color to the RGB strip.
+ *
+ * This function must be called inside the data sequence.
+ *
+ * @param gpio_num : data line GPIO number
+ * @param rgb : 0xRRGGBB color
+ */
+static inline
+void ws2812_seq_rgb(uint8_t gpio_num, uint32_t rgb)
+{
+    ws2812_byte(gpio_num, (rgb & 0x00FF00) >> 8);
+    ws2812_byte(gpio_num, (rgb & 0xFF0000) >> 16);
+    ws2812_byte(gpio_num, (rgb & 0x0000FF) >> 0);
+}
+
+
+/**
+ * @brief Start a data sequence.
+ */
+static inline
+void ws2812_seq_start(void)
+{
+    // interruption when sending data would break the timing
+    taskENTER_CRITICAL();
+}
+
+
+/**
+ * @brief End the data sequence and display the colors.
+ */
+static inline
+void ws2812_seq_end(void)
+{
+    taskEXIT_CRITICAL();
+
+    sdk_os_delay_us(50); // display the loaded colors
+}
+
+
+#endif /* WS2812_DRV_H */