End Result

This project basically consists of a modified LACK IKEA Table in which I mounted a 8x8 RGB LED Matrix. I then covered the matrix with a semi-transparent acrylic box to blur colors and create enlarged pixels. Some Arduino code makes it possible to control each pixel individually and display light effects.

You can view the small video below to see the end result, scroll further down to see exactly how this table was made.


The matrix lays in an IKEA LACK Table. The semi-transparent box makes sure colors are displayed as vibrantly as possible.


An Arduino in the table is programmed so it can control the 64 RGB LED pixels to show more than 21 effects.

Mouse over each of these pictures to see a quick preview of each effect.


The RGB Table can easily be controlled with an iPhone or iPad. It works with TouchOSC and a mobile web page. The matrix can also be controlled with a IR Remote or through a simple serial port.

Detailed feature list


The table is a hacked IKEA LACK Table, it's length and width are both 55cm. Including the semi-transparent box, the table is 56cm heigh.


Each pixel can be individually controlled to be any color possible. This makes it possible to program some nice effects. In total so far, I've programmed 21 effects:

  • Rainbow
  • Rainbow Scroll
  • Rainbow Radial
  • Random
  • Stars
  • Fireplace
  • Flame
  • Smoker
  • Spin
  • Game Of Life
  • Fireworks
  • Write text
  • Temperature
  • Digital Clock
  • Binary Clock
  • Alarm Clock (wakeUp)
  • E.Q. Music Visualization
  • Beats Music Visualization
  • Snake Game
  • Pong (1) Game
  • Tetris Game


It's important that you can easily select any of these modes or easily play a game. This is done by adding the following control-options to the table:

  • TouchOSC (iPhone & iPad)
  • IR Remote
  • Web page
  • Serial commands

Table Construction

It all starts with an Ikea LACK Table (€5.99). This side table is really cheap and very hackable. I wanted to place LEDs and all other electronics inside the top layer of the LACK table. Fortunately the top can easily be cut open and the filling of can easily be removed. This instructable has some some instructions on how to do that.

I mounted the LEDs in a square piece of white hard board (3mm) that fits in the hole cut in the table. Then, I drilled all 64 holes for the LEDs in the top cover. You can find the drill pattern in this folder.

The matrix dividers are simple rectangles that can easily be sawn out of a blank piece of hard board panel. I assembled these before painting them with a glossy white paint coating.

The semi-translucent acrylic box cover consists of 5 parts. I ordered these at a local hobby store. I made sure the box would fit over the matrix divider and then glued the parts together. Glueing acrylic was surprisingly easy, see this instructional video on how to to that.


The electronics used in this table are really quite simple. I used an Arduino board as micro controllers that control the LED Drivers who in their turn control the LEDs itself. Another Arduino provides the interface for controlling the matrix.

Working principle

There are two main parts to the working of the table: the controller and the table itself. The controller's main purpose is to gather information and send simple commands to the table. The table itself will process these commands and color the matrix accordingly. Below is a more in depth view on the inner workings.

Controller (MASTER): Arduino Mega 2560

The controller forms the link between the end user and the table itself. Via an Arduino W5100 Ethernet shield, it is able to run a very small web server for simple GET-commands (sent via ajax from a Dropbox-hosted web page) and a OSC server/client. The TSOP4838 is an IR receiver for receiving remote commands. Simple communication over a serial port also remains an option.

The controller also collects information from various resources: clock data from the DS1307 module and music frequency info from the MSGEQ7 chip.

The controller is meant to be an always-on device. It will control the power supply of the table (slave) via a relay. This means that I'm able to power on or off the table with all of the controlling methods above.

All this information and commands are then packed up and sent to the table itself.

Note: As the controller is always on, I combined the controller with a previous project of mine that required an always on Arduino. This enables me to control my stereo installation via the small web server and an IR LED ("Control stereo IR LED" in the diagram above).

Table (SLAVE): Arduino Mega 2560

