Understanding L-Systems

Written by Ben Wendt

I’ve been playing around with processing a lot over the holidays. I’ve been watching some of Daniel Shiffman’s coding challenge videos, including the intro to L-Systems, and decided to try it out.

An L-System is a set of rules for rewriting a string. This generally creates recursive patterns in the generated strings, and in all the examples I’ve worked on, these are used to build turle graphics programs.

In turle graphics, you are programming the actions of a turtle who can move forward, turn right or left by a given amount, and you can also remember positions that it has visited. Let’s take a second to talk about the turle instructions I am using here:

  1. The turtle starts at the bottom center, facing up.
  2. F means move forward one unit.
  3. + means turn right by pi/6.
  4. - means turn left by pi/6.
  5. [ means to push the current state (position and direction) to a stack.
  6. ] means to pop the top state off the stack, and use that.
  7. All other text in the commands will be ignored by the turtle.

In an L-System, you start with a seed string, known as an axiom. Rules are iteratively applied to the axiom to generate increasingly complex strings. For example, you can start with the axiom X and the rules:

  1. X -> F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]
  2. F -> FF

Applying these rules iteratively will generate the following:

1 Iteration:

F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]

There is only one F in the instructions, so we just get a line straight up. The turle is told to do lots of turning and pushing and popping of positions, but none of those change the images. And the invalid X commands are ignored.

1 iteration

2 Iterations:

FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]

Here we start with a double F, (because rule 2 doubled the previous F, making the stem longer), but this time, we get lots of F commands applied within the push-pop states, and these created the branches at the ends of the stem. There are even more ignored X commands here. 2 iteration

3 Iterations:

FFFF++++[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]-[FF++++[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]-[F++++[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]-[X]]]

3 iteration

This is similar to step two, but now we have an extra layer of branches.

Beyond this I don’t want to paste in the output because it’s excessively long. That’s fine for the turtle to interpret, but for understanding the instructions you should be able to get the idea with the second iteration. Here are some pictures of systems with more iterations:

4 Iterations:

4 iteration

5 Iterations:

5 iteration

6 Iterations:

6 iteration

Beyond this I am not going show because there are so many overlapping lines that the images stop looking interesting.

Now let’s take a second to look at what the L-System’s rules are saying. I specifically aimed to make this look like a dandelion. So I started with a single X, which will let rule 1 run. After rule one, this is only 1 F, (moving the turtle forward one unit), to make the stem. All the turning doesn’t move the drawing pixels, and the turle just ignores the X. Subsequent iterations will use rule 2 to double the length of any existing lines, and rule 1 will add the equally spaced branches at the tips of each line.

Using L-Systems you can make much more realistic and useful models of plants than I have done here. It’s also possible to extend this technique to make three dimensional images. It’s a fun and easy way of making fractal imagery, and it’s very easy to work with and super easy to understand.