Chladni patterns are cool. I first read about them on Paul Bourke’s blog. Essentially, they are patterns that arise from certain frequencies on a bounded surface. The classic example is a violin bow pulled across the edge of a metal plate with salt on it.

Chladni patterns fall under the umbrella of the field of cymatics. There are similar phenomena that can be observed all over. For example, in fluids there are faraday waves. One of my favorite demonstrations of this branch of physics is this youtube video which unfortunately does not allow playback when embedded.

The equation for Chladni patterns on a rectangular bounded surface is given by:

\[\cos(\frac{n\pi x}{L})\cdot\cos(\frac{m\pi y}{L}) - \cos(\frac{m\pi x}{L})\cdot\cos(\frac{n\pi y}{L}) = 0\]

This equation was recreated using the Math and Function CHOPs in TouchDesigner. Adding two more channels allowed it to be represented as a 2D texture using CHOP to TOP. A few more tweaks to clean up the texture and a nice normal map was created using the Normal Map TOP. The normal map provides the velocity at any given point in the texture.

All that’s left is to create a particle system. This was achieved using some noise and a GLSL shader to utilize the information encoded in the normal map that was just created. 2D noise was created with the Noise TOP and fed into a GLSL Multi TOP along with a Feedback TOP.

The shader code:

out vec4 fragColor;

uniform float speed;

uniform vec2 vel_add;
uniform vec2 vel_scalar;

uniform vec2 pos_add;
uniform vec2 pos_scalar;
uniform vec2 pos_add_vel_toggle;
uniform vec2 pos_vel_scalar;

void main()
{
    // sTD2DInputs are the incoming TOP connections
    // [0] - Feedback TOP
    // [1] - Noise TOP (Initial state)
    // [2] - Normal Map TOP (Provides the velocity information)
	vec4 position = texture(sTD2DInputs[0], vUV.st);
	vec4 initial = texture(sTD2DInputs[1], vUV.st);
	vec4 velocity = texture(sTD2DInputs[2], position.xy / 2 + .5);

	velocity = velocity * 2.0 - 1.0;

	vec2 derivative = vec2(dFdx(position.r), dFdy(position.y));

	// velocity modifier
	velocity.st += derivative * .05;
	velocity.x += vel_add.x;
	velocity.y += vel_add.y;
	velocity.x *= vel_scalar.x;
	velocity.y *= vel_scalar.y;
	// position modifiers
	position.x += pos_add.x;
	position.y += pos_add.y;
	position.x *= pos_scalar.x;
	position.y *= pos_scalar.y;

	// adds current particle velocity to particle if uniform value is set to 1
	if(pos_add_vel_toggle.x == 1) {
		position.x += velocity.x * pos_vel_scalar.x;
	}
	if(pos_add_vel_toggle.y == 1) {
		position.y += velocity.y * pos_vel_scalar.y;
	}

	position += velocity * speed;

    // reset particle position if out of bounds
	if (position.x < -.995 || position.x > .995 || position.y < -.995 || position.y > .995) {
		position = initial;
	}
	
	fragColor = TDOutputSwizzle(position);
}

TouchDesigner provides a useful interface for modifying the values of GLSL uniforms through its GUI while also allowing them to be modified in real time through automation. I really love how this program provides the flexibility to experiment rapidly while also providing low level access. There is even a C++ API available for writing custom operators.

At this point, I have a 2D texture with all the information needed to create a particle system. Particles can easily be instantiated using the Add SOP hooked up to a Geometry COMP. Hooking up the output from the GLSL Multi TOP in the instantiation menu of the Geometry COMP gives a working particle system. (It’s a little bit more complicated than that, but this isn’t meant to be a TouchDesigner tutorial)

It’s got a bit of flashing from the automation being applied - but that is used when more effects are applied. The magic of TouchDesigner is layering your components to get really interesting visual effects. After experimenting with this base for a while, I hooked up one of my favorite effects to some music to create an audioreactive animation being rendered in real time. Information was extracted from specific frequencies of the audio to drive different parameters. For example, the kick drum for this song was read slightly below 100Hz to power the pulsing animation change.

Resolution is limited since I used the Non-Commercial license of TouchDesigner.