Lissajous figures and Harmonographs

Here is something slightly different that I’ve done with Sonic Pi - it’s not so much about the Sonic part of Sonic Pi, but about the visuals.

Note: I have included some pictures taken from a book, to compare with the visuals generated in Sonic Pi. I hope this would count as fair use under copyright law, but if it’s an issue I’d be happy to remove them and replace with them with versions entirely generated in Sonic Pi, without the book figures for comparison.

When Sonic Pi first added the Lissajous scope I had a lot of fun playing around generating Lissajous figures. I thought I had posted about it somewhere at the time, but I can’t find the post now, so maybe I’m misremembering (or maybe it was on another platform before in-thread?). Anyway, the screen recordings I made at the time are still up on Vimeo - here with various different synths and tunings, and here using only sine waves and simple rational frequency ratios. The code for both is on github, here and here respectively.

Some time later, I forget where, I saw somebody recommend a book called Harmonograph: A Visual Guide to the Mathematics of Music by Anthony Ashton. It’s a small book (pocket sized, and only around 60 pages), but it’s beautifully made, and I would also highly recommend it to anyone who is even vaguely interested in the mathematics of music.

Harmonograph

It takes a look at a Victorian device called the Harmonograph, which uses a pen and paper linked to pendulums to draw figures or graphs, and the relation of this device to musical harmony.

There are a lot of figures in the book, and I started wondering whether it would be possible to reproduce them with the Lissajous scope in Sonic Pi.

There are two types of Harmonograph - called lateral and rotary. In a lateral Harmonograph two pendulums, one linked to the pen, and the other to the paper, oscillate perpendicular to each other. This generates essentially the same Lissajous figures that I already mentioned above.
In a rotary harmonograph however, the two linear pendulums both control the pen, while a third rotary pendulum moves the paper in a circular motion. Generally, the two linear pendulums oscillate at the same frequency, therefore generating a circular motion, and this interacts with the circular motion of the paper (possibly at a different frequency and/or amplitude), generating many different and beautiful patterns.

In order to recreate these patterns in Sonic Pi, in theory I should only need two pairs of sine wave synths to generate two circular motions and add them together. However, to get it to work reliably, I found I needed good control over the relative phases of each pair of sine waves, and this was not really feasible (at least as far as I could manage) with separate synths.

So, I decided to create my own synth especially for Lissajous and Harmonograph figures. It essentially consists of two sine waves; one sent to the left, and the other to the right channel.

There are parameters to control the relative frequency, phase and amplitude of each sine wave. I’ve made the source code as well as the compiled synthdef available as a gist here, along with all the code for the figures in this post, in case anyone is interested to try it out (@samaaron I’d also be happy to open a PR if you think this might be something that could be added to Sonic Pi?).

Using a single lisa synth to simulate the two perpendicular pendulums I was able to recreate all the lateral Harmonograph figures. Here you can see some figures from the book with the corresponding Sonic Pi screen captures overlaid:

To recreate the rotary Harmonograph figures, I just had to use two lisa synths at the same time. One slightly confusing thing that took me a bit of trial and error to figure out, was that the figures labelled “inv. amp.” meant that one of the synths had to be half the amplitude of the other (not 180 degrees out of phase, as I initially assumed). Here you can see some (according to the book, these figures originally come from Sir Thomas Bazley’s Index to the Geometric Chuck, 1875, so should be out of copyright by now), again with the screen captures from Sonic Pi overlaid:

All the figures shown up to here had constant amplitude. However, a number of figures showed what you get as you allow the pendulums to decay in amplitude over time, creating more complex spiralling patterns. To recreate these, in theory I’d just have to set the release parameter on the synths. However, it was a bit tricky to get it working in a visually appealing manner. In the end I set the release to something in between 1/10 to 1/20 of a second, and captured a screen recording. The effect was spread over a handful of frames which I merged together afterwards to create the images. Here you can see the results of trying to recreate some of the figures from the book:

I didn’t overlay these ones since they are less precise reproductions of the figures than the previous ones, so they don’t line up quite so well.

There are quite a few more figures in the book that I may try to recreate in the future, but I wanted to post what I have so far, as I thought it was pretty cool, and maybe it might inspire someone else to experiment with this. I’d love to see what other people come up with!

5 Likes

As a math teacher I really appreciate this.

1 Like

