Draw Canopy Fractal

Draw a canopy-tree L-system fractal in your browser. 7 iterations, custom colors, gradient depth, PNG export. Free, offline, client-side, instant, secure.

L-system tree fractal with axiom F and rule F → FF+[+F-F-F]-[-F+F+F] at 22.5° turn angle. Trunk-to-leaf color gradient by recursion depth.

How to Use Draw Canopy Fractal

  1. Understand the L-system. Axiom F, rule F → FF+[+F-F-F]-[-F+F+F]. F = draw forward; +/- = turn left/right 22.5°; [ = push state (save position + heading); ] = pop state. Each iteration replaces every F with the rule string, so the L-system grows by ~17× per iteration.
  2. Pick iterations 1-7. 1-2 = sparse Y-fork. 3-4 = realistic tree (recommended). 5 = dense bush. 6-7 = thick canopy with thousands of segments - may take 1-3 s on mobile (button label changes to "Drawing…").
  3. Tune the geometry. Line length 2-30 px: smaller = compact; larger = tall and sprawling. Canvas size 300-1200 px (square). Line width 1-5 px.
  4. Choose colors. Trunk (default brown #92400e), leaf (default green #22c55e), background (default white). The gradient interpolates trunk → leaf based on recursion depth ÷ (iterations + 1), so deeper branches show progressively more leaf color.
  5. Watch the low-contrast warning. If your leaf and background colors are too close in luminance (YIQ distance < 50), an info toast warns you the tree may be invisible.
  6. Press Ctrl+Enter or click Draw Fractal. Stats card shows: segments, branches, max recursion depth, L-system character count, render time.
  7. Export: Copy puts the PNG on your clipboard (image type, not data URL - pasteable directly into image editors via ClipboardItem API). Download saves a timestamped file.

Frequently Asked Questions

How does this differ from other L-system tree fractals?

The rule FF+[+F-F-F]-[-F+F+F] creates TWO branch clusters per segment (one tilted left, one tilted right), each containing 3 sub-strokes (forward-turn-forward-turn-forward). Compared to a simpler binary tree F → F[+F][-F] (one fork per F), the canopy rule produces denser, more organic-looking foliage at lower iteration counts. The 22.5° turn angle is the convention from Lindenmayer’s original 1968 paper – wider angles look more “windswept”, narrower more “spear-like”.

What do the brackets actually do?

They implement a state stack. [ pushes current (x, y, heading, depth) onto a JavaScript array; ] pops the most recent frame and restores those values. Between the brackets you can wander off to draw a branch and the bracket-pair restores you to exactly where you forked. This is identical to LIFO subroutine calls – the brackets are a textual representation of recursion. Without them, F+F+F-F would just trace one wandering path; with [F+F+F]-F you trace a branch then jump back to draw the main trunk.

Why does the color gradient by depth?

Real trees have darker, browner bark on the trunk that transitions to lighter green at the canopy. The tool reads the current bracket-stack depth (incremented at [, decremented at ]), divides by iterations+1 to get t ∈ [0, 1], and uses that as the lerp parameter for trunk → leaf interpolation. So depth 0 (trunk) = trunk color, deepest branches = leaf color. Without this trick the tree would be uniformly one color, looking flat.

Why is iteration capped at 7?

The L-system grows by ~17× per iteration (rule has 17 F characters in expansion). Lengths: iter 1 = 20 chars, iter 2 = 340, iter 3 = ~5.8k, iter 4 = ~100k, iter 5 = ~1.7M, iter 6 = ~29M, iter 7 = ~500M. Browsers OOM around iter 8. Iter 7 already takes 1-3 s to render. Iter 7 is the comfortable ceiling; going higher would crash the tab.

What’s “max depth” in the stats card?

The deepest bracket-stack depth reached during rendering. Equivalent to the longest unbroken nested [[[[... sequence in the L-system string. For iter N, max depth roughly equals N (since branches are restored between ] pairs at the same level). Useful for tuning – if max depth is small (e.g. 3) when you set iter 6, the rule doesn’t actually go that deep.

Why does my tree grow upward and not branch sideways?

Initial heading is −π/2 radians (straight up) and the starting position is bottom-center. The rule’s + and - operators add ±22.5° to the heading. So all branches deviate symmetrically from “straight up,” which matches biological trees growing toward the light (heliotropism). Mathematically, you could change initial heading to 0 and the tree would grow rightward – but the bottom-up convention is more recognizable.

Can I simulate different tree species?

The rule is fixed (canopy), but parameters vary the look. Pine-like: small line length (5-7 px), narrow color range (dark green to brown). Oak-like: longer line length (15-20 px), iteration 5+, wide trunk. Willow-like: very short line length (3 px) at iteration 6+ for the drooping fine-detail. For a different rule (Sierpiński-tree, dragon curve, Hilbert curve), you’d need a different tool – the L-system rule is hardcoded here.

Does rendering use Retina/HiDPI?

Yes. The canvas’s backing-store resolution is multiplied by window.devicePixelRatio (2 on Mac Retina, 2 or 3 on most phones), then the drawing transform is scaled by the same ratio. Result: anti-aliased lines stay crisp on high-DPI displays. The CSS-displayed size is still the value you set (e.g. 800 px) – only the underlying pixel buffer is denser.

Why does the L-system char count appear in stats?

It’s the length of the expanded rule string before drawing. Useful for debugging performance: render time is roughly proportional to L-system bytes (each F = one stroke). If 100k chars takes 30 ms on your machine, you can predict 1.7M chars (iter 5) at ~500 ms. Lets you trade iteration depth for speed without trial-and-error.

Is my data secure?

Yes, 100% client-side. The L-system expansion and Canvas drawing run in your browser. No parameters, images, or strings are sent anywhere. Verify via your browser’s Network tab – only the initial HTML/CSS/JS load happens.