Smooth animation with an 8x8 RGB LED matrix

Below are several videos that demonstrate smooth animations running on an 8x8 RGB LED matrix.

The main factors that contribute to the effect:

  • Use of a diffuser screen, placed at just the right distance from the LEDs.
    I used a tracing paper for the screen - it is opaque enough so the pixels blend, but not too much so some contrast remains.
    The physical setup can be seen in the attached images.

  • The calculations that produce the animation are done using a larger number of "virtual pixels" than the ones actually available. These are then rendered onto the low-res matrix.

  • The frame rate is high enough to create the illusion of smooth motion

Examples:
Fire simulation
The logic here is pretty simple.
For each keyframe, a new row of pixels with random brightness values is generated.
In each sub-frame, the matrix is gradually shifted up, interpolating between full rows.
The calculated "raw" brightness is faded according to a fixed mask, to give the flame a shape.
The hue of each pixel is taken from a constant mask, so the inner area of the fire is yellow and the outer area deep red.
The code for this example is available on github.

Particle systems
These next examples use the same hardware setup, but the animation is generated using a particle system library that I posted about yesterday.

Enjoy

Very nice effect...!

Holy cow that is really well done. Where did you get that diffuser screen? I don't know a lot about them but I have tried diffusing with thin white cloth and paper and never got a good result.

Care to share any code or schematics?

Thanks, sure :slight_smile:

The diffuser screen is made of a sheet of tracing paper, attached to a frame made of foam core - see attached image.
Standard paper is too white and opaque which results in really low contrast and the light hardly getting through.
Note that the distance of the screen from the LED matrix is really important and will be different depending on the specific material used.

The circuit itself is just an off-the-shelf Colorduino - I believe that a Rainbowduino would work just as well.

The code for all the examples (library and sketches) is available on github.

giladaya:
The circuit itself is just an off-the-shelf Colorduino - I believe that a Rainbowduino would work just as well.

Hi giladaya,
I can confirm the code works on Rainbowduino with very little modification. Fantastic work.

Hello giladaya,

this really looks great. I was searching for some nice Matrix Animations and started a thread, which was not very succesful
LED Matrix - animation pattern.
I´ll try to port your code to my matrix and go on collecting some nice effects :slight_smile:

Some time ago I wrote a .NET program which can demonstrate matix animations on a PC. Maybe I´ll release it to make it easier to create some new effects.

Greetings
Thomas

Respect, well done! Good presentation, too.

I can imagine, that it would look great on a huge indirect working matrix (so, that you just see the projection at a wall) with powerful LED strip elements...

edit, next day:

I have to test that with a serious output device... :wink:

This looks great!

Once my 8x8 matrix arrives, I'd like to try to adapt it as an interactive nightlight, where new particles would be triggered if a PIR motion detector is triggered while ambient light is low. I was thinking I might be able to accomplish this by updating your "Flame.ino" example (arduino-particle-sys/Flame.ino at master · giladaya/arduino-particle-sys · GitHub). In that example, some of the parameters for the particle system are established in Setup:

source.x = 112;  
source.y = 1;  
Emitter_Fountain::minLife = 20;  
Emitter_Fountain::maxLife = 80;  
Particle_Std::ay = 1;

I was thinking that I could add some lines to Loop that would adjust these continuously. For instance:

if (triggerPin == HIGH && ambientLight <= lightThreshold) {      //assuming these are all defined elsewhere...
   Particle_Std::ay = 1;
}
else { Particle_Std::ay = 0; }

I expect this would leave the particles at the bottom of the display most of the time, but then they'd shoot up when the PIR is triggered in the dark.
Any ideas or suggestions for a more elegant implementation, or smarter usage of the library? Thanks! I'm looking forward to experimenting with this.

@giladaya: Could it be, that you swapped columns and rows ( width and height) in you code?
I'm currently trying to port your "fire"-code to a WS2812 LED strip with 6 columns ( 6 pixel width) and 8 rows (8 pixel hight).

As I understand your code, the function "generateline()" creates a new pixel-line on the bottom line of the matrix. But your code says:

/**
* Randomly generate the next line (matrix row)
*/
void generateLine(){
  for(unsigned char x=0;x<ColorduinoScreenHeight;x++) {
      line[x] = random(64, 255);
  }
}

