My initial inspiration for this project idea came from a variety of sources. The idea started from being intrigued by the effects of light inside infinity mirrors and infinity cubes from our initial light artist and general research. I then wanted to personalize the project by both changing the shape to be the same as a twenty sided die (d20) used in Dungeons and Dragons as well as by programming it to be a music visualizer using my prior experience of Arduinos.
The project itself was challenging both mechanically and programmatically and as a result went through an iterative design process along the way. The mechanical design and assembly of the project was definitely the area I was least familiar with and where I encountered the most setbacks and revisions. However in the end I was able to successfully complete my project at a state that I am very happy with while also learning Blender and gaining more experience in 3D printer, laser cutting, soldering, and Arduino programming. Images and references to the creation of this project can be found at the end of this blogpost.

For my final documentation, I decided to present it in a video format as the project is meant to be viewed as a dynamic piece reacting to music. The video itself consists of a mash of 3 songs filmed at different angles. It was difficult to find the right balance between lowering the contrast to decrease the LED glare on the camera versus keeping the normal contrast to not lose the contrast between the different LED colors. So I ended up playing with different contrast levels for different shots. I found the close up shots on the second song looked really cool and definitely came out the best on the camera. You can see the final video bellow, which is a mix of Flight by Tristam & Braken, Take Over by Jeremy McKinnon, and To The Stars by Braken.
Other Images:


