by Piter Pasma, March 2020
This article is about the basics of constructing cool loopy functions, sometimes also called harmonographs (but that’s a very broad term), and shows step by step how I build and investigate some possible formulas for them.
There are various curious things to do with combinatorics going on, and things related to ratios. If you have a better idea than me about the math involved in these kinds of things, please let me know!
The code examples in this article will be in Javascript. If you use a framework like P5js or D3, you probably already have a proper vector class available. But if you don’t, let’s do this really quickly. A (2D) vector is just an object that holds an x and a y-coordinate:
const vec2 = (x, y) => ({'x': x, 'y': y});
The only thing we’ll be doing to vectors today is adding them. So let’s define a function for that:
const vec2_add = (a, b) => vec2(a.x + b.x, a.y + b.y);
And now we can begin.
Let’s start with a rotating vector. We can define this in Javascript like this:
const circle = (phi, r) => vec2(r * Math.cos(phi), r * Math.sin(phi));
We can turn this into a rotating function that takes a frequency, phase and amplitude. It also takes a final parameter s
, which goes from 0 to 1. It returns a point that traces a circle of radius a
around the origin, f
times. The phase p
shifts s
so that the circle starts somewhere else.
const R = (f, p, a, s) => circle((s * f + p) * TAU, a);
If you draw this function, it looks like this.
Now, what happens if you add two rotating functions together? Let’s try it out over here:
You can edit the formula. Try changing some of the numbers above. Remember that the parameters for R
are frequency, phase and amplitude, followed by s
. So R(-2, 0, 1, s)
means a frequency of -2, phase shift of 0 and an amplitude of 1.
We can notice a few interesting things:
s = 0..1
, you’ll need integer frequencies.We arbitrarily decide that we’re not interested in things that rotate more often than 6 times. So we want two frequencies from {1, 2, 3, 4, 5, 6}, they can’t be the same and the order doesn’t matter. Since it also doesn’t matter which frequency is negative, we’ll decide it’ll always be the second one. This gives us 15 combinations.
You can try out all of these 15 combinations using the buttons below the following figure:
Now some interesting things happen and patterns start to emerge.
First off, we notice that some combinations result in the same figure. For instance 1:2
is the same as 2:4
. If you think about it, 2:4
actually draws the figure twice, both rotations go twice as fast and complete the loop twice. Another way to look at this is by thinking of ratios: the ratio 1/2 is the same value as the ratio 2/4. So just like with ratios, we can simplify 2:4
into 1:2
. The reason why we don’t discard these duplicates just yet, is because they’re not really the same; it draws the figure multiple times. Later when we modulate the parameters, this will become important.
An even more interesting pattern appears when we list the number of the rotational symmetry of the figure in a table. Can you figure out the pattern?
f1 | f2 | sym |
1 | 2 | 3 |
1 | 3 | 4 |
1 | 4 | 5 |
1 | 5 | 6 |
1 | 6 | 7 |
2 | 3 | 5 |
2 | 4 | 3 |
2 | 5 | 7 |
2 | 6 | 4 |
3 | 4 | 7 |
3 | 5 | 8 |
3 | 6 | 3 |
4 | 5 | 9 |
4 | 6 | 5 |
5 | 6 | 11 |
It appears that the symmetry of the figure is equal to the sum of the _simplified_ frequencies. That is, if you interpret them as a ratio, you simplify the ratio, and then you add the numerator and denominator. I think that’s weird. When do you otherwise ever add the numerator and denominator of a fraction?
Yes we can! We can start by defining a very simple oscillator (oscillating thing?) that wobbles around a certain value, deviating by a scaled sine wave:
const O = (f, p, v, d, s) => v + d * Math.sin((s * f + p) * TAU)
Wow, did I say simple? This one takes five parameters, frequency, phase, value, deviation and the final parameter s
.
The fun thing is, as long as you use integer frequencies, this function exactly loops back on itself. That means you can replace any value in our previous formula with an oscillator, and it will still be a smooth loop.
That’s pretty wild! I’ll wait a moment for you to make sense of it and to try out a number of things for yourself.
A few things that I find:
Some weird stuff is going on still and I think my theory isn’t entirely correct. I feel like I’m 90% there but also missing some big fundamental math idea.
2:6
figure has a symmetry of 4, but if you modulate it with an oscillator of frequency 4, it’ll turn into a figure with symmetry 2! This makes sense if you think about it, because it traces the fourfold shape twice.3:6
figure, which has a symmetry of 3, loses this symmetry as soon as you modulate it with a frequency 3 oscillator. My theory is partially bogus under modulation.2:4
figure?I have! Take a look at my recent series Epihyperderpflardioids one, two and three (more coming).
You can see earlier results in my Instagram posts harmonic chains 1 and harmonic chains 2.
I have also used these techniques to build the shapes for Procedural scribbles on sinusoids part I and part II, Squid Yoga and Curly 9000.
You might find it interesting that in my interactive examples, besides the variable s
, which traces around the figure, there is also another variable called t
which counts up with time. You could use this in, say, oscillator functions and get moving shapes. Just saying you might want to go back and check that out …
If you’re not entirely sure how to implement the math discussed in this article, check out this very simple P5js sketch.
If you make anything cool using this technique, or you have questions, or corrections, or a brilliant idea, please don’t hesitate to send me a message!