Thank you frederik and eduairet! It's nice to see your approach. I tried a few more times after submiting the topic and this is what I ended up with:
CANVAS = 500
MARGIN = 60
CIRCLES = 10
CSIZE = (CANVAS / CIRCLES) - (MARGIN / CIRCLES)
FRAMES = 96
ROWS = CIRCLES
for frame in range(FRAMES):
newPage(CANVAS, CANVAS)
fill(0)
rect(0, 0, CANVAS, CANVAS)
frameDuration(1 / 24)
translate(0, CSIZE / 2)
fill(1)
for row in range(ROWS):
for circle in range(CIRCLES):
save()
translate(circle * CSIZE)
scale(sin(pi * frame / FRAMES + circle / CIRCLES), center=(CSIZE / 2 + MARGIN / 2, MARGIN / 2))
oval(MARGIN / 2, -CSIZE / 2 + MARGIN / 2, CSIZE, CSIZE)
restore()
translate(0, CSIZE)
saveImage("~/Desktop/circleTest.gif")
The resulting .gif: