Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Recursion and L-SystemsLesson 9.2
PSD for Architecture, Planning & Urban Design/Module 9 · Generative & Parametric Scripting

Lesson 9.2 · Generative & Parametric Scripting

Recursion and L-Systems

Rules that call themselves - how a few lines of self-similar instruction grow fractals, branching structures and plant-like form

13 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

A fern, a river delta, a lightning bolt, a tree - nature builds staggering complexity by repeating one simple rule at every scale.

Look closely at a fern and each frond is a smaller fern; a branch splits, and each branch splits the same way again. This self-similarity - the same rule applied at every scale - is everywhere in the natural world, and it is astonishingly cheap to describe in code.

Two ideas unlock it. Recursion is a function that calls itself, shrinking the problem each time until it hits a stopping point. L-systems are a rewriting grammar that turns a tiny rule into an elaborate string of instructions you can draw. Together they let a dozen lines grow a tree.

Fern = fern of ferns. Base case stops it. L-system: axiom + rules + turtle.

Recursion - a function that calls itself

A recursive function is one that calls itself on a smaller version of the same problem. It sounds circular, and it would be - an infinite loop - if it were not for the one rule that makes recursion safe: every recursive function must have a base case, a condition where it stops calling itself and simply returns an answer. Without a base case you get a RecursionError; with one, the calls unwind cleanly.

Here is recursion doing something a designer cares about - subdividing a length into equal pieces by halving, depth times:

python
def subdivide(length, depth):
    if depth == 0:              # base case: stop here
        return [length]
    half = length / 2
    return subdivide(half, depth - 1) + subdivide(half, depth - 1)

print(subdivide(4.0, 2))        # [1.0, 1.0, 1.0, 1.0]

Read it as a decision: if we have subdivided enough (depth == 0), stop and return this piece; otherwise, split in half and ask the same function to subdivide each half one level less. Each call reduces depth by one, so it marches toward the base case and terminates. The mental model is a stack of nested tasks: the function keeps opening smaller copies of itself, then closes them from the innermost out. Anything with a naturally nested or self-similar structure - subdivided grids, tree menus, folder trees, branching geometry - is often clearest expressed this way.

If that feels slippery at first, you are in good company - recursion is the concept that most reliably makes newcomers pause, because the function seems to use itself before it is finished. The trick is to trust the recursion: assume the inner call already does its job correctly for a smaller input, and you only have to get two things right. First, the base case - what to return when the problem is as small as it gets. Second, the recursive step - how to build the answer for a bigger problem out of the answer for a smaller one. Get those two pieces right and the whole tower of calls takes care of itself; you never have to trace every level in your head, which is a relief, because for anything interesting there are far too many levels to hold at once.

RECURSION NEEDS A BASE CASEsubdivide(length, depth)depth == 0 ?the base caseyes: stopreturn [length]no: call itself twiceon two halves, depth - 1yesnoeach callre-enters the top
Zoom
Every recursive function is this shape: it checks a base case, and if it is not met, it calls itself on a smaller problem - here, subdividing each half one depth less. Each call re-enters the top with a smaller argument, marching toward the base case so the recursion always ends.

Every recursion needs a base case. No base case = infinite loop = crash.

Recursion versus a loop - when to reach for it

You can often write the same thing with a loop or with recursion, and for flat, linear work a loop is usually simpler and faster. Recursion earns its place when the structure is nested - when each piece contains smaller pieces of the same kind. A classic is a fractal rule like the Koch curve, where each line segment is replaced by four smaller segments, and each of those by four more:

python
def koch(length, depth):
    if depth == 0:
        return [("F", length)]     # F = draw forward
    third = length / 3
    seg = koch(third, depth - 1)
    return seg + [("L", 60)] + seg + [("R", 120)] + seg + [("L", 60)] + seg

print(len(koch(1.0, 2)))            # grows fast: 4 segments per level

Each level multiplies detail by four - two levels give 16 draw steps, five levels give over a thousand, all from one rule. That explosive growth is the point and the danger: recursion generates enormous complexity from tiny descriptions, but go too deep and you overwhelm memory or the recursion limit. Start shallow (depth 2 or 3), see the result, then increase depth deliberately. The honest guideline: use a loop for repetition, use recursion for self-similarity - and always keep the base case in sight.

Loop = repetition. Recursion = self-similarity (pieces contain smaller pieces).

L-systems - a grammar that grows

An L-system (Lindenmayer system, named after the biologist who devised it to model plant growth) is a beautifully simple idea. You start with a short string called the axiom, and you have rules that say how to rewrite each symbol. On every generation, you replace every symbol at once according to the rules, and the string gets longer and more structured. It is recursion expressed as text rewriting.

python
def grow(axiom, rules, generations):
    s = axiom
    for _ in range(generations):
        s = "".join(rules.get(ch, ch) for ch in s)
    return s

rules = {"F": "F[+F]F[-F]F"}
print(grow("F", rules, 2))