So you are using "x" for the position of the "cursor" on the line, but you use the ColorduinoScreenHeight as upper border.
This is not a problem, as long as you use this nomenclature consistently and yout matrix has exactly the same number of rows and columns.
Problems start if the number of columns and rows differ.
Another point is the declaration of the mask-Arrays (valueMask[ColorduinoScreenWidth][ColorduinoScreenHeight] and hueMask[ColorduinoScreenWidth][ColorduinoScreenHeight])
Mario.

my dream is to make one of this.....

toxman:
my dream is to make one of this.....

Set your dreams higher. This guy did a great job but at this point this is almost a canned project, all the data you need is right here and it isn't all that expensive. So just do it. Then design something yourself, that is the real challange.

beginning from the low side..... nest step is higher XD

@mkl0815 did you ever manager to port this? I've tried to make this library independent but it seems to rely on the colorduino libraries. All I need is to be able to define the columns and rows and get r,g,b for each pixel. Haven't had any luck so far, if I remove the colorduino library reference everything goes to pot.

mkl0815:
@giladaya: Could it be, that you swapped columns and rows ( width and height) in you code?
I'm currently trying to port your "fire"-code to a WS2812 LED strip with 6 columns ( 6 pixel width) and 8 rows (8 pixel hight).

As I understand your code, the function "generateline()" creates a new pixel-line on the bottom line of the matrix. But your code says:

/**
  • Randomly generate the next line (matrix row)
    */
    void generateLine(){
      for(unsigned char x=0;x<ColorduinoScreenHeight;x++) {
          line[x] = random(64, 255);
      }
    }


So you are using "x" for the position of the "cursor" on the line, but you use the *ColorduinoScreenHeight* as upper border. 
This is not a problem, as long as you use this nomenclature consistently and yout matrix has exactly the same number of rows and columns. 
Problems start if the number of columns and rows differ.
Another point is the declaration of the mask-Arrays (*valueMask[ColorduinoScreenWidth][ColorduinoScreenHeight]* and *hueMask[ColorduinoScreenWidth][ColorduinoScreenHeight]*) 
Mario.

Hi, here a concept, how to map 2 rows to a RGB strip using FastSPI_LED...

/* 
 * Copyright (C) 2013 Gilad Dayagi.  All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

 /*
  * An example for the Arduino particle system library
  * A wandering particle that emits colorful smoke 
  * 
  * Note: this example uses the colorduino library becuse that is what I had, 
  * but any device that supports setting a pixel to an RGB value can be used
  */

//#include <Colorduino.h>

#include <FastSPI_LED.h>
#include "ParticleSys.h"
#include "Particle_Std.h"
#include "Particle_Fixed.h"
#include "Emitter_Fountain.h"
#include "PartMatrix.h"

#define NUM_LEDS 20

//change the order of your rgb here
struct CRGB { unsigned char r; unsigned char g; unsigned char b; }; 
struct CRGB *leds; 


const byte numParticles = 50;
boolean pulseOn = false;

Particle_Std particles[numParticles];
Particle_Fixed source;
Emitter_Fountain emitter(0, 0, 5, &source);
ParticleSys pSys(numParticles, particles, &emitter);
PartMatrix pMatrix;

/**
 * Render the particles into a low-resolution matrix
 */
void drawMatrix(){
    pMatrix.reset();
    pMatrix.render(particles, numParticles);
    //update the actual LED matrix
    for (byte y=0;y<8;y++) {
        //for(byte x=0;x<8;x++) {
            //Colorduino.SetPixel(x, y, pMatrix.matrix[x][y].r, pMatrix.matrix[x][y].g, pMatrix.matrix[x][y].b);
              leds[y].r = pMatrix.matrix[3][y].r;
              leds[y].g = pMatrix.matrix[3][y].g;
              leds[y].b = pMatrix.matrix[3][y].b; 
              
              leds[NUM_LEDS-y].r = pMatrix.matrix[4][y].r;
              leds[NUM_LEDS-y].g = pMatrix.matrix[4][y].g;
              leds[NUM_LEDS-y].b = pMatrix.matrix[4][y].b; 
             
             
             
             
              
     //   }
    }
}