The table itself in essence is simple: the Arduino Mega 2560 receives the commands from the controller, processes these and sends the matrix data to a series of 11 WS2803 Driver chips. These drivers are positioned in a one-dimensional string of chips and form a snake-like pattern on the table. The conversion from one dimension (a simple string of lights) to two dimensions (the actual matrix on the table) is done in software.

I connected DALLAS DS18B20 for easy and accurate temperature readings. This chip is added to the table instead of the controller so I would get a more accurate room temperature reading instead of a temperature reading inside some far hidden closet.

Complete Parts List


  • 2x Arduino Mega 2560
  • 1x Arduino W5100 Ethernet Shield

Chips (ICs)

  • 11x WS2803 as LED drivers
  • 1x DS1307 Clock Module (Tiny RTC)
  • 1x MSGEQ7 for music frequency bands
  • 1x TSOP4838 for IR Remote
  • 1x Dallas DS18B20 as temperature sensor


  • 64x Common Anode RGB LED (5mm)
  • 1x Yellow LED (3mm)
  • 1x IR LED (5mm)


  • 11x 1.2kΩ
  • 2x 10kΩ
  • 2x 100Ω
  • 1x 4.7kΩ
  • 1x 47kΩ
  • 1x 200kΩ


  • 13x 100nF
  • 1x 10nF
  • 1x 33pF


  • 2x Push Buttons
  • 1x On/Off Switch
  • 2x 3.5mm audio jack sockets
  • 2x Ethernet (RJ-45) Sockets
  • 1x Mini USB Socket
  • 1x Red Banana Plug
  • 1x Black Banana Plug
  • 1x Ethernet cable


Table (slave) schematic

As you can see on the schematic above, the table is primarily built up of one repeating block: the WS2803 with a resistor, a capacitor and its LEDs.

The WS2803 connects to the Arduino via an I2C-connection. This connection only requires a data line and a clock line: SDA and SCL. Each WS2803 has an I2C-IN (SDI and CKI) and an I2C-OUT (SDO and CKO), this enables us to chain the chips together and thus forming a one-dimensional string of 11 WS2803 chips. Connect the OUT-channels (pin 24 and 25) of the previous chip to the IN-channels (pin 4 and 5) of the next one.

Each WS2803 can drive 6 common anode RGB LEDs: pin 6-23. Note however that the last connected LED will be LED number 1 in software, so the LEDs (and chips) need to be connected in reverse order.

As the WS2803 is a constant current driver, there's no need for a current limit resistor for each LED. Only one resistor is needed to set the maximum output current on each channel (between GND en pin 3 of the WS2803). The datasheet specifies the value of this resistor as follows: If and we can calculate the value for : I used a 1.2kΩ resistor.

Lastly a decoupling capacitor of 0.1uF (100nF) is added between +5V (pin 28) and GND (pin 1) as close to the chip as possible.

Note on using the TLC5940 as LED driver: I tried daisy chaining 12 TLC5940s, but failed to do so. I couldn't get past daisy-chaining 4 TLC5940s on my breadboard even with heavy decoupling capacitors (from 0.1uF to 100uF). After months of failed attempts I couldn't get these to work so I tried the WS2803 chips, which work brilliantly. However, the WS2803 may be harder to get a hold on and can be slightly more expensive than the TLC5940.

Controller (master) schematic

The master itself only consists of a few primary building blocks.

  • The 'Tiny RTC I2C'-module is connected via I2C to the Arduino.
  • The MSGEQ7 splits an audio input in 7 frequency bands, this chip is connected to the Arduino via STROBE, RESET and the actual output value: EQ.
  • The TSOP4838 is connected via IRRCV, an output IR LED is connected via IROUT
  • The relay module switches the table ATX power supply on and off. A status LED is connected. The relay is connected via RELAY.

The controller (master) connects to the table itself via the TRANSR and TRANST connections. These are both connected to RX1/TX1 (switched: RX from one Arduino to TX from the other). I connected these via an RJ45 cable and connector.