My final Arduino code I used for this project. I made use of the Arduino.h, FastLED.h, and math.h libraries:
#include <Arduino.h>
#include <FastLED.h>
#include <math.h>
#define LED_PIN 7
#define NUM_LEDS 117
#define SPECTRUM_LENGTH 6
int RED[3] = {255, 0, 0};
int GREEN[3] = {0, 255, 0};
int BLUE[3] = {0, 0, 255};
int ORANGE[3] = {255, 100, 0};
int PURPLE[3] = {100, 0, 255};
int YELLOW[3] = {255, 255, 0};
CRGB leds[NUM_LEDS];
byte first = 1;
int band[18];
int scale = 1;
int color[3] = {RED[0], RED[1], RED[2]};
int rnd[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
void set_band (int b0, int b1, int b2, int b3, int b4, int b5, int b6, int b7, int b8,
int b9, int b10, int b11, int b12, int b13, int b14, int b15, int b16, int b17) {
band[0] = b0;
band[1] = b1;
band[2] = b2;
band[3] = b3;
band[4] = b4;
band[5] = b5;
band[6] = b6;
band[7] = b7;
band[8] = b8;
band[9] = b9;
band[10] = b10;
band[11] = b11;
band[12] = b12;
band[13] = b13;
band[14] = b14;
band[15] = b15;
band[16] = b16;
band[17] = b17;
}
void setupSpectrum() {
//Setup pins to drive the spectrum analyzer. It needs RESET and STROBE pins.
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
//Init spectrum analyzer
digitalWrite(4,LOW); //pin 4 is strobe on shield
digitalWrite(5,HIGH); //pin 5 is RESET on the shield
digitalWrite(4,HIGH);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
}
void setup() {
Serial.begin(9600);
Serial.println("Hello World!");
setupSpectrum();
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
}
int getLoudest(int spectrum[SPECTRUM_LENGTH]) {
byte loudestBand = 0;
int loudestValue = -1;
for(byte band = 0; band < SPECTRUM_LENGTH; band++) {
if (spectrum[band] > loudestValue) {
loudestBand = band;
loudestValue = spectrum[band];
}
}
return loudestBand;
}
void assignColor(int c[3]) {
color[0] = c[0];
color[1] = c[1];
color[2] = c[2];
}
void visualizeLoudest(int loudest) {
FastLED.clear();
switch (loudest)
{
case 0:
assignColor(ORANGE);
break;
case 1:
assignColor(BLUE);
break;
case 2:
assignColor(RED);
break;
case 3:
assignColor(GREEN);
break;
case 4:
assignColor(PURPLE);
break;
case 5:
assignColor(YELLOW);
break;
case 6:
assignColor(YELLOW);
break;
}
}
int r() {
return ((int)random(0, 2)) * 2;
}
void visualizeBand(float value, float max_val, float min_val) {
int diff = round((value - min_val) / (max_val - min_val) * 10);
if (diff > 10) diff = 10;
scale = 13 - diff;
switch (diff)
{
case 0:
set_band(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
break;
case 1:
set_band(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
break;
case 2:
set_band(0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0);
break;
case 3:
set_band(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
break;
case 4:
set_band(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0);
break;
case 5:
set_band(0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0);
break;
case 6:
set_band(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0);
break;
case 7:
set_band(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0);
break;
case 8:
set_band(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
break;
case 9:
set_band(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
break;
case 10:
if(millis() % 3 == 0) {
rnd[0] = r(); rnd[1] = r(); rnd[2] = r(); rnd[3] = r(); rnd[4] = r(); rnd[5] = r(); rnd[6] = r(); rnd[7] = r(); rnd[8] = r(); rnd[9] = r(); rnd[10] = r(); rnd[11] = r(); rnd[12] = r(); rnd[13] = r(); rnd[14] = r(); rnd[15] = r(); rnd[16] = r(); rnd[17] = r();
}
set_band(rnd[0], rnd[1], rnd[2], rnd[3], rnd[4], rnd[5], rnd[6], rnd[7], rnd[8], rnd[9], rnd[10], rnd[11], rnd[12], rnd[13], rnd[14], rnd[15], rnd[16], rnd[17]);
break;
default:
set_band(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
break;
}
}
void lightLED(int pos, int mode) {
switch (mode)
{
case 0:
leds[pos] = CRGB(0, 0, 0);
break;
case 1:
leds[pos] = CRGB(color[0] / scale, color[1] / scale, color[2] / scale);
break;
case 2:
leds[pos] = CRGB(color[0], color[1], color[2]);
break;
default:
leds[pos] = CRGB(0, 0, 0);
break;
}
}
void visualizeSpectrum(int spectrum[SPECTRUM_LENGTH], int start) {
for (int i = 0; i < SPECTRUM_LENGTH; i++) {
visualizeBand((float)spectrum[i], (float)1000, (float)50);
for (int pos = 0; pos < 18; pos ++) {
lightLED(start + pos + i * 18, band[pos]);
}
}
}
void printSpectrum(int spectrum[SPECTRUM_LENGTH]) {
for (int i = 0; i < SPECTRUM_LENGTH; i++) {
Serial.print(spectrum[i]);
Serial.print("\t");
}
Serial.println("");
}
// Function to read 7 band equalizers
void dance() {
// Spectrum analyzer read values will be kept here.
int middle[7]; // Band 0 = Lowest Frequencies.
int right[7];
int left[7];
// Get the reading from each of 7 channels each (left and right)
byte Band;
for (Band=0;Band <7; Band++) {
// left
left[Band] = analogRead(0);
// right
right[Band] = analogRead(1);
// middle
middle[Band] = (left[Band] + right[Band]) / 2;
digitalWrite(4,HIGH); //Strobe pin on the shield
delay(2);
digitalWrite(4,LOW);
}
visualizeLoudest(getLoudest(middle));
visualizeSpectrum(middle, 0);
// printSpectrum(middle);
FastLED.show();
}
void loop() {
dance();
delay(25);
}
Your dice came out really well, I lobe the infinity mirror aspect I think that it looks awesome!
I love your up-close pictures, the inside of the cube looks so cool! I’m also really impressed with how the LEDs change with the music. I really like your final piece, awesome job!
When I initially watched your video, I was floored by how professional it was. Your project is so clean and complete, something that a company would be proud to put out as its final project. I love how in-depth you are with the technical side, and how much effort was put into representing music as light art.
This project looks really professional and well done, maybe you can pair it with the light tree and start a rave. Also, the infinity mirror illusion is really cool and kind of masked the leds that did not turn on.