# Dynamic Recursive Fractal Tree
A **p5.js** recursive fractal tree whose branching angle varies smoothly over time, simulating a natural swaying animation. The tree transitions in color from brown at the trunk to green at the tips, and branches become progressively thinner with each level of recursion. This project demonstrates fundamental generative art concepts: recursion, color interpolation, and time‑based animation.
## Quick Start
1. Save the HTML code above as `index.html`.
2. Open it in any modern web browser (an internet connection is required to load p5.js from the CDN).
3. Watch the tree gently sway and grow.
## Code Walkthrough
### Global Parameters
```javascript
const MAX_DEPTH = 10; // Maximum recursion depth
const TRUNK_LENGTH = 120; // Initial trunk length (px)
const LENGTH_SCALE = 0.7; // Length reduction per level
const BASE_ANGLE = Math.PI / 6; // Base branching angle (30°)· MAX_DEPTH controls the complexity: higher values yield more branch generations. · TRUNK_LENGTH defines the starting height of the tree. · LENGTH_SCALE determines how much each child branch shrinks relative to its parent, mimicking natural tapering. · BASE_ANGLE is the fundamental branching angle, later modulated by an animated offset.
setup() and draw() — The p5.js Lifecycle
In p5.js, setup() and draw() are functions that you must define manually, but they are called automatically by the library at the appropriate times:
· setup() runs once when the sketch starts. It is typically used for initial configuration like creating the canvas and setting angle modes. · draw() runs repeatedly (about 60 times per second) after setup(), creating an animation loop. If draw() is omitted, p5.js draws a single frame and stops. For this animated tree, both functions are required.
function setup() {
createCanvas(600, 600);
angleMode(RADIANS);
}
function draw() {
background(235, 245, 235);
let angleOffset = sin(millis() * 0.002) * 0.3;
let dynamicAngle = BASE_ANGLE + angleOffset;
push();
translate(width / 2, height);
branch(TRUNK_LENGTH, 0, dynamicAngle);
pop();
}setup() details
· createCanvas(600, 600) creates a 600×600 pixel drawing surface. · angleMode(RADIANS) ensures all trigonometric functions and rotations work in radians for consistency.
draw() details & animation logic
· background(235, 245, 235) paints a fresh background each frame, erasing the previous tree. · sin(millis() * 0.002) produces a value that oscillates smoothly between –1 and 1 over time. Multiplying by 0.3 limits the angle variation to approximately ±0.3 radians (±17°), yielding a gentle sway. The resulting dynamicAngle is passed into the recursion and used for every bifurcation, making the whole tree oscillate in unison. · translate(width / 2, height) moves the origin to the bottom-center of the canvas. By default, p5.js draws with the Y‑axis pointing downward, so using a negative Y in line() makes the tree grow upward. · push() / pop() isolate transformations, preventing them from accumulating across frames.
branch(len, depth, angle) — The Recursive Heart
function branch(len, depth, angle) {Termination condition
if (depth > MAX_DEPTH) return;The recursion stops once the designated maximum depth is exceeded, keeping the structure finite and performant.
Color gradient
let t = map(depth, 0, MAX_DEPTH, 0, 1);
let brown = color(139, 69, 19);
let green = color(34, 139, 34);
let branchColor = lerpColor(brown, green, t);· map(depth, 0, MAX_DEPTH, 0, 1) normalizes the current depth to a range of [0, 1]. A depth of 0 (trunk) yields 0, and the maximum depth yields 1. · lerpColor(brown, green, t) linearly interpolates between saddle brown and forest green, creating a seamless transition as branches grow outward.
Branch thickness
let weight = map(depth, 0, MAX_DEPTH, 12, 1);
strokeWeight(weight);Again, map() normalizes depth to produce a stroke weight from 12 pixels (trunk) down to 1 pixel (finest twigs).
Drawing the segment
stroke(branchColor);
line(0, 0, 0, -len);A straight line is drawn upward from the current origin. After this call the origin is still at the branch’s base.
Recursive branching
translate(0, -len); // Move origin to branch tip
if (depth < MAX_DEPTH) {
let newLen = len * LENGTH_SCALE;
push();
rotate(angle); // Right child: rotate clockwise
branch(newLen, depth + 1, angle);
pop();
push();
rotate(-angle); // Left child: rotate counterclockwise
branch(newLen, depth + 1, angle);
pop();
}· translate(0, -len) shifts the origin to the tip of the current branch, making it the starting point for child branches. · For depths less than MAX_DEPTH, two symmetrical child branches are spawned. · Each child is drawn inside a push()/pop() block so that the rotation applied to one does not affect the other. · The dynamic angle is reused, ensuring the whole tree responds to the same oscillating value.
Visual and Animation Principles
· Dynamic sway: sin(millis()) delivers a smooth, periodic motion. The amplitude and frequency can be tuned to simulate light wind. · Self‑similar structure: Every layer follows the same geometric rules, producing a classical fractal pattern — a hallmark of natural branching. · Gradient coloring: Brown → green interpolation reinforces the sense of growth, from woody trunk to living foliage. · Finite depth: Capping the recursion depth guarantees performance and keeps the visual composition clean and balanced.
Tunable Parameters
Modify the global constants to create drastically different trees:
Variable Role Recommended range MAX_DEPTH Number of branch levels 6 – 14 TRUNK_LENGTH Trunk height 80 – 200 px LENGTH_SCALE Branch length decay 0.5 – 0.8 BASE_ANGLE Base bifurcation angle 0.2 – 0.6 rad Amplitude (0.3) Sway intensity 0.1 – 0.5
Ideas for Extension
· Add randomness (e.g., slight length or angle noise per branch) for a more organic look. · Draw leaves or blossoms at the terminal branches. · Change the color gradient over time to simulate seasonal shifts (green → orange → red). · Map the branching angle to the mouse position for interactive control.
Tech stack: p5.js · Recursion · Fractal geometry · Creative coding