Turtle - Bio

The turtle module provides a visual and intuitive way to simulate biological structures like atoms, molecules and proteins.

Molecular Geometry

The Water Molecule (H2O)

Draw a classic Mickey Mouse-shaped water molecule.

  • Create a turtle to represent the drawing pen.
  • Draw a large red circle to represent the Oxygen atom.
  • Lift the pen, move to the center of the Oxygen atom, and set the turtle’s heading.
  • Draw a thick gray line (the chemical bond) outward, then draw a smaller white (or light gray) circle for the first Hydrogen atom.
  • Return to the center of the Oxygen atom. Turn the turtle exactly 104.5 degrees (the bond angle of water).
  • Draw the second bond and the second Hydrogen atom.

Possible solution, draw Water Molecule (H2O):

# Simple Mickey Mouse Water Molecule (H2O)
import turtle
t = turtle.Turtle()
t.speed(3)
# -------------------------
# Function to draw a filled circle (atom)
# -------------------------
def draw_atom(radius, fill_color):
t.color("black", fill_color)
t.begin_fill()
t.circle(radius)
t.end_fill()
# -------------------------
# Function to draw a bond + hydrogen
# -------------------------
def draw_hydrogen(angle):
t.setheading(angle)
t.pendown()
t.pensize(5)
t.color("gray")
t.forward(100)
draw_atom(25, "white")
t.penup()
t.goto(0, 0)
# -------------------------
# Draw Oxygen
# -------------------------
t.penup()
t.goto(0, -60)
t.pendown()
draw_atom(60, "red")
t.penup()
t.goto(0, 0)
# -------------------------
# Draw Hydrogens
# -------------------------
draw_hydrogen(0)
draw_hydrogen(104.5)
turtle.done()

Biological Dynamics

Brownian Motion (Random Walk)

Simulate the erratic, random movement of a microscopic particle suspended in a fluid.

  • Import the random module alongside turtle.
  • Create a small, colored dot (the turtle) representing a protein or molecule.
  • Write a while loop that repeats 500 times.
  • Inside the loop, have the turtle turn a random angle (between 0 and 360 degrees) and move forward a random small distance (e.g., between 1 and 10 pixels).
  • Leave the turtle’s pen down so it draws a trail of its path.
  • Add an “invisible boundary” (like a cell membrane). If the turtle’s x or y coordinates go beyond a certain limit, force it to bounce back into the center.

Cellular Viral Infection Dynamics

Simulate a simple viral infection spreading through a cluster of cells.

  • Create a list of 20 turtles. Scatter them randomly across the screen and stamp them as green circles. These are healthy cells.
  • Create one red turtle (the virus/infected cell) and place it in the center.
  • Create an animation loop where all turtles move randomly (using a mini random walk).
  • The Dynamic Rule: Check the distance between the red turtle and the green turtles. If a green turtle gets within a specific distance (collision radius) of a red turtle, change the green turtle’s color to red.
  • Watch as the “infection” dynamically spreads through the moving population!

You will need to use turtle.distance() to calculate the proximity between the agents on the screen.

Possible solution

import turtle
import random
wn = turtle.Screen()
wn.bgcolor("black")
wn.title("Simulació de propagació viral")
wn.setup(width=600, height=600)
# --- Create green cells ---
cells = []
for _ in range(20):
cell = turtle.Turtle()
cell.shape("circle")
cell.color("green")
cell.penup()
cell.goto(random.randint(-250, 250), random.randint(-250, 250))
cells.append(cell)
# --- Create red cell (the virus) on center ---
virus = turtle.Turtle()
virus.shape("circle")
virus.color("red")
virus.penup()
virus.goto(0, 0)
# --- Custom params ---
collision_radius = 20
move_distance = 10
# --- Main program ---
while True:
for cell in cells:
# Move green cells randomly
dx = random.randint(-move_distance, move_distance)
dy = random.randint(-move_distance, move_distance)
cell.goto(cell.xcor() + dx, cell.ycor() + dy)
# Check infection
if cell.color()[0] == "green" and cell.distance(virus) < collision_radius:
cell.color("red")

Protein Structures

Primary Structure (The Polypeptide Chain)

