July 2, 2017
Hey! I just uploaded this visualizer (below) to a remix of my song Love. If you want to learn about how I made the visualizer, keep reading.
It was done entirely in Python (my favorite programming language). At a high level, my code basically creates (from scratch) a whole bunch of images and then compiles them into a video with 30 images (or frames) per second. So how do I make a single frame?
When you look at a color image on a computer, each pixel is typically represented by 3 numbers. Those 3 numbers are the brightness in each of the 3 color channels RED, GREEN, BLUE (or simply RGB). Let’s say those brightness levels can vary anywhere from 0 to 1 (they actually go from 0 to 255 but let’s just use 0 to 1 for simplicity). Using different RGB combinations you can make essentially any color. For example, pure blue is [0,0,1], darker blue is [0,0,0.4], red is [1,0,0], yellow is [1,1,0], magenta is [1,0,1], purple is [0.5,0,1], white is [1,1,1], black is [0,0,0], etc. If you want to play around with RGB coloring, check out this link.
Ok, so the first step in making a frame is to set those RGB values for each pixel in the image. To do this, I first need to understand how to define each pixel’s location. For a single frame, pretend it’s actually an X-Y plot, where the (0,0) point is the bottom left, and each pixel’s location can be defined in (X,Y) coordinates. So for a 1000×1000 pixel image, for instance, the top right pixel is located at (1000,1000) and the center pixel is at (500,500).
To make the colors vary across the image, I define each RGB channel intensity as a function of X and Y in the frame. What’s particularly useful in this case is the fact that the 3 RGB channels are independent, so I can set up 3 different equations (1 for each RGB channel) that define the coloring. To make the color vary in a wave-like manner, these equations need to be sine waves. Something like R=sin(AX+BY/2). The width (or wavelength) of these color bands/stripes can be varied by playing around with those A and B constants. I chose to use different wavelengths for each RGB channel so that they end up mixing in interesting ways.
For instance, if a red stripe crosses paths with a blue stripe, what you see in the overlapping region is purple. So, while the RGB channels are defined independently, what your eye actually sees is a mix of all of them. That’s what creates the huge range of constantly morphing colors, rather than simply red, green, and blue. Super beautiful.
Once those RGB equations were defined, I wanted to give the image that cool symmetry. This step was the hardest part to program. To do this, I first erased everything except 1/8th of the image (cut like a slice of pizza). This is the repeating pattern. Imagine taking a single slice of pizza, and flipping it along one side edge so that it’s now face-down in the neighboring spot. Do it again, and now it’s face-up in the next spot. Keep doing that until you go all the way around and you’re back to where you started. That’s what I did, in a nutshell. It means that each 1/8th slice is a mirror image of it’s neighbors. And that’s what gives you that trippy kaleidoscopic effect.
OK, so now we have one full image. To make the whole video look like it’s moving, each frame needs to vary slightly from the frame before it. This was done by including a “time” variable, t, in the RGB vs. XY functions. Something like R=sin(tAX+BY/2). Each time a new frame is rendered, the equation describing the colors is a tiny bit different because t is increasing. It also causes the wavelength (the width of the color stripes) to vary in time, which explains why the visualizer appears to keep changing. These changing wavelengths were initially unintended, but once I noticed they were happening I fell in love.
Alright, so now we have a bunch of frames that vary in time. Great. Now, you may have noticed that the visualizer responds to the music. I found some Python code that calculates a fast fourier transform of the song. It’s incredibly complicated, and I don’t fully understand how it works, but the output of that code is the sound “intensity” or “loudness” at each frequency at each time. For instance, when a loud kick drum is happening, the frequencies between 30-100Hz have a high intensity. To make the visualizer respond to the music, I inserted this low frequency intensity into a couple of the equations that define the color bands. It’s a little tricky getting it to respond to the bass and kick drum without making the visualizer too strobey. It’s a balance.
And that’s it! Now I can just run the program and it spits out a video. Follow all that? Do you have any suggestions for improving this visualizer? Do you want even MORE detail? Props to you if you got this far. Thanks for reading.
My next blog post will likely be a more detailed discussion of how I make my music. Would that interest you? Stay tuned 😉
I love you.
Anthony / Trazer