The L-System, or Lindenmayer system, named after it’s creator, Aristid Lindenmayer.
L-systems were introduced in 1968 to describe the behavior of plant cells and model the growth of a variety of organisms.

However, L-Systems are an incredibly versatile tool. In no way are they limited to biology; anywhere requiring parallel state changes can be modeled with L-systems. Variations on the grammar can model an inexhaustible variety of geometric shapes, especially when combined with other notations (Conway’s Polyhedron Notation comes to mind.)

These L-systems work just as Aristid intended in his original grammar, only the reulting string is interpreted as movements forming the outline of a polygon.

To enter a rewrite rule we use the Syntax A>BCD..Z.
The character on the left of the “>”, in this case A, is the character to be replaced.
Everything on the right hand side of “>” are the characters that will replace all instances of the character on the left. Additional rules can be added by placing a comma between it and the proceeding rule.

Example:
A ages and becomes B
B ages into C and gives birth to an extra A
C ages to D and gives birth to an extra A
D dies.
This can be expressed simply as “A>B,B>CA,C>DA,D> ” and within 9 generations a single A will have grown into DABBCABCACADAB; with a space representing every dead cell because it’s white-space sensitive; spaces are characters like any other.

Iterate String With L-System

Start:
Rules:
Iterations:

Output:

Draw L-System Output

Turns
Name
Character
Degrees
Turn 1
Turn 2
Turn 3
Turn 4

Relative Movement
Name
Character
Right-Left
Forward-Back
Forward
Backward
Left
Right

Absolute Movement
Name
Character
Right-Left
Forward-Back
Forward
Backward
Left
Right

JavaScript Code

