When I was a kid, I really enjoyed this game called Electroplankton. Especially the game mode called Hanenbow, where you launched small tadpoles onto leaves. On impact the leaves would emit glockenspiel sounds, and create a nice sounding melody. Here’s what it looked like:

I wondered, how hard could it be to implement something similar? If you just want to see the animation, click here. If you’re interested in the background, read on!

Concept

The basic idea: Cannon, Balls, Bouncers

The basic idea: Cannon, Balls, Bouncers

First we have a cannon that shoots out balls at a certain frequency. These balls then fly through a field of bouncers, that act like small trampolines for the balls. Upon hitting a bouncer, a ball will emit a certain tone.

Physics

We’re going to be using the wonderful CindyJS Framework, that does a lot of the heavy lifting behind the scenes.

Ball flying physics is easy. A ball with position $x$ and velocity $v$ will be updated in two steps:

  • Updating the speed: $v \gets v + dt \cdot g$, with a time step $dt$ and the gravity vector $g$.
  • Updating the position: $x \gets x + dt \cdot v$

The collisions with the bouncers are where it gets tricky. First of all, how do we detect a collision?

This is what we want to detect: $x$ and $\bar x$ are on opposite sides of the bouncer.

This is what we want to detect: $x$ and $\bar x$ are on opposite sides of the bouncer.

Say a ball is updated from position $x$ to position $\bar x$. We want to check if it collides with a bouncer during that update. A bouncer is essentially a line between two points $b_1$ and $b_2$.

One can see that we have a collision if and only if these two conditions are fulfilled:

  • $x$ and $\bar x$ lie on opposing sides of the line $b_1b_2$.
  • $b_1$ and $b_2$ lie on opposing sides of the line $x\bar x$.

There is a neat way of calculating which side of a line a point is on using determinants. The determinant

$$\det\begin{pmatrix}x_A&x_B&x_C\\ y_A&y_B&y_C\\ 1&1&1\end{pmatrix}$$

will have a positive sign if $C$ lies left of the line $AB$, and a negative one if it lies on the right:

The sign of the determinant tells us whether $C$ is left or right of the line $AB$

So a collision is equivalent to having both $${\det(b_1,b_2,x) \cdot \det(b_1,b_2,\bar x) \leq 0}$$ and $${\det(x, \bar x, b_1) \cdot \det(x, \bar x, b_2) \leq 0}.$$

Upon collision, we want to play a sound and reflect the ball. This is done by setting the velocity $v \gets v - 2\langle v,n \rangle n$, where $n$ is the bouncer’s normal.

Music

If we were to just emit random tones, the result would be disharmony. We must therefore pick the tones we play from some harmonic distribution. The easiest way to do this is to limit the tones to a certain chord. We can then change the chords over time, to get a nice little tune.

As you have probably guessed from the title, we’ll be using the chord progression from Pachelbel’s Canon.

(Transposed) Chord Progression of Pachelbel’s Canon

(Transposed) Chord Progression of Pachelbel’s Canon

CindyJS has a MIDI Interface, that just enumerates musical notes starting with C0. So our chord progression can be transcribed into

chords = [
    [63, 68, 72], // Ab
    [63, 67, 70], // Eb
    [60, 65, 68], // Fm
    [60, 63, 67], // Cm
    [56, 61, 65], // Db
    [56, 60, 63], // Ab
    [56, 61, 65], // Db
    [58, 63, 67]  // Eb
];

Each one of the bouncers will play a tone based on this chord table, where the row will cycle through time.

Finished animation

Putting it all together, we get this nice little interactive animation.

If you’re interested in the source code, you can find it here.