void setup()
{
  FastSPI_LED.setLeds(NUM_LEDS);
  //select your chipset according to your strip type here - have a look at FastSPI documentation
  //i´m using a LPD1101 right know who behaves like a LPD6803
  FastSPI_LED.setChipset(CFastSPI_LED::SPI_LPD6803); 
  FastSPI_LED.init();
  FastSPI_LED.start();
  leds = (struct CRGB*)FastSPI_LED.getRGBData(); 
   
  
  //Colorduino.Init(); // initialize the board
  
  // compensate for relative intensity differences in R/G/B brightness
  // array of 6-bit base values for RGB (0~63)
  // whiteBalVal[0]=red
  // whiteBalVal[1]=green
  // whiteBalVal[2]=blue
  //byte whiteBalVal[3] = {36,63,7}; // for LEDSEE 6x6cm round matrix
  //Colorduino.SetWhiteBal(whiteBalVal);
  
  randomSeed(analogRead(0));
  
  //source.vx = 3;
  //source.vy = 1;
  source.x = 112;
  source.y = 1;
  Emitter_Fountain::minLife = 20;
  Emitter_Fountain::maxLife = 80;
  Particle_Std::ay = 1;
  //PartMatrix::isOverflow = false;
 
  //init all pixels to zero
  pMatrix.reset();
}

void loop()
{
    pSys.update();
    drawMatrix();
    FastSPI_LED.show();

    delay(20);
}

This is incredible work! Can it be adapted to plain arduino and single color 8x8 matrix ?

...mapped to a 240 LED Strip with WS2812 on a cylindrical matrix...

Because many people asked here a quick solution how to map the whole matrix to a strip:

/* 
 * Copyright (C) 2013 Gilad Dayagi.  All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

 /*
  * An example for the Arduino particle system library
  * Creates a spinning galaxy effect
  * 
  * Note: this example uses the colorduino library becuse that is what I had, 
  * but any device that supports setting a pixel to an RGB value can be used
  */

//#include <Colorduino.h>
#include <FastSPI_LED2.h>
#include "ParticleSys.h"
#include "Particle_Bounce.h"
#include "Emitter_Spin.h"
#include "PartMatrix.h"

#define NUM_LEDS 240

CRGB leds[NUM_LEDS];

const byte numParticles = 600;

Particle_Bounce particles[numParticles];
Emitter_Spin emitter(112, 112, 5, 7);
ParticleSys pSys(numParticles, particles, &emitter);
PartMatrix pMatrix;

/**
 * Render the particles into a low-resolution matrix
 */
void drawMatrix(){
    pMatrix.fade();
    pMatrix.render(particles, numParticles);
    //update the actual LED matrix
   for (byte y=0;y<8;y++) {
        for(byte x=0;x<8;x++) {
            //Colorduino.SetPixel(x, y, pMatrix.matrix[x][y].r, pMatrix.matrix[x][y].g, pMatrix.matrix[x][y].b);
    leds[40+(x*2)+y*20].r = pMatrix.matrix[x][y].r;
    leds[41+(x*2)+y*20].r = pMatrix.matrix[x][y].r;
    leds[40+(x*2)+y*20].g = pMatrix.matrix[x][y].g;
    leds[41+(x*2)+y*20].g = pMatrix.matrix[x][y].g;
    leds[40+(x*2)+y*20].b = pMatrix.matrix[x][y].b; 
    leds[41+(x*2)+y*20].b = pMatrix.matrix[x][y].b; 
        }
    }
}

void setup()
{
  //Colorduino.Init(); // initialize the board
  FastLED.addLeds<WS2812, 6, GRB>(leds, NUM_LEDS);
  // compensate for relative intensity differences in R/G/B brightness
  // array of 6-bit base values for RGB (0~63)
  // whiteBalVal[0]=red
  // whiteBalVal[1]=green
  // whiteBalVal[2]=blue
  byte whiteBalVal[3] = {36,63,7}; // for LEDSEE 6x6cm round matrix
  //Colorduino.SetWhiteBal(whiteBalVal);
  
  randomSeed(analogRead(0));
  
  pMatrix.reset();
 
  //Colorduino.FlipPage(); // swap screen buffers to show it
  
  emitter.oscilate = true;
}

