L-Systems and Turtle Graphics
A Lindenmayer system rewrites a short string over and over according to a handful of rules, and a "turtle" — a cursor that remembers a position and a heading — turns the result into ferns, trees, dragon curves, and city street networks.
1. What is an L-system?
A Lindenmayer system (L-system) was introduced in 1968 by biologist Aristid Lindenmayer to model the growth of algae and plants cell by cell. Its core idea is deceptively simple: start with one symbol (the axiom), and repeatedly replace every symbol in the string with a longer string according to a fixed set of production rules. After a handful of iterations, a single character has grown into a string thousands of characters long — and if you interpret that string as drawing instructions, you get a recognisable plant, snowflake, or curve.
The magic is that L-systems are context-free (in the simplest variant): the rewrite rule for a symbol doesn't care what surrounds it, yet the aggregate result still captures the self-similar branching you see in real ferns and trees, because biological growth itself is largely a repeated local rule applied at every growing tip.
L-systems are a general-purpose fractal generator, closely related to iterated function systems (IFS). The Koch snowflake and dragon curve — both classic fractals — have equally clean L-system definitions, alongside more organic shapes like Barnsley's fern.
2. String rewriting: axiom and rules
An L-system is defined by three things: an alphabet of symbols, an axiom (the starting string), and a set of production rules mapping each symbol to a replacement string. Take the simplest possible example:
Rules: A → AB, B → A
Applying the rules generation by generation:
Gen 1: AB
Gen 2: ABA
Gen 3: ABAAB
Gen 4: ABAABABA
The string length follows the Fibonacci sequence exactly — a reminder that L-systems, growth patterns, and number sequences are often the same idea wearing different clothes.
3. Turtle graphics: from string to pixels
A string of letters is not a picture yet. Turtle graphics (from Seymour Papert's Logo language, 1967) gives meaning to each character by interpreting the string as a sequence of commands for an imaginary turtle that carries a position (x, y) and a heading angle:
y += sin(heading) · stepLength
drawLine(prevX, prevY, x, y)
Run the turtle through a whole rewritten string, character by character, and the line segments it draws trace out the shape encoded in the grammar.
4. The standard turtle alphabet
| Symbol | Meaning |
|---|---|
F, G |
Move forward by one step, drawing a line |
f |
Move forward by one step without drawing (a "jump") |
+ |
Turn left (counter-clockwise) by the fixed angle δ |
- |
Turn right (clockwise) by the fixed angle δ |
[ |
Push the current state (position + heading) onto a stack |
] |
Pop a state off the stack and make it current |
Only two numbers control the whole visual family: the
step length (how far each F moves)
and the turn angle δ (how sharply +
and - rotate the heading). Changing δ from 20° to 25°
can turn a tidy geometric pattern into a wild organic one.
5. Branching with a stack: [ and ]
Plants branch — a stem splits into two, each of which may split
again. A single turtle can only face one direction at a time, so
branching is modelled with a stack: [
saves the turtle's current position and heading, the turtle draws
a side branch, and ] restores the saved state so the
main stem can continue exactly where it left off.
Rule: F → F[+F]F[-F]F
δ = 25°
After 4–5 generations, this three-line rule produces a recognisably bush-like silhouette — proof that most of the visual complexity of a plant is not encoded explicitly, it emerges from a short rule applied recursively.
6. Classic examples
Koch curve (fractal coastline)
Rule: F → F+F--F+F
δ = 60°
Dragon curve
Rules: X → X+YF+, Y → -FX-Y
δ = 90°
Sierpiński triangle (arrowhead)
Rules: A → B-A-B, B → A+B+A
δ = 60°
Barnsley-style fern
Rules: X → F+[[X]-X]-F[-FX]+X, F → FF
δ = 25°
Real plants aren't perfectly self-similar. A common trick is a stochastic L-system: give a symbol two or more alternative replacement rules and pick between them randomly each time (weighted by probability). This breaks the mechanical regularity and produces far more convincing, nature-like variation between branches.
7. Pseudocode
Generating and drawing an L-system in two clear phases:
// Phase 1: string rewriting
function rewrite(axiom, rules, generations):
s = axiom
for g in range(generations):
s = join(rules[c] or c for c in s)
return s
// Phase 2: turtle interpretation
function drawTurtle(str, stepLength, angleDeg):
x, y, heading = 0, 0, -90 // start pointing up
stack = []
for ch in str:
if ch in "FG":
nx = x + cos(heading) * stepLength
ny = y + sin(heading) * stepLength
drawLine(x, y, nx, ny)
x, y = nx, ny
elif ch == "f":
x += cos(heading) * stepLength
y += sin(heading) * stepLength
elif ch == "+":
heading += angleDeg
elif ch == "-":
heading -= angleDeg
elif ch == "[":
stack.push({x, y, heading})
elif ch == "]":
{ x, y, heading } = stack.pop()
Note the separation of concerns: rewrite knows
nothing about drawing, and drawTurtle knows nothing
about grammar rules. The same turtle interpreter draws a Koch
curve, a fern, or a dragon curve — only the axiom, rules, and
angle change.
🐢 Try the L-system simulation
Pick from 8 presets — Koch curve, dragon curve, Sierpiński arrowhead, Barnsley fern and more — and tune step length, angle and generation count live.
Open simulation →