Programming is entirely done in the Arduino programming environment. I've written my own library for displaying data on the table (see below). Communication is done in the sketch itself. The controller is programmed with a sketch (no custom made library).

Working principle

The following flowchart should explain how the code works and how all the parts fit together.

First, the master (master.ino) collects all its data: table mode, possible IR Remote commands, ethernet,... This information is translated into one table mode and numerical values (for example spectrum values). These are loaded into a struct data: data.mode contains the table mode, data.value0, data.value1,..., data.value6 contain the necessary numerical data. readyToSend=1 finally indicates that the struct is ready to be sent. This struct is passed to the EasyTransfer library and sent over Serial1.

The table (table.ino) receives this struct over Serial1 and unpacks the data in an identical struct (EasyTransfer library). From there it will go through a couple of if/else-statements for translating the table mode into a usable command for the custom made Display library (see below).

The custom made Display Library (libraries/Display/Display.h and libraries/Display/Display.cpp) is called via functions such as display.clear(), display.show() and display.rainbow(). These functions adapt a one dimensional struct in which each pixel is represented by a red, green and blue value. The conversion from two dimensions to one dimension is done in display.setPixel(..), see this file to see how the this conversion is done. This struct is then given to the FASTSPI library and FASTSPI_LED.show() is called: the data is sent to the LED drivers.

List of used libraries

Below is a list of all used Arduino libraries in this project. If not standard, put these in -arduino-folder-/libraries/. I would also like to thank the developers of all these libraries. Of course my self written Display library is also used, see below for that.

Display library for Arduino

I wrote this library to separate communication and effects from each other. This library's sole purpose is to generate effects and display data on the matrix. This library is only active on the slave device (table). In total I programmed 21 effects so far, these are all split into one or more functions (methods) which are documented below.


Below is a very basic example on how to use the library. This code shows a rainbow-effect on the table. Note that the FASTSPI_LED library and the Arduino Particle System library are required.

#include <EasyTransfer.h>
#include <FastSPI_LED.h>
#include <avr/pgmspace.h>
#include <Display.h>

//Particle System: https://github.com/giladaya/arduino-particle-sys
#include <ParticleSys.h>
#include <Particle_Std.h>
#include <Particle_Bounce.h>
#include <Particle_Fixed.h>
#include <Emitter_Fountain.h>
#include <Particle_Attractor.h>
#include <PartMatrix.h>
#include <Emitter_Spin.h>

int r=8;
Display display(r);

