Randomness & Generative Magic

Everything so far has been deterministic: same code, same picture, every time. One import changes that, and it turns your program into a machine that produces a different artwork every time it runs. This is the part of the day I look forward to most.

The random Module

import random

random.randint(1, 10)                       # a random integer from 1 to 10, inclusive
random.choice(["red", "blue", "gold"])      # one random element from a list
random.uniform(0, 360)                      # a random float — handy for angles

Standard library, no installation. Run any of these twice and you get different answers.

Scatter One Shape

The canvas runs from about −400 to +400 on each axis; random.randint(-350, 350) picks a point inside it with a margin. Combine random position, size, sides, and colour:

x = random.randint(-350, 350)
y = random.randint(-350, 350)
t.penup()
t.goto(x, y)
t.pendown()
draw_shape(
    sides=random.randint(3, 8),
    length=random.randint(10, 60),
    colour=random.choice(PALETTE),
)
screen.update()

Shapes that land near the edge get clipped. That's a look, not a bug.

The Generative Loop

Now wrap it in a loop. Before you run it, take a guess: 80 random shapes... ordered? Chaotic? Something else?

for _ in range(80):
    x = random.randint(-350, 350)
    y = random.randint(-350, 350)
    t.penup()
    t.goto(x, y)
    t.pendown()
    draw_shape(
        sides=random.randint(3, 8),
        length=random.randint(10, 60),
        colour=random.choice(PALETTE),
    )

screen.update()

Run it. Run it again. Every run is a different image, because Python made different choices inside the ranges you defined.

This is generative art: you author the rules, and the program produces the image. Notice that the designer didn't disappear. You chose the palette, the size range, the shape vocabulary, the count. Randomness is controlled variation inside boundaries you set.

Seeds: Freezing the One You Love

Random runs are disposable, right up until one is perfect and then gone forever when you re-run. It happens to everyone, and it stings. random.seed() is the cure:

random.seed(23)   # shapes cluster nicely in the top-left

With a seed at the top of your program, the "random" sequence becomes reproducible: same seed, same image, every run. Different seed, different (but again repeatable) image.

So the workflow is: run repeatedly until you find a composition you like, then lock it with a seed. The number itself is just a label for one specific sequence. There's nothing special about 23, or 42.

Stretch Challenges

  • Second layer: after the main loop, add a second loop with a different rule. Larger shapes only, one bold colour, or outline-only.
  • Size-position correlation: make larger shapes cluster near the centre: length = int(60 - abs(x) / 10).
  • Random rotation: t.setheading(random.randint(0, 360)) before each draw_shape call. How does the composition change?
  • Animated version: remove tracer(0, 0), set t.speed(3), and move screen.update() inside the loop so shapes appear one by one. Save it as a separate file so you keep your static version.