With a couple of loops and a bit of maths you can create a rotating spiral.

Animating a rotating spiral

Following up on the concepts covered in my previous article “Animations from first principles in 5 minutes” and “More animations from first principles in 5 minutes”, in this article we will create the animation you can see above.

We start by modifying the parametrisation of the circle to create a spiral:

SIDE = 600

def spiral(percentage):
    return (
        SIDE // 2
        * percentage
        * cos(10 * pi * percentage),
        SIDE // 2
        * percentage
        * sin(10 * pi * percentage),
    )

The 10 inside cos/sin dictate how many turns the spiral does, all you have to do is divide that number by 2, so a 10 means we do 5 turns around the centre of the spiral.

You can “easily” put the spiral on the screen:

from itertools import product
from math import sin, cos, pi

import pygame

SIDE = 600
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

screen = pygame.display.set_mode((SIDE, SIDE))
screen.fill(WHITE)

def draw_pixel(screen, x, y, colour):
    x, y = round(x), round(y)
    for dx, dy in product(range(-1, 2), repeat=2):
        screen.set_at((x + dx, y + dy), colour)

def spiral(percentage):
    return (
        SIDE // 2 * percentage * cos(10 * pi * percentage),
        SIDE // 2 * percentage * sin(10 * pi * percentage),
    )

STEPS = 3000
for step in range(STEPS + 1):
    percentage = step / STEPS
    x, y = rotating_spiral(percentage, tick / 10)
    draw_pixel(screen, x, y, BLACK)
pygame.display.flip()
input()

By modifying the function spiral to accept an argument that represents time and by creating an outer loop that emulates ticking of time, we can rotate this spiral:

# ...

def spiral(percentage, time):
    return (
        SIDE // 2 * percentage * cos(10 * pi * percentage + time),
        SIDE // 2 * percentage * sin(10 * pi * percentage + time),
    )

# ...

STEPS = 3000
for tick in count():
    screen.fill(WHITE)
    for step in range(STEPS + 1):
        percentage = step / STEPS
        x, y = rotating_spiral(percentage, tick / 10)
        draw_pixel(screen, x, y, BLACK)
    pygame.display.flip()

To make the spiral expand and contract, we must make it so that the radius has to change as time ticks:

def rotating_spiral(percentage, time):
    return (
        SIDE // 2
        + (1 + sin(time) / 10)  # <-- new
        * percentage * (SIDE // 3) * cos(10 * pi * percentage + time),
        SIDE // 2
        + (1 + sin(time) / 10)  # <-- new
        * percentage * (SIDE // 3) * sin(10 * pi * percentage + time),
    )

Finally, to add colour, we create two functions that generate the background and foreground colours for each frame instead of using the constants WHITE / BLACK:

# ...

def bg(time):
    return (
        40 + int(abs(30 * sin(0.05 * time))),
        40 + int(abs(30 * sin(0.05 * time))),
        40 + int(abs(30 * sin(0.05 * time))),
    )

def fg(time):
    return (
        255 - int(abs(15 * sin(0.1 * time))),
        85 + int(abs(160 * sin(0.1 * time))),
        85 + int(abs(60 * sin(0.1 * time))),
    )

STEPS = 3000
for tick in count(step=0.1):  # <-- smaller tick
    screen.fill(bg(tick))
    clr = fg(tick)
    for step in range(STEPS + 1):
        percentage = step / STEPS
        x, y = rotating_spiral(percentage, tick)
        draw_pixel(screen, x, y, clr)
    pygame.display.flip()

Running the code above should produce the following animation:

Become a better Python 🐍 developer 🚀

+35 chapters. +400 pages. Hundreds of examples. Over 30,000 readers!

My book “Pydon'ts” teaches you how to write elegant, expressive, and Pythonic code, to help you become a better developer. >>> Download it here 🐍🚀.

Previous Post Next Post

Blog Comments powered by Disqus.