Draw a “beads on a string” model of a polypeptide chain using different colors to represent different types of amino acids (e.g., hydrophobic vs. hydrophilic).

  • Create a list of colors in Python (e.g., ["red", "blue", "green", "blue", "yellow", "red"]). Each color represents a specific amino acid.
  • Write a for loop that iterates through your list of colors.
  • For each color, change the turtle’s fill color, draw a filled circle (the amino acid bead), and then move the turtle forward.
  • Keep the pen down as you move forward to draw a black line connecting the beads—this represents the peptide bond.

First solution:

import turtle
screen = turtle.Screen()
screen.bgcolor("white")
screen.title("Primary Structure - Beads on a String")
t = turtle.Turtle()
t.speed(2)
t.pensize(2)
# --------------------------
# Sample AA (AminoAcids)
# hydrophobic = blue, hydrophilic = red, special = green
# --------------------------
amino_acids_colors = ["red", "blue", "green", "yellow", "blue", "red", "green"]
# --------------------------
# Draw polypeptide chain.
# --------------------------
t.penup()
t.goto(-250, 0) # començar a l'esquerra
t.pendown()
for color in amino_acids_colors:
t.fillcolor(color)
t.begin_fill()
t.circle(20) # bead size
t.end_fill()
t.forward(50) # go to next bead
turtle.done()

Improved version, based on Real AA sequence. B chain of Human Hemoglobin. 17 AA.

import turtle
# --------------------------
# Classe per a un aminoàcid
# --------------------------
class AminoAcid:
def __init__(self, name, color):
self.name = name
self.color = color
self.position = (0, 0)
# --------------------------
# Real AA sequence. B chain: Human Hemoglobin. 17 beads.
# --------------------------
# Format: (name, color)
amino_acids = [
AminoAcid("Phe", "red"),
AminoAcid("Val", "orange"),
AminoAcid("Asn", "yellow"),
AminoAcid("Gln", "green"),
AminoAcid("His", "cyan"),
AminoAcid("Leu", "blue"),
AminoAcid("Cys", "purple"),
AminoAcid("Gly", "magenta"),
AminoAcid("Ser", "lime"),
AminoAcid("His", "cyan"),
AminoAcid("Leu", "blue"),
AminoAcid("Val", "orange"),
AminoAcid("Glu", "salmon"),
AminoAcid("Ala", "brown"),
AminoAcid("Leu", "blue"),
AminoAcid("Tyr", "plum"),
AminoAcid("Leu", "blue")
]
screen = turtle.Screen()
screen.bgcolor("white")
screen.title("Protein Visualization — Insulina Humana (cadena B)")
t = turtle.Turtle()
t.speed(2)
t.pensize(2)
# --------------------------
# Draw AA chain
# --------------------------
t.penup()
t.goto(-300, 0)
t.pendown()
for aa in amino_acids:
# Draw bead
t.fillcolor(aa.color)
t.begin_fill()
t.circle(15)
t.end_fill()
# Save text position
aa.position = t.position()
# Draw lines.
# t.penup()
t.forward(50)
# t.pendown()
# --------------------------
# Write aminoacid name.
# --------------------------
for aa in amino_acids:
t.penup()
x, y = aa.position
t.goto(x, y + 30)
t.write(aa.name, align="center", font=("Arial", 12, "normal"))
t.hideturtle()
screen.mainloop()

Secondary Structure (Alpha Helix & Beta Sheet)

Write two separate functions to draw the two most common secondary structures side-by-side: a coiled alpha-helix and a zig-zagging beta-pleated sheet.

  • The Alpha Helix: Write a loop that moves the turtle slightly forward and then draws a partial circle (using turtle.circle(radius, extent)). Repeating this slightly overlapping pattern creates a 2D illusion of a 3D spring or coil.
  • The Beta Sheet: Lift the pen and move to a new area of the screen. Write a loop that uses sharp, alternating angles (e.g., turn left 60 degrees, move forward, turn right 120 degrees, move forward, turn left 60 degrees). Draw multiple parallel zig-zag lines to represent the “pleated” sheet.
  • Draw small, dashed lines connecting the parallel strands of your beta sheet to represent the hydrogen bonds holding them together.

Repte: Dibuixar els aminoàcids i cadenes d’una proteïna d’un fitxer PDB.

