off
ikonic>
OUT 0 B

Glass

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.

This demonstration is being rebuilt for Ikonic.

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:

  • F or G: Move forward and draw a line (branch)
  • +: Turn left (yaw) by angle
  • -: Turn right (yaw) by angle
  • [: 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. DOI90079-9) - 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

Interactive Tools

Related Articles

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