Online 3D Harmonograph

This is an online 3d harmonograph version of those harmonograph devices often seen in science museums – except that instead of drawing pretty harmonic patterns on paper, here we plot them in 3D-space! Glowscript code and my other efforts are available at Mandrian‘s GlowScript ProgramsVpython code is available at Github. MIT License. My original Vpython code and further information available here.

  • Click on Run! to draw the next harmonograph.
  • Rotate by dragging the mouse with the right button held down.
  • Zoom in or out with the scroll wheel or by holding down both mouse buttons as you drag.
  • No pan yet, hopefully later.
  • Tip: zoom in very close, rotate the harmonograph…

A 3D Harmonograph in Python & Vpython

I posted this on GitHub some time ago, and forgot to post about it here. So here it is now. The idea is simple enough: harmonographs are just graphical representations of the sums of the motions of 2 or more pendulums on the 2D plane, such as a piece of paper, just as you see on those devices in all good science museums. We can program this by treating their motions as harmonic – i.e. sine waves, with damping due to friction. However, there’s no reason not to extend the pendulums’ motions to 3D. My previous harmonograph used PyGame for the graphics, but AFAIK there’s no builtin 3D.

So for this exercise I switched to Vpython. VPython is the Python programming language plus a 3D graphics module called “visual” originated by David Scherer in 2000. This was fairly straightforward but for one oddity: as the trace grew, it started to become ‘angular’ or jagged at the outer boundary. The ugliness was a deal-breaker. After a lot of Googling I discovered from the documentation that ‘trail’ would only hold up to 1000 points, after which it silently dropped points regularly along its length (in order to keep the drawing process smooth). So then I tried just starting a new trail after 1000 points, following on from the current trail. It worked!

Just like my previous harmonograph, this one selects some random parameters, with the frequencies close to integer multiples. You can rotate the graph in 3D by holding down the right mouse button while rotating the trackball – on my mouse, you may have to experiment… the thing rotates anyway. As usual, you can get get it from GitHub, here, or just copy & paste the following code (MIT licence):

'''    3D Spectral Harmonographs   Copyright 2014 Alan Richmond (

    Uses Vpython. Hold down right mouse button and move mouse or trackball to rotate.
    Press any key for next harmonograph (e.g. space bar).
    MIT License.
from visual import *
from math import sin
import random as r

width,height=1280,720       # YouTube HD
#width,height=1920,1080      # my left monitor
#width,height=1280,1024      # my right monitor
#depth=height                # 3d
hui=.159                    # hue increment
dec=0.99998                 # decay factor
dt=0.01                     # time increment
mx=4                        # amplitude & frequency ranges (-/+)
sd=0.002                    # standard deviations (frequency spread)
#   Amplitudes & scales
def scale(length):
    while True:
        if max>0: break
    return a1,a2,length/(2*max)

d=display(title='3D Spectral Harmonograph',width=width,height=height)
while True:                             # Main loop
    #   Amplitudes & scales
    #   Frequencies
    fx1, fx2 =  r.randint(1,mx) + r.gauss(0,sd), r.randint(1,mx) + r.gauss(0,sd)
    fy1, fy2 =  r.randint(1,mx) + r.gauss(0,sd), r.randint(1,mx) + r.gauss(0,sd)
    fz1, fz2 =  r.randint(1,mx) + r.gauss(0,sd), r.randint(1,mx) + r.gauss(0,sd)
    #   Phases
    px1, px2 =  r.uniform(0,2*pi), r.uniform(0,2*pi)
    py1, py2 =  r.uniform(0,2*pi), r.uniform(0,2*pi)
    pz1, pz2 =  r.uniform(0,2*pi), r.uniform(0,2*pi)
    print   ax1,ax2,ay1,ay2
    print   fx1,fx2,fy1,fy2
    print   px1,px2,py1,py2
    #   Note that there are 2 nested loops here, where 1 should suffice BUT curve() only takes
    #   1000 points before dropping some. See bottom of
    #   My solution is to start a new trail every 1000 points.
    for j in range (100):
        if not first: trail=curve(frame=f,pos=(x,y,z),color=color.hsv_to_rgb((hue,1,1)))
        for i in range (1000):
            #   Each pendulum axis is sum of 2 independent frequencies
            x = xscale * k * (ax1*sin(t * fx1 + px1) + ax2*sin(t * fx2 + px2))
            y = yscale * k * (ay1*sin(t * fy1 + py1) + ay2*sin(t * fy2 + py2))
            z = zscale * k * (az1*sin(t * fz1 + pz1) + az2*sin(t * fz2 + pz2))
            hue = (hue + dt*hui) % 360      # cycle hue
#    key = d.kb.getkey() # wait for and get keyboard info
    #   This is clunky - I don't know what I'm doing but it works...
    for obj in d.objects:
        obj.visible = False

A harmonograph is a mechanical apparatus that employs pendulums to create a geometric image. The drawings created typically are Lissajous curves, or related drawings of greater complexity. The devices, which began to appear in the mid-19th century and peaked in popularity in the 1890s, cannot be conclusively attributed to a single person, although Hugh Blackburn, a professor ofmathematics at the University of Glasgow, is commonly believed to be the official inventor.

A simple, so-called “lateral” harmonograph uses two pendulums to control the movement of a pen relative to a drawing surface. One pendulum moves the pen back and forth along one axis and the other pendulum moves the drawing surface back and forth along a perpendicular axis. By varying the frequency and phase of the pendulums relative to one another, different patterns are created. Even a simple harmonograph as described can create ellipses, spirals, figure eights and other Lissajous figures.

More complex harmonographs incorporate three or more pendulums or linked pendulums together (for example hanging one pendulum off another), or involve rotary motion in which one or more pendulums is mounted on gimbals to allow movement in any direction.



Leave a Reply

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