void loop()
{
    pSys.update();
    drawMatrix();
    //Colorduino.FlipPage();
    FastSPI_LED.show(); 
    delay(5);
}

would like to know if someone successfully ported the led matrix code to the led strip hardware.

im working on it , but it seams that in not only a conversion of coordinates and also adapting to use the led strip functions, but it seams that the libraries itself needs to be modified .
As i still don't think this make sense, im inclined to think , that a better knowledge of how to modify the fountain generation parameters , for a certain led pattern is what is needed.

Then my question, is if somebody have any documentation on the libraries.

thanks

sweet, added yo my site :wink:

anybody knows how to modify the code in order to include a wider matrix?
I have tried by changing the y range to 30 , but this doesn't work .

/*

  • Copyright (C) 2013 Gilad Dayagi. All rights reserved.
  • This program is free software: you can redistribute it and/or modify
  • it under the terms of the GNU Lesser General Public License as published by
  • the Free Software Foundation, either version 3 of the License, or
  • (at your option) any later version.
    */

/*

  • An example for the Arduino particle system library
  • A wandering particle that emits colorful smoke
  • Note: this example uses the colorduino library becuse that is what I had,
  • but any device that supports setting a pixel to an RGB value can be used
    */

//'r' for rainbowduino, 'c' for colorduino
//#define BOARD 'r'

//#if BOARD == 'c'
//#include <Colorduino.h>
//#else
//#include <Rainbowduino.h>
//#endif
#include "FastLED.h"
#include "ParticleSys.h"
#include "Particle_Std.h"
#include "Particle_Fixed.h"
#include "Emitter_Fountain.h"
#include "PartMatrix.h"

const byte numParticles = 40;
boolean pulseOn = false;

Particle_Std particles[numParticles];
Particle_Fixed source;
Emitter_Fountain emitter(0, 1, 10, &source);
ParticleSys pSys(numParticles, particles, &emitter);
PartMatrix pMatrix;

#define NUM_LEDS 237

CRGB leds[NUM_LEDS];

/**

  • Render the particles into a low-resolution matrix
    */
    void drawMatrix(){
    pMatrix.reset();
    pMatrix.render(particles, numParticles);
    //update the actual LED matrix
    int LEDnum ;
    for (byte y=0;y<8;y++) {
    for(byte x=0;x<8;x++) {
    //#if BOARD == 'c'
    // Colorduino.SetPixel(x, y, pMatrix.matrix[x][y].r, pMatrix.matrix[x][y].g, pMatrix.matrix[x][y].b);
    //#else
    // Rb.setPixelXY(PS_PIXELS_Y-y-1, x, pMatrix.matrix[x][y].r, pMatrix.matrix[x][y].g, pMatrix.matrix[x][y].b);
    //#endif

if ((y & 01) == 0) {
LEDnum = (y+1)30 - x-1;
} else {
LEDnum=y
30+x;
}

leds[LEDnum].r = pMatrix.matrix[x][y].r;
leds[LEDnum].g = pMatrix.matrix[x][y].g;
leds[LEDnum].b = pMatrix.matrix[x][y].b;
}
}
}
// *******************************************//

void setup()
{
//#if BOARD == 'c'
// Colorduino.Init(); // initialize the board
FastLED.addLeds<WS2812B, 6, GRB>(leds, NUM_LEDS);
// compensate for relative intensity differences in R/G/B brightness
// array of 6-bit base values for RGB (0~63)
// whiteBalVal[0]=red
// whiteBalVal[1]=green
// whiteBalVal[2]=blue
byte whiteBalVal[3] = {36,63,7}; // for LEDSEE 6x6cm round matrix
//Colorduino.SetWhiteBal(whiteBalVal);
//#else
// Rb.init();
//#endif

randomSeed(analogRead(0));

//source.vx = 3;
//source.vy = 1;
source.x = 0;
source.y = 1;
Emitter_Fountain::minLife = 20;
Emitter_Fountain::maxLife = 80;
Particle_Std::ay = 1;
//PartMatrix::isOverflow = false;

//init all pixels to zero
pMatrix.reset();
}

void loop()
{
pSys.update();
drawMatrix();
//#if BOARD == 'c'
//Colorduino.FlipPage();
FastLED.show();
//#endif
delay(40);
}