This Python + Pygame program draws the trace of 4 decaying sine waves, 2 per axis, with rainbow colours. It generates a sequence of random harmonographs.

A harmonograph is a mechanical device typically seen in science museums, that has two or more pendulae with attached pens, that can draw on a sheet of paper. The pendulae are set in motion and the pen draws pretty patterns on the paper. This is easily simulated in a computer program, by plotting orthogonal sine waves acting together on a drawing point. This generates Lissajous figures, which are decayed to make a pleasing nesting of ‘parallel’ lines, like you might see on bank notes.

It’s fast, and can be set to go much faster (or slower) if you want. Tip: set the display window to fullscreen. MIT license; download from GitHub

'''    Spectral Harmonographs   Copyright 2014 Alan Richmond (
print("Quit: q key, Screenshot: spacebar")

import pygame, sys, random as r
from pygame.locals import *
from math import pi, sin, cos, exp
#                        EDIT THESE:
width,height=1280,720       # YouTube HD
width,height=1920,1080      # my left monitor
width,height=1280,1024      # my right monitor
width,height=1680,1050      # Lucy's monitor
#width,height=2560,1440      # YT channel art
dd=0.99995                  # decay factor
dt=0.02                     # time increment
speed=200                   # yes, speed
hui=57*2                    # Hue increment
sd=0.005                    # frequency spread (from integer)
mx=4                        # max range for amplitudes and frequencies
print("Hit SPACE to save")

def check_event():
    global save
    for event in pygame.event.get():
        if event.type == QUIT:
        elif event.type == KEYDOWN and event.key == K_q:
        elif event.type == KEYDOWN and event.key == K_SPACE:
            print("Saving when finished...")

def scale(length):
    while True:
        if max > 0: break
    return a1,a2,length/(2*max)
pygame.event.set_allowed([QUIT, KEYDOWN])
screen = pygame.display.set_mode((width,height),DOUBLEBUF)
while True:
#   Amplitudes and scales
    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)
    px1, px2 =  r.uniform(0,2*pi), r.uniform(0,2*pi)
    py1, py2 =  r.uniform(0,2*pi), r.uniform(0,2*pi)

    t=0.0                       # angle for sin
    while dec > 0.015:
                                # calculate next x,y point along line
        x = xscale * dec * (ax1*sin(t * fx1 + px1) + ax2*sin(t * fx2 + px2)) + width/2
        y = yscale * dec * (ay1*sin(t * fy1 + py1) + ay2*sin(t * fy2 + py2)) + height/2
        dec*=dd                 # decay
        if not first:           # ignore any complaint about prev_x,y being undefined
#            fg.hsva=(hue,sat,val,aaa)
#            hue = (hue + dt*hui) % 360      # cycle hue
            pygame.draw.aaline(screen, fg, (x, y), (prev_x, prev_y), 1)

        prev_x = x              # save x,y for next line segment start
        prev_y = y
        if steps%speed==0: pygame.display.update()
        t+=dt                   # increment angle for sin

    if save:                    # parameters are encoded into filename
        pars='shg-{0}_{1}-{2}_{3}-{4}_{5}'.format(ax1,ax2,fx1,fx2,px1,px2), pars+'.jpg')
        print("Saved as "+pars+'.jpg')

#    screen.fill((255,255,255))

You may need to install python and/or pygame, e.g. on Ubuntu/Debian style linuxes:

sudo apt-get install pygame
sudo apt-get install python
Go to the directory you saved it in and make it executable, then run it:

chmod +x
Set the display window to fullscreen (right click on title bar, maybe it’s under More Actions?).

[welcomewikilite wikiurl=”” sections=”Short description” settings=””]

3 thoughts on “Spectral Harmonographs”

    1. The wordpress plugin for Python has a habit of changing ‘>’ to ‘&’…
      I’ve changed the code, and another one further down, please try again
      and forgive my late reply. Sorry.

  1. Hi there,

    just stumbled across this page here and wanted to mention something. The code above is working….partially. All lines appear white. However, the code on the Github is missing the parentesis on all print commands, after adding these I was able to get the images as shown here (with colored lines).

    On a first glance I couldn’t find the mistake for the missing color in the code on this page, but I’ll have a closer look soon.

    Best regards and thank you for this beautiful example
    – Chris

Leave a Reply

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