L-System 3D Tree Generation
L-systems (Lindenmayer systems) are a formal grammar system developed by biologist Aristid Lindenmayer in 1968 for modeling biological growth, particularly well-suited for plants and trees. This article covers the implementation of L-systems for generating 3D fractal trees in Three.js, used in the animated background system.
The interactive visualization above demonstrates various L-system configurations. You can:
-
Select predefined L-systems to see different tree structures
-
Enter custom axioms and rules to create your own tree patterns
-
Adjust iterations and angle to see how parameters affect the tree shape
-
Rotate and zoom by clicking and dragging or scrolling
Overview
L-systems are based on rewriting rules that replace symbols in a string iteratively. The generated string is then interpreted by a "turtle" (a virtual drawing agent) that moves and rotates in 3D space to draw the tree structure.
Key Concepts
An L-system consists of:
-
Axiom: The initial string (seed) - e.g.,
"F"or"X" -
Rules: Production rules that replace symbols - e.g.,
F → F[+F]F[-F]F -
Iterations: How many times to apply the rules (typically 3-6)
-
Angle: The rotation angle for
+and-symbols (typically 20-30°)
Turtle Graphics Interpretation
The generated string is interpreted by a turtle that moves and rotates in 3D space:
-
ForG: Move forward and draw a line (branch) -
+: Turn left (yaw) byangle -
-: Turn right (yaw) byangle -
[: Push current state (position, direction, up vector) onto a stack -
]: Pop state from stack (return to previous position and orientation) -
>: Pitch up -
<: Pitch down -
&: Roll left -
^: Roll right -
L: Draw a leaf (simplified as a sphere) -
!: Decrease branch thickness -
': Decrease branch length
Implementation Details
String Generation
The L-system string is generated by iteratively applying production rules:
function generateLSystem(rule: LSystemRule): string {
let result = rule.axiom;
for (let i = 0; i < rule.iterations; i++) {
let newResult = '';
for (const char of result) {
newResult += rule.rules[char] || char;
}
result = newResult;
}
return result;
}
Each iteration expands the string according to the rules. For example, with axiom "F" and rule F → F[+F]F[-F]F:
-
Iteration 0:
F -
Iteration 1:
F[+F]F[-F]F -
Iteration 2:
F[+F]F[-F]F[+F[+F]F[-F]F]F[+F]F[-F]F[-F[+F]F[-F]F]F[+F]F[-F]F -
And so on...
3D Turtle Graphics
The turtle maintains state including position, direction, and up vector for proper 3D rotation:
interface TurtleState {
position: THREE.Vector3;
direction: THREE.Vector3; // Forward direction
up: THREE.Vector3; // Up direction for pitch/roll
length: number; // Current branch length
radius: number; // Current branch radius
}
Branch Generation
Branches are created as cylinders with decreasing radius and length:
// Create branch segment
const branchGeometry = new THREE.CylinderGeometry(
currentRadius,
currentRadius * 0.9, // Taper
currentLength,
6 // Segments for performance
);
const branch = new THREE.Mesh(branchGeometry, trunkMaterial);
Each branch is positioned and oriented based on the turtle's current state, creating a continuous tree structure.
State Stack
The [ and ] symbols use a stack to save and restore turtle state, enabling branching:
const stateStack: TurtleState[] = [];
// Push state
if (char === '[') {
stateStack.push({
position: turtle.position.clone(),
direction: turtle.direction.clone(),
up: turtle.up.clone(),
length: turtle.length,
radius: turtle.radius,
});
}
// Pop state
if (char === ']') {
const savedState = stateStack.pop();
if (savedState) {
turtle.position = savedState.position;
turtle.direction = savedState.direction;
turtle.up = savedState.up;
turtle.length = savedState.length;
turtle.radius = savedState.radius;
}
}
Predefined L-System Configurations
Simple Branching
Axiom: F
Rule: F → F[+F]F[-F]F
Angle: 25°
Iterations: 4
Creates a basic ternary branching pattern with three branches at each node.
Complex Branching
Axiom: F
Rule: F → FF+[+F-F-F]-[-F+F+F]
Angle: 22.5°
Iterations: 4
Produces denser branching with more organic structure, using longer segments and more complex branching patterns.
Recursive Tree
Axiom: X
Rules:
X → F+[[X]-X]-F[-FX]+X
F → FF
Angle: 25°
Iterations: 5
Uses a recursive structure with a non-terminal symbol X that expands into more complex patterns, creating self-similar structures.
Bushy Tree
Axiom: F
Rule: F → F[+F][-F][>F][<F]
Angle: 20°
Iterations: 4
Creates four-way branching (left, right, up, down) for dense foliage, using pitch controls (> and <) for vertical branching.
Custom L-System Input
The visualization allows users to enter custom axioms and rules to experiment with different tree structures. The system supports:
-
Multiple rules: Define rules for different symbols (e.g.,
F → FF,X → F+[[X]-X]-F[-FX]+X) -
Flexible syntax: Use any symbols, though standard turtle graphics symbols have special meaning
-
Real-time updates: Changes to rules or iterations update the tree immediately
Performance Considerations
L-system tree generation can be computationally expensive, especially with high iteration counts:
-
String length: Grows exponentially with iterations (e.g., 3 iterations → ~1000 chars, 5 iterations → ~100,000 chars)
-
Geometry complexity: Each branch segment requires geometry creation
-
Memory usage: State stack and geometry objects consume memory
Optimizations used:
-
Limited iterations: Default to 3-5 iterations for interactive visualization
-
Low-poly geometry: Use 6 segments for cylinders instead of high-resolution
-
Throttled updates: Regenerate tree only when parameters change
-
Geometry reuse: Reuse materials across branches
Comparison with Simplified Trees
The animated background uses simplified geometric trees for performance, while L-systems provide more realistic and varied tree structures. The trade-off:
-
L-systems: More realistic, infinite variety, but computationally expensive
-
Simplified trees: Fast, good enough for background, but less variety
For the background, simplified trees are preferred because:
-
Thousands of trees need to be rendered
-
Performance is critical
-
Trees are viewed from a distance where detail is less important
References
Academic Papers
-
Lindenmayer, A. (1968). "Mathematical Models for Cellular Interactions in Development." Journal of Theoretical Biology, 18(3), 280-315. DOI - Original L-system paper
-
Prusinkiewicz, P., & Lindenmayer, A. (1990). The Algorithmic Beauty of Plants. Springer-Verlag. - Comprehensive book on L-systems and plant modeling
-
Prusinkiewicz, P., et al. (2001). "The Use of Positional Information in the Modeling of Plants." ACM SIGGRAPH Computer Graphics, 35(3), 289-300. - Advanced L-system techniques
Online Resources
-
L-Systems - Wikipedia article on L-systems
-
Turtle Graphics - Turtle graphics explanation
-
Fractal Trees - Mathematical basis for fractal tree structures
-
Context-Free Grammars - Theoretical foundation for L-systems
Interactive Tools
-
L-System Explorer - Online L-system visualizer
-
Algorithmic Botany - Resources on algorithmic plant modeling
Related Articles
-
An adventure with 3js 3d Backgrounds - Overview of the animated background system
-
Fractal Trees - General fractal tree generation techniques
-
Terrain Generation - Procedural terrain generation where these trees are placed and integrated into the landscape
-
3d Background - Cell Shading - Rendering techniques used for trees
-
Performance Optimization - Optimization strategies for rendering many trees efficiently
Future Enhancements
Potential improvements to the L-system implementation:
-
Stochastic L-systems: Add randomness to rules for more natural variation
-
Parametric L-systems: Use parameters in rules for more control
-
Context-sensitive L-systems: Rules that depend on neighboring symbols
-
Ecosystem simulation: Multiple trees with competition and growth
-
Seasonal variation: Trees that change appearance over time
-
Wind effects: Animated trees that respond to wind
-
Leaf placement: More sophisticated leaf generation and placement
-
Bark texture: Procedural bark textures for more realism