Com recordaràs a https://xtec.dev/bio/protein/pdb podem descarregar-nos fitxers de proteïnes del Protein Data Bank i netejar-los eliminant les linies que no ens interessen, i quedant-nos únicament amb les que defineixen la seva estructura.

Una de les possibles formes de descarregar-se una proteïna i deixar-la neta (sense aigues i altres residus) és aquesta:

import httpx
pdb_id = "1A4W"
url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
response = httpx.get(url)
response.raise_for_status()
with open(f"{pdb_id}.pdb", "w") as f:
f.write(response.text)
print("PDB descarregat.")
with open(f"{pdb_id}.pdb") as infile, open(f"{pdb_id}_clean.pdb", "w") as outfile:
for line in infile:
if line.startswith("ATOM"): # només àtoms de la proteïna
outfile.write(line)
print("Proteïna neta (sense aigües ni HETATM).")

També pots usar la eina Prody (que encara no hem vist amb profunditat):

Llavors, a aquest codi cal afegir la forma de seleccionar la informació que ens interessa per a dibuixar-la amb Turtle.

El resultat esperat ha de ser semblant a aquest:

En el projecte de gitlab associat, dins del directori pdbeads, podràs analitzar una versió inicial.


Advanced Biological Dynamics

Enzyme-Substrate Complex (The Lock and Key)

Animate a small molecule (the substrate) randomly floating around until it successfully “docks” into the specific shape of a larger molecule (the enzyme).

  • Create a large, stationary turtle and use turtle.shape() to give it a custom shape, or draw a “Pac-Man” like figure with an open mouth. This open mouth is the active site.
  • Create a smaller turtle (the substrate) that perfectly fits into that active site.
  • Set the smaller turtle on a random walk loop (like in Exercise 3).
  • The Dynamic Rule: Inside the loop, check the substrate’s coordinates. If it enters the exact coordinates of the enzyme’s active site, stop the random walk.
  • Snap the substrate into the center of the active site, pause for 1 second (using time.sleep()), change the substrate’s color (showing it has been catalyzed into a “product”), and have it bounce away.

Osmosis across a Semi-Permeable Membrane

Simulate water molecules moving to equalize concentration, while larger solute molecules remain trapped on one side.

  • Draw a dashed vertical line down the exact center of the screen. This is your semi-permeable cell membrane.
  • Create two lists of turtles. Make 30 small blue dots (water) and 10 large green dots (sugar/solute).
  • Start the simulation with 15 water dots on the left side, and 15 water dots plus all 10 green dots on the right side.
  • Animate all dots with a random walk.
  • The Dynamic Rule: When a blue dot hits the dashed line’s x-coordinate, let it pass through. When a green dot hits the dashed line, force it to reverse its heading (bounce off), keeping it trapped on the right side.
  • Observe over time: Because water moves freely, more water will eventually accumulate on the right side, simulating osmotic pressure!

To manage multiple moving turtles simultaneously without the animation lagging, turn off the automatic screen updates using turtle.tracer(0) and manually update the screen at the end of each loop iteration using turtle.update().


Cellular behavior

Chemotaxis is the movement of a cell in response to a chemical stimulus, and Phagocytosis is the act of a cell engulfing a large particle (like a white blood cell eating a bacterium).

The Macrophage Chase

Program a large white blood cell (the macrophage) to actively hunt down a small, erratically moving bacterium.

  • Create a small, red turtle representing the Bacterium. Set it on a continuous, erratic random walk loop (moving fast, changing directions often).
  • Create a large, white turtle representing the Macrophage.
  • The Dynamic Tracking Rule: Inside the animation loop, have the Macrophage constantly check the (x, y) coordinates of the Bacterium.
  • Use macrophage.setheading(macrophage.towards(bacterium)) so the white blood cell always points toward its prey.
  • Have the Macrophage move forward at a slightly slower speed than the Bacterium. Because the Bacterium moves randomly and the Macrophage moves directly, the Macrophage will eventually catch it.
  • The Phagocytosis Rule: When macrophage.distance(bacterium) < 20, hide the Bacterium turtle (using bacterium.hideturtle()) and change the Macrophage’s color to indicate it has “digested” the pathogen.