🔍

DOTMATRIX – creative coding in Processing for LED Panels

basic setup

Creating interactive applications for a limited number of LED panels can be understood as the development of an application for a second screen of a pretty limited dimension. Therefore it comes handy to create a simple custom framework to both deploy the panel screen and a primary monitor for development and advanced control during the installation. This can become quiet complicated soon, so for conveience, i created a wrapper script, that takes care of all buffers, placements and checks. One simple skew is needed indeed: Instead of using the common „setup“ and „draw“ function, simply use the custom „SETUP“ and „UPDATE“ functions. This framework core may not be touched in general – just copy paste it in your project.

*The framework has a development mode switch integrated. ( bool devmode ) This is needed to focus processing power to the development monitor or the actual LED panels.

basic two screen setup

ProjectorSketch projectorSketch;
boolean devmode = true;

void setup() {
  
  size(512, 512); //setup the primary Monitor sketch size
  SETUP(); // execute the custom SETUP
  projectorSketch = new ProjectorSketch();  // Create an instance of ProjectorSketch
  
  // Run the ProjectorSketch
  String[] args = {"--display=2", "--present", "Projector"};
  PApplet.runSketch(args, projectorSketch);
 
}

void draw() {
    // Display the buffer
    image(BUFFER, 0, 0);
    // Update the buffer
   if(!devmode){UPDATE();}
}


class ProjectorSketch extends PApplet {
  void settings() {
    fullScreen(JAVA2D);
  }

  void draw() {
    // Draw the buffer on the projector sketch
    // Update the buffer
   if(devmode){UPDATE();}else{image(BUFFER, 0, 0);}
   
  }
}

// ###################################################
// ################ WORK BELOW HERE :) ###############
// ###################################################

// setup the dimensions of the LED Matrix here
int W = 128;
int H = 512;

PGraphics BUFFER; // Graphics buffer shared between sketches

void SETUP(){
     // Create the buffer
  BUFFER = createGraphics(W, H);
  UPDATE();

}

void UPDATE() {
    // Draw something to the buffer
  BUFFER.beginDraw();
  BUFFER.fill(0,4);
  BUFFER.noStroke();
  BUFFER.rect(0,0,W,H);
  BUFFER.stroke(255);
  BUFFER.strokeWeight(4);
  BUFFER.line(random(W), random(H), random(W), random(H));
  BUFFER.endDraw();

}


advanced setup

The advanced setup comes with p5control, an additional* UI for Processing – very handy for generative design development. ( *needs to be installed for your IDE! ) In the example, we instantiate 50 custom class objects called aDOT. These objects will then be updated each frame and drawn to the buffer.

advanced setup with UI controls and objects

ProjectorSketch projectorSketch;
import controlP5.*;
ControlP5 cp5;

//setup the UI values here
boolean devmode = true;
float noise_motion = 0;

void setup() {
  
  size(512, 512); //setup the primary Monitor sketch size
  setupUI(); // initialized the p5 contol UI
  SETUP(); // execute the custom SETUP
  projectorSketch = new ProjectorSketch();  // Create an instance of ProjectorSketch
  
  // Run the ProjectorSketch
  String[] args = {"--display=2", "--present", "Projector"};
  PApplet.runSketch(args, projectorSketch);
 
}

void setupUI(){

   cp5 = new ControlP5(this); 
  
  int xpos = 150;
 
     // create a devmode toggle
  cp5.addToggle("devmode")
     .setPosition(xpos,10)
     .setSize(30,30)
     ;
  // create a simple slider
    cp5.addSlider("noise_motion")
     .setPosition(xpos,80)
     .setRange(.1,8)
     .setSize(200,20)
     ;
}

void draw() {
    // Display the buffer
    image(BUFFER, 0, 0);
    // Update the buffer
   if(!devmode){UPDATE();}
}


class ProjectorSketch extends PApplet {
  void settings() {
    fullScreen(JAVA2D);
  }

  void draw() {
    // Draw the buffer on the projector sketch
    // Update the buffer
   if(devmode){UPDATE();}else{image(BUFFER, 0, 0);}
   
  }
}


// ###################################################
// ############ MAINLY WORK BELOW HERE :) ###############
// ###################################################


// setup the dimensions of the LED Matrix here
int W = 128;
int H = 512;

aDOT[] dots = new aDOT[50]; // create 50 dot objects

PGraphics BUFFER; // Graphics buffer shared between sketches

void SETUP(){
  // Create the buffer
  BUFFER = createGraphics(W, H);
 
  // initalize all 50 Dots
   for (int i = 0; i < dots.length; i++) {
    dots[i] = new aDOT(random(W), random(H), random(1, 3), random(TWO_PI));
  }
   

}