That grow function is the whole engine: for each generation, every character is looked up in rules (and left unchanged if it has no rule, via rules.get(ch, ch)). The symbols are a little turtle-graphics language: F means draw forward, + and - mean turn right and left by a fixed angle, and [ and ] mean 'remember where I am' and 'jump back there' - which is exactly how you make a branch that returns to the trunk. One rule, applied twice, already produces a bushy instruction string; applied five times it describes a whole plant.

What makes this so powerful is that the rule and the drawing are cleanly separated. The grow function knows nothing about geometry - it just rewrites text - and the turtle knows nothing about biology - it just follows symbols. That separation means you can change the look (the turn angle, the step length, the drawing tool) without touching the grammar, and change the grammar without touching the drawing. It is the same discipline good scripts always reward: small pieces that each do one thing, snapped together. Historically this is why L-systems succeeded as models of real botany - Lindenmayer could capture the growth rule of an actual plant species in a compact grammar, then let the interpreter render it - and it is why they remain such a satisfying introduction to generative form: the entire creative space lives in a handful of characters you can edit and immediately re-grow.

AN L-SYSTEM GROWS A PLANTrule: F -> F[+F]F[-F]FF = draw forward [ ] = branch off and return + - = turngen 0gen 1gen 2same tiny rule, applied again and again - simple rules, complex form
Zoom
An L-system rewrites the axiom 'F' by the rule F -> F[+F]F[-F]F once per generation. The same tiny rule, applied again and again, grows an ever more branched structure - the essence of generating complex, plant-like form from a simple, self-similar instruction.

Axiom + rules + generations. [ ] = branch and return. + - = turn.

From string to drawing - interpreting the turtle

An L-system string is only instructions; to see form you feed it to a turtle - an imaginary pen with a position and a heading that reads the string one symbol at a time. F moves the pen forward drawing a line, +/- rotate the heading, and [/] push and pop the state onto a stack so branches spring off and snap back. Here is the interpreter producing line segments as coordinate pairs:

python
import math

def draw(s, step=1.0, angle=25):
    x, y, a = 0.0, 0.0, 90.0     # start pointing up
    stack, lines = [], []
    for ch in s:
        if ch == "F":
            nx = x + step * math.cos(math.radians(a))
            ny = y + step * math.sin(math.radians(a))
            lines.append(((x, y), (nx, ny)))
            x, y = nx, ny
        elif ch == "+": a -= angle
        elif ch == "-": a += angle
        elif ch == "[": stack.append((x, y, a))
        elif ch == "]": x, y, a = stack.pop()
    return lines

print(len(draw(grow("F", {"F": "F[+F]F[-F]F"}, 3))))

Those ((x, y), (nx, ny)) pairs are geometry you can hand to any drawing tool - matplotlib for a sketch, or Rhino/Grasshopper to build real curves (Module 6). The three knobs - the rule, the turn angle, and the number of generations - each change the plant's character completely: a small angle gives a tall narrow tree, a wide angle a spreading bush. That is the joy of L-systems for a designer: a handful of parameters, an entire family of organic forms.

You can push the grammar further. Add a stochastic twist by choosing between two rules at random for each F (a stochastic L-system), and every plant grows a little differently while keeping the same species - which is exactly where this lesson meets the randomness of the previous one, seed and all. Or make the turn angle itself a parameter that drifts with a noise function, and the tree leans and sways. The point is that L-systems are not a fixed trick; they are a small, extensible language for growth that you can bend toward whatever organic system you are modelling.

Turtle reads the string: F draws, +/- turn, [ ] branch. Out come line segments.

Where recursion shows up beyond plants

It would be a mistake to file recursion away as 'the L-system thing'. Self-similar structure is everywhere in design and design software, and once you recognise it you will reach for recursion naturally. Subdividing a plan or facade is recursive - split a rectangle, then split each half the same way, stopping when the pieces are small enough - and this is the idea behind quadtree and binary space partitioning layouts used for automatic room division and responsive grids. Space-filling curves, fractal ornament, and tree diagrams of every kind - a spanning structure, an org chart, a decision tree - are all recursive in shape.

Recursion is also the honest way to walk anything nested. A folder that contains folders that contain folders is a tree, and a recursive function that processes a folder and then calls itself on each sub-folder is the clean way to, say, gather every drawing in a deep project directory (a task Module 8 returns to). The same applies to nested data - a JSON structure with lists inside dictionaries inside lists - where a recursive walker handles arbitrary depth that a fixed set of loops cannot.

The judgement call is always the same. Reach for recursion when the structure genuinely nests and each part contains smaller parts of its own kind; stick with a plain loop when the work is flat and linear. And whichever you choose, keep the two safety habits in view: write the base case first, and make sure every recursive call moves measurably toward it. Recursion rewards you with descriptions that match the structure of the problem - which is why, for branching, subdivision and nesting, it so often reads more clearly than the loop-and-stack alternative.

Subdivision, quadtrees, folder trees, nested JSON - all naturally recursive.

Concepts & tools in this lesson

recursion

A function that calls itself on a smaller problem

The natural fit for nested, self-similar structure. Every recursive function needs a base case or it never stops.

base case

The condition where recursion stops and returns

Write it first. Without it you get infinite recursion and a RecursionError; with it, the calls unwind cleanly.