I’ve really enjoyed playing with this. Some great work! However, I did have a couple of issues.First a couple of minor typos in the rotary.rb file. The first two examples have the synth called as :lissa instead of :lisa. The second was a bit more puzzling. The ratio: parameter didn’t seem to work as expected. Looking at the synth definition it should multiply the note value, but it didn;t seem to work, the pitch remaining the same. However if I put left-ratio:3,right_ratio: 3 this did do what I expected. So in the rotary file examples I had to use entries for left_ratio: and right_ratio: set to the same value rather than just ratio: which should affect borh left and right. Doing this the examples worked.
eg this worked

uncomment do
  # 5:2 inv. amp. counterc. (p0)
  synth :lisa, left_ratio: 2,right_ratio: 2, right_phase: 0.25, phase: 0.49, amp: 1.2
  synth :lisa, left_ratio: 5,right_ratio: 5, right_phase: -0.25, phase: 0.49, amp: 0.5
end

but this didn’t

uncomment do
  # 5:2 inv. amp. counterc. (p0)
  synth :lisa, ratio: 2, right_phase: 0.25, phase: 0.49, amp: 1.2
  synth :lisa, ratio: 5, right_phase: -0.25, phase: 0.49, amp: 0.5
end

EDIT
I decided to rebuild the synth definition in SuperCollider. I did this it it all then worked as expected. Looks like there may be a problem with the downloaded compiled synth in your gist.

1 Like

Thanks for the feedback!

I had lost the exact code I used for the figures, and had to piece it together from Sonic Pi’s git buffer history, so it’s possible there are more typos or errors in there, but I’ve fixed the synth name in the gist now.

About the ratio option, that is strange, I don’t see why that isn’t working - I’ll have a go at reproducing it myself later and see if I can work out what’s wrong.

EDIT: just saw your edit, I must have also picked up an old version of the synth - I’ll update it later when I get a chance - Thanks!

EDIT2: I’ve updated the synth in the gist, so hopefully it should work now.

I’ve now got the book (thanks for the info) which is very interesting. I’ve also been working on adding a TouchOSC to the rotary program, so that I can change values as it runs. Set the duration very long, and added control loops to both the oscillators with parameters controlled from TouchOSC. Working, but still refining it.

Hi emlyn I get the following error: unknown synth :lisa. Which is the target sytem of the compiled file, I’m running it using Sonic Pi 3.3.1 on Windows 10 64-bit.