void UPDATE() {
  
   BUFFER.beginDraw();
   
   // draw transparent rect for constant fade out
   BUFFER.fill(0,4);
   BUFFER.noStroke();
    BUFFER.rect(0,0,W,H);
   
   // call each dot, update and draw it!
   for (int i = 0; i < dots.length; i++) {
    dots[i].move();
    dots[i].display();
  }
  
   BUFFER.endDraw();

}

// ###################################################

class aDOT {
  float x, y; // Position
  float speed; // Speed
  float angle; // Angle of movement

  aDOT(float x_, float y_, float speed_, float angle_) {
    x = x_;
    y = y_;
    speed = speed_;
    angle = angle_;
  }

  void move() {
    // Move the dot
    x += cos(angle) * speed;
    y += sin(angle) * speed;

    x+=  (noise(x*.05,y*.04)-.5)*noise_motion;
    x+=  (noise(y*.05,x*.04)-.5)*noise_motion;


    // Check if dot is outside the stage
    if (x < 0) {
      x = W;
    } else if (x > W) {
      x = 0;
    }
    if (y < 0) {
      y = H;
    } else if (y > H) {
      y = 0;
    }
  }

  void display() {
    // Display the dot
   
    BUFFER.fill(255);
    BUFFER.ellipse(x, y, 10, 10);
  }
}

camera feed setup

As sensory input we can feed a camera in the application. In this basic setup, a camera is called with a very low resolution of 160×120 pixels. Each frame, the brightness of each pixel is calculated and combined with a simple threshold if case.

live camera feed setup

ProjectorSketch projectorSketch;
import controlP5.*;
ControlP5 cp5;

//setup the UI values here
boolean devmode = true;
float threshold = 0;

void setup() {
  
  size(512, 512); //setup the primary Monitor sketch size
  setupUI(); // initialized the p5 contol UI
  SETUP(); // execute the custom SETUP
  projectorSketch = new ProjectorSketch();  // Create an instance of ProjectorSketch
  
  // Run the ProjectorSketch
  String[] args = {"--display=2", "--present", "Projector"};
  PApplet.runSketch(args, projectorSketch);
 
}

void setupUI(){

   cp5 = new ControlP5(this); 
  
  int xpos = 150;
 
     // create a devmode toggle
  cp5.addToggle("devmode")
     .setPosition(xpos,10)
     .setSize(30,30)
     ;
  // create a simple slider
    cp5.addSlider("threshold")
     .setPosition(xpos,80)
     .setRange(44,224)
     .setSize(200,20)
     ;
}

void draw() {
    // Update the buffer
  if( devmode){UPDATE(); image(BUFFER, 0, 0);}
  
}


class ProjectorSketch extends PApplet {
  void settings() {
    fullScreen(JAVA2D);
  }

  void draw() {
    // Draw the buffer on the projector sketch
     if(!devmode){UPDATE(); image(BUFFER, 0, 0);}
   
  }
}

// ###################################################
// ############ MAINLY WORK BELOW HERE :) ###############
// ###################################################


// setup the dimensions of the LED Matrix here
int W = 128;
int H = 512;

 
import processing.video.*;
PGraphics BUFFER; // Graphics buffer shared between sketches

Capture video;

void SETUP(){
  // Create the buffer
  BUFFER = createGraphics(W, H);
  BUFFER.smooth();
  BUFFER.beginDraw();
  BUFFER.background(222);
  BUFFER.endDraw();
 
  video = new Capture(this, 160, 120);

  // Start capturing the images from the camera
  video.start();
 

}

void UPDATE() {
    
     background(222);
   
     
     set(150, 120, video);
     
   
      BUFFER.beginDraw();
      
      int index = 0;
     video.loadPixels();
      for (int y = 1; y < video.height; y++) {

   
        for (int x = 0; x < video.width; x++) {
     
            int pixelColor = video.pixels[index];
              
       
            // Faster method of calculating r, g, b than red(), green(), blue()
            int r = (pixelColor >> 16) & 0xff;
            int g = (pixelColor >> 8) & 0xff;
            int b = pixelColor & 0xff;
            
            float px = x*.8;
            int py = y*4;
            
            int pixelBright = max(r, g, b);
            if(pixelBright > threshold){
              BUFFER.noStroke();
              BUFFER.fill(255);
              BUFFER.circle(px,py,4);
            }else{

              BUFFER.noStroke();
              BUFFER.fill(0);
              BUFFER.circle(px,py,4);
            
            
            }
             // Move to the next pixel
            index++;
      
      }
  
      }
      
       BUFFER.endDraw();
 
}


void captureEvent(Capture c) {
  c.read();
}