Lissajous curves are fun. And who doesn’t dream of standing right inside one all the time? The boys from Tame Impala certainly do, because some of their concert’s light shows consisted of little else than Lissajous curves:

When I was at one of their shows, I actually saw how they put a camera in front of an old analogue oscilloscope in a corner of the stage to capture them.

WebVR now makes it possible to fully immerse in these curves.

But first, we have to extend the well-known 2D curves that are generated by oscilloscopes to the third dimension, so that

x = A_{x} * sin(ω_{1}t + δ)

y = B_{y} * sin(ω_{2}t)

becomes

x = A_{x} * sin(ω_{1}t + δ)

y = B_{y} * sin(ω_{2}t)

z = C_{z} * sin(ω_{3}t)

Again, I’m using the WebVR Boilerplate, which allows to get started quickly. Let’s create some spheres at first. You can of course choose to use heart symbols or, even better, kittens instead of spheres.

var spheres = []; var geometry = new THREE.SphereGeometry(0.01, 16, 16); var material = new THREE.MeshNormalMaterial(); var number_of_spheres = 10000; for (var i = 0; i < number_of_spheres; i++){ var sphere = new THREE.Mesh(geometry, material); scene.add(sphere); sphere.position.setZ(-1); spheres.push(sphere); }

We set the spheres to a Z position of -1, in order to see them when creating 2D curves.

Now, we declare angular frequencies, amplitudes and phase. Be aware, that it could get really messy if you don’t use frequencies that **almost** have simple ratios. Be also aware, that when using **exact** ratios with small integers like 1:2:3 or 2:3:4, the curve will not move. Look at the Wikipedia examples to get an impression of 2D curve looks with simple frequency ratios.

//frequencies var fx = 300; var fy = 700; var fz = 1200.05; //calculate angular frequencies var af_x = 2 * Math.PI * fx; var af_y = 2 * Math.PI * fy; var af_z = 2 * Math.PI * fz; //amplitudes var A = 1; var B = 1; var C = 1; var phase = 0;

In the animate function, we calculate the timestamp for each sphere in each animation frame. The framerate is usually 60 Hz, so we have to divide the timestamp offset by 60. We could improve this by checking by how much the timestamp increases with each frame, but let’s keep it easy for now.

// Request animation frame loop function function animate(timestamp) { //get all spheres to their right position for (var i = 0; i < number_of_spheres; i++){ var timestamp_in_seconds = timestamp / 1000;//get the exact timestamp offset for every single sphere. since one animation frame has a duration of 1/60 s, we do var timestamp_offset = i / number_of_spheres / 60;var t = timestamp_in_seconds + timestamp_offset; var x = A * Math.sin(af_x * t) + phase; var y = B * Math.sin(af_y * t); var z = C * Math.sin(af_z * t); spheres[i].position.setX(x); spheres[i].position.setY(y); // you can comment out the following line to obtain a 2D curve spheres[i].position.setZ(z); }

And then we should get something like this:

## Using Web MIDI to control frequencies

Instead of hard-coding the three frequencies in the script, we could use Web MIDI to read them from a connected MIDI keyboard.

The code for this is included in the demo, but it does not work too well, because piano notes are equal tempered and result in messy curves. See this nice video (using Lissajous curves) for an explanation: