A music video designed and coded for a single ”bamb00” by a Polish drummer wh0wh0 (Jacek Prościński).
The video is a live recording of the website bamb00.net, coded in JavaScript to animate 3D objects, triggered by the sounds of the drums. Every playback generates a different result – random elements, perspectives. and movements – guided by algorithmic rules. Read more about the coding process
GOALS
When I first heard the track, I realized this task would require a unique approach. Jacek’s music is insanely dynamic and sounds different at every performance. Sometimes, certain parts are extended longer than usual. That’s why I began thinking about how to make the visuals more adaptable over time and focused on the generative aspect of the project. With so many layers and sounds involved, I knew that to create a proper visualization, I needed to try something that would give me flexibility and also challenge me. I decided to work in 3D. Additionally, the system had to function in real-time for the live shows (ideally at 60 fps), which meant it needed to be lightweight to render and designed in a simple, efficient way.
Solutions
DYNAMIC
I began by gathering all the information about the scenes, key moments, and drum notes into a MIDI track. A MIDI track is easy to edit in a program like Ableton and can also be loaded onto a website.
I counted thirteen scenes and set different parameters for each to vary their dynamics.
To amplify the sense of the vast number and density of notes being played, I made sure not to miss any sounds in the MIDI track. I then connected each note to specific functions, such as moving, flipping, or rotating particles, rotating the camera, adjusting gaps or scales, and changing sets of elements.
if (midiNote==0) { // KICK
if (chance(0.85)) resetParts();
else if (changeTransforms)
for (int i=0; i<7; i++) translateParticle();
if (chance(changeViewChance)) setRandomView();
if (changeSpaces) randomSpacing();
if (changeScales) randomScaling();
if (changeRandomRotY) randomRotY();
if (changeRandomProtY) randomPRotY();
if (changePartsSet) randomPartsSet();
}
HEAD PARTS
The core element of the video is a head divided into multiple parts of varying sizes. To generate these parts, I needed a library capable of intersecting 3D objects. miho’s JSCG library was the best choice. It's based on the JavaScript csg.js library, which I later used in the on-line version.
With this library, cutting the object into blocks became straightforward. Each block was created by intersecting the object (the head) with a translated cube. However, generating these intersections was time-consuming (around 30 seconds for a 5x5x5 set), so I pre-generated all the configurations I wanted to use.
I aimed to avoid working with a classic timeline as much as possible. In most cases, I used my eased() function to smooth the animation.
function eased(from, to, ease) {
return from + ( to - from ) * ease;
}
For example, when working with position, this function calculates a point along the path to a given destination based on the progress and the speed I choose.
To achieve a simple, clean effect, I went for an outline "vector" style. Initially, I considered using an edge detection filter, but it proved to be too crisp and not performed well. Instead, I separated the object's fill and stroke, then slightly translate the fill towards the camera, allowing it to partially overlay the strokes.