Tutorial Request: Delay Animation

I'm trying to learn DrawBot (and Python) and I'm having a hard time trying to replicate an animation I saw on Twitter. Here it is: https://tixy.land (it's the first one)
Here is what I wrote so far:
canvasSize = 500 circleCount = 10 circleSize = canvasSize / circleCount offset = circleCount * circleSize frameCount = 24 rowCount = 10 for frame in range(frameCount): newPage(canvasSize, canvasSize) frameDuration(1/frameCount) circleScale = cos(pi * frame / frameCount) translate(canvasSize/2, circleSize/2) for row in range(rowCount): save() for i in range(circleCount + 1): save() translate(i * circleSize) scale(circleScale, center=(circleSize/2  offset/2, 0)) oval(circleSize  offset/2, circleSize/2, circleSize, circleSize) restore() restore() translate(0, circleSize) saveImage("~/Desktop/circleTest.gif")
And this is the output:
I'm not sure how to make the animation for each row start at a different frame so that it has that wave effect. Is it possible to progressively delay the start of each row animation? Or is that not the right approach here?

a lot is happening at the same time
I hope the example is clear enough!
# seperate width and height canvasWidth = 300 rowCount = 10 circleCount = 10 # calculate circleSize based on the width and the amount circles circleSize = canvasWidth / circleCount # must be even frameCount = 24 # calcualte the height based on the circle size and rows canvasHeight = circleSize * rowCount # start loop for frame in range(frameCount1): # calculate a factor, number between 0  1 # going from 0  1  0 frameFactor = frame / (frameCount) * 2 if frameFactor > 1: frameFactor = 2  frameFactor # create a page newPage(canvasWidth, canvasHeight) # draw a white background with savedState(): fill(1) rect(0, 0, canvasWidth, canvasHeight) # translate half of the circle size translate(circleSize/2, circleSize/2) # start a loop for each row for row in range(rowCount): # get the scale base factor, a number between 0  1 circleScale = row / (rowCount  1) # add the frame factor circleScale += frameFactor # normalize the circle size if circleScale > 1: circleScale = 2  circleScale # adding some easein circleScale = circleScale * circleScale # start loop for each column for i in range(circleCount): # save and restore with savedState(): # translate to the x, y of the oval translate(i * circleSize, row * circleSize) # scale scale(circleScale) # draw the oval oval(circleSize/2, circleSize/2, circleSize, circleSize) # save the image saveImage("test.gif")

the resulting animated gif

I found this very interesting so I tried to do it without a lot of success, I ended with something like this:
w, h = 1000, 1000 divs = 10 colSize, rowSize = w//divs, h//divs fps = 12 seconds = 1 duration = 1 / fps totalFrames = seconds * fps def circles(step): for x in range(0, w, colSize): for y in range(0, h, rowSize): d = rowSize * sin(y/(divs/2)+step) if d > 0: fill(1) else: fill(1, 0, 0) oval(x + colSize/2  d/2, y + rowSize/2  d/2 , d, d) for frame in range(totalFrames): newPage(w, h) frameDuration(duration) fill(0) rect(0, 0, w, h) circles(frame) saveImage("~/Desktop/circleTest.gif")
I'm not understanding how the time works in the reference to achieve the smooth variation

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:

cool !

@vitorcarvalho wow, this is lovely!!!