L-system

A symbol-rewriting grammar for growth patterns

Axiom + rules + generations. Devised to model plants; the engine is a few lines, the results are elaborate.

turtle interpretation

Turning the symbol string into geometry

A pen with position and heading reads F/+/-/[/] and emits line segments you can draw or send to Rhino/Grasshopper.

Hands-on workshop

Workshop - grow a plant with an L-system

Build the two-part engine - a `grow` function that rewrites the string and a `draw` turtle that turns it into line segments - then tune the parameters to get different plants. Uses only the standard library (plus matplotlib if you want to see it).

Python 3 (math is built in). Optional: matplotlib to visualise the segments.

Given & goal
Goal: a parametric plant generator you can tune
Inputs: Python 3, the math module (matplotlib optional to view)
Time: ~45 minutes
  1. 1Copy the grow(axiom, rules, generations) function. Test it with rules = {"F": "F[+F]F[-F]F"} on axiom "F" for 1, 2 and 3 generations, printing the length of each string so you feel it grow.
  2. 2Copy the draw turtle interpreter. Run it on the generation-3 string and print how many line segments it produced - that is your plant, as geometry.
  3. 3Change the turn angle from 25 to 15, then to 40, regenerating each time. Note in a sentence how the plant's shape changes (narrow and tall versus wide and spreading).
  4. 4Invent a second rule set - try {"F": "FF-[-F+F+F]+[+F-F-F]"} - and compare the character of the plant it grows against the first.
  5. 5Bonus: plot the segments with matplotlib (plt.plot over each ((x,y),(nx,ny)) pair) to actually see the tree, and try depth 4 - notice how fast the segment count climbs.

You’ll walk away with
A working two-function L-system (grow + draw) that prints a segment count for a generation-3 plant, plus a short note on how changing the angle and the rule changed the form - and, if you did the bonus, an image of the tree.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Recursion and L-systems are the mathematics behind branching and fractal architecture - tree-column canopies, dendritic structural nets, subdivided facades and self-similar screens. Because a whole family of forms lives behind three parameters (rule, angle, depth), you can generate and compare dozens of branching options in minutes, then take the coordinate output straight into Rhino or Grasshopper to develop it as buildable geometry.

For the interior designerScripts for data, schedules & layouts

Self-similar rules generate the intricate, hand-crafted-looking patterns that flat repeats never quite achieve - branching partition screens, fractal-inspired ceiling reliefs, generative jaali and lattice work, or a growing motif across a feature wall. An L-system gives you an ornament that feels organic and one-of-a-kind, yet is fully parametric: nudge the angle and the entire pattern re-grows to fit the panel.

For the studentA hireable computational skill

L-systems are one of the most rewarding first generative projects - a small, self-contained engine that produces visibly beautiful results and teaches recursion for real. The turtle interpreter here is a complete, portable piece of code you can extend, and the ideas lead straight into the Computational Design course. Build a plant grower and you will understand recursion far better than any abstract example could teach.

Misconception check

Recursion is an advanced, dangerous technique that beginners should avoid.

Recursion is a normal tool, not a rite of passage, and it is genuinely the clearest way to express anything with a nested, self-similar structure - subdivided geometry, trees, branching form. The 'danger' people mean is real but small and easily managed: forget the base case and it never stops, or recurse too deep and Python raises a RecursionError. Both are avoided by the same discipline - always write the stopping condition first, and start with a shallow depth you can see before increasing it. You do not need recursion for flat, linear repetition (a loop is simpler there), but for self-similar form it is often the most honest description of what you actually mean.
Try it

Do it yourself

Trace the rules by hand.

  1. 1Why must every recursive function have a base case, and what happens without one?
  2. 2Rewrite subdivide(4.0, 2) in your head - how many pieces come out, and how long is each?
  3. 3In an L-system, what do the symbols [ and ] represent, and why are they essential for a branch?
  4. 4Applying the rule F -> FF to the axiom F three times gives what string?
  5. 5You want a taller, narrower tree from the same rule - which parameter do you change, and which way?
Take this with you

The one line to carry out

Recursion and L-systems build elaborate, self-similar form from tiny rules: a function that calls itself with a base case, and a grammar that rewrites a string you then draw with a turtle. Simple instructions, at every scale, add up to complexity.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01Recursion (computer science)Wikipedia, 2026.
  2. 02L-systemWikipedia, 2026.
  3. 03SubroutineWikipedia, 2026.
  4. 04Procedural generationWikipedia, 2026.
Related lessons
Recap
Recursion is a function that calls itself on a smaller problem, made safe by a base case that stops it; it is the natural way to describe nested, self-similar structure like fractals and branching. An L-system is a rewriting grammar - axiom, rules, generations - that grows a string of symbols, which a turtle interpreter (F draws, +/- turn, [/] branch) converts into drawable geometry. A handful of parameters yields whole families of organic form.
Carry forward →

Recursion grows form from a self-calling rule; next we widen the lens to the broader toolbox of generative algorithms - grids and tilings, attractors, cellular automata and packing - the ways a rule becomes form, bridging into the Computational Design course.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →