For the cover of Grafia magazine 3/2010 we created these scripts in Nodebox, a harmonograph and a spirograph. Copy the code into a new Nodebox file and experiment with the sliders to create different figures.

# http://en.wikipedia.org/wiki/Harmonograph
from math import sin, cos, exp, pi, radians
from random import seed
def pendulum(t, a, f, p, d):
return a * sin(t * f + p) * exp(-d * t)
def harmonograph(t, a1, f1, p1, d1, a2, f2, p2, d2,
a3, f3, p3, d3, a4, f4, p4, d4):
x = pendulum(t, a1, f1, p1, d1) + pendulum(t, a2, f2, p2, d2)
y = pendulum(t, a3, f3, p3, d3) + pendulum(t, a4, f4, p4, d4)
return x, y
var("a1", NUMBER, 100, 0, 200)
var("f1", NUMBER, 1.8, 1, 2)
var("f1_tweak", NUMBER, 0, 0, 0.05)
var("p1", NUMBER, 0.6, 0, pi)
var("d1", NUMBER, 0.0015, 0, 0.1)
var("a2", NUMBER, 40, 0, 200)
var("f2", NUMBER, 1.8, 1, 2)
var("f2_tweak", NUMBER, 0, 0, 0.05)
var("p2", NUMBER, 0.6, 0, pi)
var("d2", NUMBER, 0.0015, 0, 0.1)
var("a3", NUMBER, 33, 0, 200)
var("f3", NUMBER, 1.8, 1, 2)
var("f3_tweak", NUMBER, 0, 0, 0.05)
var("p3", NUMBER, 2.1, 0, pi)
var("d3", NUMBER, 0.0015, 0, 0.1)
var("a4", NUMBER, 160, 0, 200)
var("f4", NUMBER, 1.2, 1, 2)
var("f4_tweak", NUMBER, 0, 0, 0.05)
var("p4", NUMBER, 2.3, 0, pi)
var("d4", NUMBER, 0.0015, 0, 0.1)
var("revolutions", NUMBER, 100, 1, 250)
var("highres", BOOLEAN, False)
samples = 100.0 if highres else 20.0
f1 += f1_tweak
f2 += f2_tweak
f3 += f3_tweak
f4 += f4_tweak
stroke(0)
nofill()
strokewidth(0.25)
translate(250, 250)
autoclosepath(False)
beginpath()
for i in range(int(revolutions * 2 * pi * samples)):
x, y = harmonograph(i/samples,
a1, f1, p1, d1, a2, f2, p2, d2,
a3, f3, p3, d3, a4, f4, p4, d4)
if i == 0: moveto(x, y)
else: lineto(x, y)
endpath()

# Spirograph http://en.wikipedia.org/wiki/Spirograph
from math import sin, cos, pi
def spirograph(t, R, r, p):
x = (R - r) * cos(t) + p * cos((R - r) * t / r)
y = (R - r) * sin(t) - p * sin((R - r) * t / r)
return x, y
var("R", NUMBER, 12.0, -25, 25)
var("R_tweak", NUMBER, 0.0, -0.25, 0.25)
var("r", NUMBER, 5.0, -10, 10)
var("r_tweak", NUMBER, 0.0, -0.1, 0.1)
var("p", NUMBER, -2.0, -10, 10)
var("revolutions", NUMBER, 10, 1, 30)
var("highres", BOOLEAN, False)
samples = 250.0 if highres else 25.0
R += R_tweak
r += r_tweak
r = 0.1 if r == 0 else r
stroke(0)
strokewidth(0.25)
nofill()
autoclosepath(False)
translate(250, 250)
beginpath()
for i in range(int(revolutions * 2 * pi * samples)):
x, y = spirograph(i/samples, R, r, p)
x, y = x * 7, y * 7
if i == 0: moveto(x, y)
else: lineto(x, y)
endpath()
Great sript!
thanks a lot!