void setup(){
    trans.begin(details(data), &Serial1);   

void loop(){


  • Display display(byte r)

    Initiate the library (also initiates the FASTSPI_LED library). byte r represents the height (or width) of the square matrix (in this case 8). Call the constructor as a global variable, meaning before setup().

Available public methods

  • void display.clear([boolean show]);

    Clears the display, only showing if boolean show = true.

  • void display.fadeOut([int duration]);

    Fade everything that is displayed on the matrix, int duration gives the delay between steps.

  • void display.setPixel(byte x, byte y, byte red, byte green, byte blue[, float alpha]);

    Set a pixel with byte x and byte y position to the color given by byte red, byte green and byte blue, optionally calculating values with float alpha in mind.

  • void display.setRotation(byte rot);

    Turn the matrix around: byte rot * 90°

  • void display.setAll(byte red, byte green, byte blue);

    Set all pixels to a given color.

  • void display.drawCircle(byte x0, byte y0, byte r, byte red, byte green, byte blue);

    Draw a circle with middle point byte x0 and byte y0, radius byte r and in a given color.

  • void display.drawRectangle(byte x0, byte y0, byte h, byte b, byte red, byte green, byte blue);

    Draw a rectangle with starting point byte x0 and byte y0, with a height of byte h and width of byte b and in a given color

  • void display.drawCharacter(char c[, x0, y0], byte red, byte green, byte blue);

    Draw a large character, optionally giving a starting point. Note: If no starting point is given, the character is drawn in the origin.

  • void display.scrollLeft(String s, int d, byte red, byte green, byte blue);

    Scroll a text (given in String s), with a delay of int d between steps.

  • void display.fillRandom(float p);

    Fill the matrix randomly, float p gives fill-to-empty-ratio.

  • void display.rainbow([int d]);

    Start (or proceed) to fill the complete matrix with succeeding colors. int d gives the delay between steps.

  • void display.rainbowScroll(int d, byte step1, byte step2);

    Show a vertically scrolling rainbow matrix. int d sets the delay between steps, byte step1 sets the initial step in hue on the matrix per pixel, byte step2 sets the steps in hue in time.

  • void display.rainbowRadial(int d, byte step1, byte step2);

    Show a radial scrolling matrix. Parameters are the same as above.

  • void display.stars(int d);

    Show a few white pixels, fading in time. int d gives the delay between steps in both fading and generating pixels

  • void display.fireplace();

    Simulate a fireplace, no arguments.

  • void display.eq(byte h1,byte h2, byte h3, byte h4, byte h5, byte h6, byte h7[, byte red, byte green, byte blue]);

    Display EQ: 8 columns to visually represent music frequency bands (byte h1, byte h2,..). The eighth column represents the average of the first 7. Color arguments are optional, if no color is given the columns will be displayed in vertical steps of hue (y=0 is yellow, y=7 is red).

  • void display.beats(byte h1, byte red, byte green, byte blue);

    Basically equivalent to setAll but specifically designed to visualizing the beats in music. byte h1 represents the bass in 5 values which are then calculated in the alpha-channel of setAll.

  • void display.snake();

    Initialize the snake-game, sets the snake in the middle and sets random food position.

  • void display.snakeMove(byte move);

    Play snake-game by moving the snake. byte move can have the following values: 1 is up, 2 is right, 3 is down and 4 is left (clock-wise rotation).

  • void display.pong1();

    Initialize a sort-of-pong-game with only 1 player.

  • void display.pong1Move();

    Proceed one step into game, not equivalent to pong1MovePlatform.

  • void display.pong1MovePlatform(byte direction);

    Move the pong platform, byte direction gives the direction, it can have one of two values: 2 is right, 4 is left.

  • void display.tetris();

    Initialize a tetris-like-game, a random block is choosen at the top of the matrix

  • void display.tetrisMove(byte direction);

    Move the tetris-block, byte direction can have one of the following values: 1 is rotating, 2 is right, 3 is down and 4 is left.

  • void display.digitalClock(byte hour, byte minute);

    Show a digital clock in numbers of 3x4. First line represents byte hour, second byte minute.

  • void display.binaryClock(byte hour, byte minute, byte second);

    Show a binary clock, more information about the binary clock here. (blocks of 1x2)

  • void display.printTemperature(float temp);

    Print the temperature, color depends on the temperature (cold is blue and hot is red). Note: actually getting the temperature out of the sensor is not part of the library. Reading the sensor has to be done in the sketch.

  • void display.wakeUp();

    Steadily increase color brightness over a long period of time (simulating a sunrise).

  • void display.gameOfLifeFill();

    First loop only, fill the matrix with random cells.

  • void display.gameOfLife();

    Proceed a generation in Conway's Game Of Life. The color of newly generated pixels changes accordingly to the generation. More information about Conway's Game Of Life here.

  • void display.drawPartMatrix(PartMatrix pMatrix);

    Draws the particle matrix provided by the Arduino Particle System Library. Note: actually generating the particle system is done in the sketch, not in the library. This method only shows the output of the Particle System.

Thanks to

These are all web pages and developers from where I got my inspiration for building this table in the first place. These are also research pages where I found some solutions to some of my electronic problems that I ran into.

I originally got the idea for building this table from these people/pages.
These guys gave me the idea to switch to the WS2803 LED driver:
I copied some code snippets from these projects. They are also credited in the code.
Credit also goes to the developers of all the included libraries (see above).

I'm sorry, this browser is becoming quite old, it's time to get a new browser.