<!DOCTYPE html>
<style>
.FormRow{width:100%;height:32px;border-bottom:solid 1px #000;}
.FormLeft{width:30%;height:100%;display:inline-block;}
.FormRight{width:69%;height:100%;display:inline-block;}
.FormHalf{width:49%;height:100%;display:inline-block;}
.FormThird{width:32%;height:100%;display:inline-block;}
.FormQuarter{width:24.5%;height:100%;display:inline-block;}
.FormRightHalf{width:34%;height:100%;display:inline-block;}
.fullsize{height:100%;width:100%;}
#LSystemCanvas{width:max(100%,800px);height:max(100%,800px);}

</style>

<script>

demos={"Square1":["FLFLFLFL","F>FLFNFRFMF",2,"L",90,"R",-90,"M",45,"N",-45,"F",0,1,"B",0,-1,"Q",-1,0,"P",1,0,"W",0,1,"S",0,-1,"A",-1,0,"D",1,0],
"Square2":["FLFLFLFL","F>FNFLFRFMF",2,"L",90,"R",-90,"M",45,"N",-45,"F",0,1,"B",0,-1,"Q",-1,0,"P",1,0,"W",0,1,"S",0,-1,"A",-1,0,"D",1,0],
"Square3":["FLFLFLFL","F>FLFFRFRFFLF",3,"L",90,"R",-90,"M",45,"N",-45,"F",0,1,"B",0,-1,"Q",-1,0,"P",1,0,"W",0,1,"S",0,-1,"A",-1,0,"D",1,0],
"Triangle1":["FLFLFL","F>FNFLFRFMF",2,"L",120,"R",-120,"M",60,"N",-60,"F",0,1,"B",0,-1,"Q",-1,0,"P",1,0,"W",0,1,"S",0,-1,"A",-1,0,"D",1,0],
"Triangle2":["FLFLFL","F>FNFLFRFMF",2,"L",120,"R",-120,"M",60,"N",-60,"F",0,1,"B",0,-1,"Q",-1,0,"P",1,0,"W",0,1,"S",0,-1,"A",-1,0,"D",1,0],};


function DisplayDemo(){
name=document.getElementById("Demos").value;
var values=demos[name];
document.getElementById("start").value=values[0];
document.getElementById("rules").value=values[1];
document.getElementById("iterations").value=values[2];
document.getElementById("Turn1").value=values[3];
document.getElementById("Turn1Angle").value=values[4];
document.getElementById("Turn2").value=values[5];
document.getElementById("Turn2Angle").value=values[6];
document.getElementById("Turn3").value=values[7];
document.getElementById("Turn3Angle").value=values[8];
document.getElementById("Turn4").value=values[9];
document.getElementById("Turn4Angle").value=values[10];
document.getElementById("RMove1").value=values[11];
document.getElementById("RMove1X").value=values[12];
document.getElementById("RMove1Y").value=values[13];
document.getElementById("RMove2").value=values[14];
document.getElementById("RMove2X").value=values[15];
document.getElementById("RMove2Y").value=values[16];
document.getElementById("RMove3").value=values[17];
document.getElementById("RMove3X").value=values[18];
document.getElementById("RMove3Y").value=values[19];
document.getElementById("RMove4").value=values[20];
document.getElementById("RMove4X").value=values[21];
document.getElementById("RMove4Y").value=values[22];
document.getElementById("AMove1").value=values[23];
document.getElementById("AMove1X").value=values[24];
document.getElementById("AMove1Y").value=values[25];
document.getElementById("AMove2").value=values[26];
document.getElementById("AMove2X").value=values[27];
document.getElementById("AMove2Y").value=values[28];
document.getElementById("AMove3").value=values[29];
document.getElementById("AMove3X").value=values[30];
document.getElementById("AMove3Y").value=values[31];
document.getElementById("AMove4").value=values[32];
document.getElementById("AMove4X").value=values[33];
document.getElementById("AMove4Y").value=values[34];
makeLSystem();
LSystemDraw();
}

function lSystem(start,rules,iteration){
if (iteration==0){
return start;
}
else
{
var next="";
for (i in start){
var char=start[i];
if (char in rules){
next+=rules[char];
}
else
{
next+=char;
}
}
return lSystem(next,rules,iteration-1);
}
}

function makeRules(rulesString){
var rules=rulesString.split(",");
out={};
for (i in rules){
var sr=rules[i].split(">");
out[sr[0]]=sr[1];
}
return out;
}

function makeLSystem(){
var startinput=document.getElementById("start").value;
var rulesinput=document.getElementById("rules").value;
var dict=makeRules(rulesinput);
var iterationsinput=document.getElementById("iterations").value;
var result=lSystem(startinput,dict,iterationsinput);
var output=document.getElementById("Results");
//LSystemDraw(result);
output.value=result;
}

function LSystemDraw(){
var instructions=document.getElementById("Results").value;
var turns={};
	turns[document.getElementById("Turn1").value]=parseFloat(document.getElementById("Turn1Angle").value);
	turns[document.getElementById("Turn2").value]=parseFloat(document.getElementById("Turn2Angle").value);
	turns[document.getElementById("Turn3").value]=parseFloat(document.getElementById("Turn3Angle").value);
	turns[document.getElementById("Turn4").value]=parseFloat(document.getElementById("Turn4Angle").value);

var relativemovements={};
	relativemovements[document.getElementById("RMove1").value]=[parseInt(document.getElementById("RMove1X").value),parseInt(document.getElementById("RMove1Y").value)];
	relativemovements[document.getElementById("RMove2").value]=[parseInt(document.getElementById("RMove2X").value),parseInt(document.getElementById("RMove2Y").value)];
	relativemovements[document.getElementById("RMove3").value]=[parseInt(document.getElementById("RMove3X").value),parseInt(document.getElementById("RMove3Y").value)];
	relativemovements[document.getElementById("RMove4").value]=[parseInt(document.getElementById("RMove4X").value),parseInt(document.getElementById("RMove4Y").value)];

var absolutemovements={};
	absolutemovements[document.getElementById("AMove1").value]=[parseInt(document.getElementById("AMove1X").value),parseInt(document.getElementById("AMove1Y").value)];
	absolutemovements[document.getElementById("AMove2").value]=[parseInt(document.getElementById("AMove2X").value),parseInt(document.getElementById("AMove2Y").value)];
	absolutemovements[document.getElementById("AMove3").value]=[parseInt(document.getElementById("AMove3X").value),parseInt(document.getElementById("AMove3Y").value)];
	absolutemovements[document.getElementById("AMove4").value]=[parseInt(document.getElementById("AMove4X").value),parseInt(document.getElementById("AMove4Y").value)];

var pi=Math.PI;
points=[];
var angle=0;
var x=0;
var y=0;
for (i in instructions){
var val=instructions[i];

if (val in turns){
angle+=turns[val]/180*pi;
}

if (instructions[i] in relativemovements){
forwardback=relativemovements[instructions[i]][1];
leftright=relativemovements[instructions[i]][0];
x+=Math.cos(angle)*forwardback;
y+=Math.sin(angle)*forwardback;
x+=Math.cos(angle+pi/2)*leftright;
y+=Math.sin(angle+pi/2)*leftright;
points.push([x,y]);
}

if (instructions[i] in absolutemovements){
forwardback=absolutemovements[instructions[i]][1];
leftright=absolutemovements[instructions[i]][0];
x+=leftright;
y+=forwardback;
points.push([x,y]);
}


}
var points=normalize_points(points);

var can=document.getElementById("LSystemCanvas");
const ctx = can.getContext('2d');
var w=can.width;
var h=can.height;

    ctx.strokeStyle = 'black';
    ctx.lineWidth = 2;
ctx.clearRect(0, 0, w, h);
//clear canvas;
ctx.beginPath();
ctx.moveTo( points[0][0]*w, points[0][1]*h); // point 1
for (i in points){
    ctx.lineTo(points[i][0]*w, points[i][1]*h);
}
ctx.closePath();
    ctx.stroke();
}

function normalize_points(p)
{
left=p[0][0];
right=p[0][0];
Ymax=p[0][1];
Ymin=p[0][1];
out=[];
for (i in p){
if (p[i][0]<left){left=p[i][0];}
if (p[i][0]>right){right=p[i][0];}
if (p[i][1]>Ymax){Ymax=p[i][1];}
if (p[i][1]<Ymin){Ymin=p[i][1];}
}

for (i in p){
var x=(p[i][0]-left)/(right-left);
var y=(p[i][1]-Ymin)/(Ymax-Ymin);
out.push([x,y]);
}
return out;}

function initalize_demos(){
var html="";
for (i in demos){
html+=`<option value="${i}">${i}</option>
`;
}
document.getElementById("Demos").innerHTML=html;
}

</script>

<h1>Iterate String With L-System</h1>
<form action="#">
      <label for="Demos">Demos</label>
      <select name="Demos" id="Demos" onchange="DisplayDemo()">
        <option value="initial">Unitialised</option>
      </select>
</form>
<div class=FormRow>
<div class=FormLeft>Start:</div>
<div class=FormRight><input class=fullsize id=start name=start /></div></div>
<div class=FormRow>
<div class=FormLeft>Rules:</div>
<div class=FormRight><input class=fullsize id=rules name=rules /></div></div>
<div class=FormRow>
<div class=FormLeft>Iterations:</div>
<div class=FormRight><input class=fullsize id=iterations name=iterations /></div></div>
<input value="Make L-System" type=button onclick="makeLSystem()">
<br>
Output:
<textarea id=Results> </textarea>
<br>
<h1>Draw L-System Output</h1>
<div class=FormRow><div class=FormThird><b>Turns</b></div></div>
<div class=FormRow><div class=FormThird>Name</div><div class=FormThird>Character</div><div class=FormThird>Degrees</div></div>
<div class=FormRow><div class=FormThird>Turn 1</div><div class=FormThird><input id=Turn1 name=Turn1 value="L" /></div><div class=FormThird><input id=Turn1Angle name=Turn1Angle value="90" /></div></div>
<div class=FormRow><div class=FormThird>Turn 2</div><div class=FormThird><input id=Turn2 name=Turn2 value="R" /></div><div class=FormThird><input id=Turn2Angle name=Turn2Angle value="-90" /></div></div>
<div class=FormRow><div class=FormThird>Turn 3</div><div class=FormThird><input id=Turn3 name=Turn3 value="M" /></div><div class=FormThird><input id=Turn3Angle name=Turn3Angle value="45" /></div></div>
<div class=FormRow><div class=FormThird>Turn 4</div><div class=FormThird><input id=Turn4 name=Turn4 value="N" /></div><div class=FormThird><input id=Turn4Angle name=Turn4Angle value="-45" /></div></div>
<br>
<div class=FormRow><div class=FormThird><b>Relative Movement</b></div></div>

<div class=FormRow><div class=FormQuarter>Name</div><div class=FormQuarter>Character</div><div class=FormQuarter>Right-Left</div><div class=FormQuarter>Forward-Back</div></div>

<div class=FormRow><div class=FormQuarter>Forward</div><div class=FormQuarter><input id=RMove1 name=RMove1 value="F" /></div><div class=FormQuarter><input id=RMove1X name=RMove1X value="0" /></div><div class=FormQuarter><input id=RMove1Y name=RMove1Y value="1" /></div></div>

<div class=FormRow><div class=FormQuarter>Backward</div><div class=FormQuarter><input id=RMove2 name=RMove2 value="B" /></div><div class=FormQuarter><input id=RMove2X name=RMove2X value="0" /></div><div class=FormQuarter><input id=RMove2Y name=RMove2Y value="-1" /></div></div>

<div class=FormRow><div class=FormQuarter>Left</div><div class=FormQuarter><input id=RMove3 name=RMove3 value="Q" /></div><div class=FormQuarter><input id=RMove3X name=RMove3X value="-1" /></div><div class=FormQuarter><input id=RMove3Y name=RMove3Y value="0" /></div></div>

<div class=FormRow><div class=FormQuarter>Right</div><div class=FormQuarter><input id=RMove4 name=RMove4 value="P" /></div><div class=FormQuarter><input id=RMove4X name=RMove4X value="1" /></div><div class=FormQuarter><input id=RMove4Y name=RMove4Y value="0" /></div></div>
<br>
<div class/=FormRow><div class=FormThird><b>Absolute Movement</b></div></div>
<div class=FormRow><div class=FormQuarter>Name</div><div class=FormQuarter>Character</div><div class=FormQuarter>Right-Left</div><div class=FormQuarter>Forward-Back</div></div>

<div class=FormRow><div class=FormQuarter>Forward</div><div class=FormQuarter><input id=AMove1 name=AMove1 value="W" /></div><div class=FormQuarter><input id=AMove1X name=AMove1X value="0" /></div><div class=FormQuarter><input id=AMove1Y name=AMove1Y value="1" /></div></div>

<div class=FormRow><div class=FormQuarter>Backward</div><div class=FormQuarter><input id=AMove2 name=AMove2 value="S" /></div><div class=FormQuarter><input id=AMove2X name=AMove2X value="0" /></div><div class=FormQuarter><input id=AMove2Y name=AMove2Y value="-1" /></div></div>

<div class=FormRow><div class=FormQuarter>Left</div><div class=FormQuarter><input id=AMove3 name=AMove3 value="A" /></div><div class=FormQuarter><input id=AMove3X name=AMove3X value="-1" /></div><div class=FormQuarter><input id=AMove3Y name=AMove3Y value="0" /></div></div>

<div class=FormRow><div class=FormQuarter>Right</div><div class=FormQuarter><input id=AMove4 name=AMove4 value="D" /></div><div class=FormQuarter><input id=AMove4X name=AMove4X value="1" /></div><div class=FormQuarter><input id=AMove4Y name=AMove4Y value="0" /></div></div>
<div><input value="Draw" type=button onclick="LSystemDraw()">


<canvas id=LSystemCanvas width=800 height=800></canvas>

<script> 
initalize_demos();</script>
Back to top