Hi @MegamanX
Sorry, there’s one step that I forgot to explain: in the Sonic Pi preferences, in the Audio tab, you have to check “Enable external synths/FX”. Also make sure the compiled file is in a folder that matches the folder referenced by the load_synthdefs command (either put it in “~/.sonic-pi/synthdefs/”, or change the code to point to the correct folder.
Hope that helps!

1 Like

Thanks a lot it works now :slight_smile: !

1 Like

That’s a great idea, I think the way the parameters affect the figure will become much more intuitive if you can change them and see the effects in real time!

Yes it’s really cool. I’v also added the ability to constantly rotate the phase of one of the two synth instances which then rotates the shape. This then brings to life complex shapes if you have two right offsets at different settings to 0.25. I can adjust the right offsets in steps of 0.1 or 0.05 up and down, reset them to 0 and invert them 0.25 → -0.25. I can also do this for the phase setting for each instance, or leave one at 0 and rotate the other increasing or decreasing (which reverses the rotation direction). Finally I can increase or decrease the amplitude of each instance in steps of 0.1 Using these controls you can quickly change shapes, and investigate how they alter.
I may add later a setting record and retrieve, so that you can store and retrieve interesting shapes easily. Here’s a screen shot showing TouchOSC on the left and the Sonic Pi scope on the right. Haven’t named all the controls yet.

2 Likes

This reminds me very much of the Spirograph set I got for my 10th birthday, some time in the mid 60’s.

Eli…

1 Like

Here is a doodle I played with this afternoon. The possibilities are endless.

IT looks much better on a monitor screen. The video has problems with moire patterns.

code

#dancing scope sine waves by Robin Newman, June 2022
# Experiments with Emlyn's lisa synth
#see https://in-thread.sonic-pi.net/t/lissajous-figures-and-harmonographs/6852
pi = Math::PI
define :sin do |x|
  return Math::sin(x)
end
define :cos do |x|
  return Math::cos(x)
end
# Load the synth, set the default params, and disable any overall filters
load_synthdefs ENV['HOME'] + '/.sonic-pi/synthdefs'
use_synth_defaults attack: 0.5, sustain: 5, release: 0.5, amp: 1.5
set_mixer_control! hpf_bypass: 1, lpf_bypass: 1, limiter_bypass: 1, leak_dc_bypass: 1

use_bpm 20 #slow is good, but can be higher

with_fx :ping_pong ,phase: pi/4 do #gives a nice modulation effect on the changes
  m = synth :lisa, sustain: 1000,right_amp: 0,left_amp: 0 #start a long sounding synth
  set :m,m #save pointer to synth
  t=0.2 #set loop sleep time
  set :mute,0
  #set :r1,2;set :r2,1 # set intial ratios just to make sure something is intialised for them
  at [1,35],[1,0] do |v|
    set :mute,v #start visuals after a short pause
  end
  live_loop :tweak do #change ratios
    set :r1,rrand_i(1,6);set :r2,rrand_i(1,6)
    sleep rrand_i(2,4)
  end
  
  live_loop :controlIt,delay: t do # control the synth
    #there are lots of possibilits here. This is just one setup I liked
    control get(:m),left_ratio: get(:r1)+0.01*sin(t*tick%(2*pi)),left_ratio_slide: t,\
      left_amp: 2.5*t%2*pi*cos(t*look/2*pi)*get(:mute),left_amp_slide: t,left_phase: pi/4,\
      right_ratio: get(:r2)+0.01*cos(2*t*look%(2*pi)),right_ratio_slide: t,\
      right_amp: 2.5*t%2*pi*sin(2*t*look/2*pi)*get(:mute),right_amp_slide: t
    puts get(:r1),get(:r2),t
    sleep 2*t
  end
end
2 Likes

Added a second example which I think give a better visual output

code below

#dancing scope sine waves by Robin Newman, June 2022
# Experiements with Emlyn's lisa synth
#see https://in-thread.sonic-pi.net/t/lissajous-figures-and-harmonographs/6852
pi = Math::PI
define :sin do |x|
  return Math::sin(x)
end
define :cos do |x|
  return Math::cos(x)
end
# Load the synth, set the default params, and disable any overall filters
load_synthdefs ENV['HOME'] + '/.sonic-pi/synthdefs'
use_synth_defaults attack: 0.5, sustain: 5, release: 0.5, amp: 1.5
set_mixer_control! hpf_bypass: 1, lpf_bypass: 1, limiter_bypass: 1, leak_dc_bypass: 1
use_random_seed 1468097531
use_bpm 20 #slow is good, but can be higher

with_fx :ping_pong,phase: pi/6, amp: 1.7 do  #gives a nice modulation effect on the changes
  
  m = synth :lisa, sustain: 1000,right_amp: 0,left_amp: 0 #start a long sounding synth
  set :m,m #save pointer to synth
  t=0.2 #set loop sleep time
  set :mute,0
  #set :r1,2;set :r2,1 # set intial ratios just to make sure something is intialised for them
  at [2*t,34],[1,0] do |v|
    set :mute,v #start visuals after a short pause
  end
  
  
  live_loop :controlIt,delay: t do # control the synth
    #there are lots of possibilits here. This is just one setup I liked
    control get(:m),left_ratio: get(:r1)+0.01*sin(t*tick%(2*pi)),left_ratio_slide: t,\
      left_amp: (0.25+2.25*t%2*pi*cos(t*look/2*pi))*get(:mute),left_amp_slide: t,left_phase: pi/4,\
      right_ratio: get(:r2)+0.01*cos(2*t*look%(2*pi)),right_ratio_slide: t,\
      right_amp: (0.25+2.25*t%2*pi*sin(2*t*look/2*pi))*get(:mute),right_amp_slide: t
    puts get(:r1),get(:r2),look
    sleep 1.5*t
  end
  
  live_loop :tweak,sync: :controlIt do #change ratios
    #rlist=(ring [1,3],[2,5],[3,2],[1,2],[5,1],[6,2],[4,6]).tick
    set :r1,rrand_i(1,6);set :r2,rrand_i(1,6)
    #set :r1,rlist[0];set :r2,rlist[1]
    sleep 2
  end
end

See top of this thread for details of the synth and how to set it up

2 Likes