Reactive Lighting Installation

Two friends studying at Leeds College of Music had a module that required them to create a piece of music for an external creative project, exhibition, or show. Together we designed, built, and exhibited the project at Hyde Park Book Club (Leeds) as part of Leeds Digital Festival.

The project consists of:

The Processing application creates an audio input stream and sets the framerate to 100 FPS. The application’s draw function applies an FFT with 512 bins to the input stream and stores the result. The updateTube function is called for each frame to configure each tube’s frequency response, colour, and sensitivity; then calculates the resulting brightness of each tube and draws a visual representation of the tube on the GUI.

Tube Processor GUI


float updateTube(int n, int fLB, int fUB, color colour, float gain){
  
  if(fUB < fLB){
    print("Error: fUB is smaller than fLB");
    return 0.0;
  }
  
  // Convert f to bins
  float fPerBin = 20000/bands;
  int fLBBin = ceil(fLB/fPerBin);
  int fUBBin = ceil(fUB/fPerBin);
  
  float binAvg = 0;
  
  // Get average of bins
  for(int i = fLBBin; i <= fUBBin; i++){
    binAvg = binAvg + spectrum[i];
  }
  
  float binMag = (binAvg/(fUBBin-fLBBin+1));
  binMag = binMag * globalGain * gain;
  
  // avg out
  float alpha = 0.595;
  binMag = (1-alpha) * tubeValues[n] + alpha * binMag;

  float binMult = binMag/255.0;
  if(binMult > 1) binMult = 1;
  byte binMag_byte = (byte)floor(0xFF * binMult);
  
  if(binMag_byte == 0xFF) binMag_byte = (byte)0xFE;
  tubeVals[n] = binMag_byte;
  return binMag;
}

The data is transferred to the microcontroller using a USB serial connection. A serialEvent is used to send the data tubeVals[] whenever the Processing application recives a serial interrupt.

void serialEvent(Serial myPort) {
  myPort.write(tubeVals);  
}

Allowing the microcontroller to request the most up to date data whenever it is ready to refresh the LEDs.

requestData();

if(Serial.available() > 0){
    Serial.readBytes(byteBuffer, NUM_TUBES);
    for(int i = 0; i < NUM_TUBES; i++){
        unsigned int brightness = (unsigned int)byteBuffer[i];
        brightness = brightness * 2;
        if(brightness > 255) brightness = 255; 
        hsv_vals[i][2] = brightness; // Update brightness
    }            
}

Initially 25 tubes were built and arranged in a 5x5 grid: outer ring blue, high frequencies; middle ring yellow, mid range frequencies; center tube red, low frequencies . The average magnitude of the frequency bins that span each tubes response is used to modulate the brightness (V of each HSV colour) to pulse the tube in time with the audio’s transients.

Tube Processor GUI