Chilling inside a giant Lissajous curve with WebVR

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 = Ax * sin(ω1t + δ)
y = By * sin(ω2t)

becomes

x = Ax * sin(ω1t + δ)
y = By * sin(ω2t)
z = Cz * sin(ω3t)

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:

lissajous 3d

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.

USB 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:

Demo

https://webaudiotech.com/sites/inside_lissajous

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.