A trunk that curves, splits, and splits again, with every length and every fork drawn from a distribution.
Nothing here is a rule about shape
Most fractal trees are a recursion on angle: draw a line, turn left by θ, recurse,
turn right by θ, recurse. This one is a recursion on growth, and almost every
number in it is drawn from a distribution rather than fixed.
float length = Scale / depth * (float)rng.Gaussian(1, LengthVar);
The length of a branch is its nominal length — inversely proportional to depth —
multiplied by a Gaussian with mean 1 and standard deviation 3.8. That is a very wide
distribution: a branch is as likely to come out at a third of its nominal length as
at three times it, and occasionally the multiplier is negative and the branch grows
backwards.
Depth one: the trunk alone. It is not straight. Each branch picks a curve direction
at random and applies a constant bend to every ten-pixel segment, so a branch is an
arc rather than a line — and at depth one the bend is quartered, which is why the
trunk is the straightest thing in the drawing.
Two ways to branch
A branch produces children in two different ways, and they behave differently.
Terminal. When a branch runs out of segments it always forks in two, symmetrically
about its own heading:
float termDir = rng.Unif(0, 1) < 0.5 ? -1f : 1f;
GrowBranch(…, angle + Spread * termDir, depth + 1, …);
GrowBranch(…, angle + Spread * -termDir, depth + 1, …);
Lateral. At every segment along its length there is a Branchiness chance —
about one in twenty-three — of budding a side branch at half the spread angle:
if (rng.Unif(0, 1) < Branchiness) { … GrowBranch(…, angle + Spread / 2f * sdir, depth + 1, …); }
The terminal fork makes the tree symmetric and the lateral bud makes it not. Without
the buds this would be a binary tree with a recognisable silhouette; with them, a long
branch can carry half a dozen children at arbitrary points along it, and the tree
becomes irregular in a way that reads as botanical.
Second-order branches. Some come from the trunk’s terminal fork, some budded off it
part-way up.
Gravitropism
Real branches turn upward. This one does too, and the term is small enough to be easy
to miss:
float up = angle < -MathF.PI / 2f ? MathF.PI / 2f - angle : angle - MathF.PI / 2f;
float nextAngle = angle + curve + up * UpAmount * depth;
up is the signed angular distance from vertical, and it is added back scaled by
UpAmount × depth — about 0.0095 per level. So the correction is nearly nothing at
depth one and appreciable at depth twelve: the trunk goes where it likes and the fine
branches all curl towards the sky.
That single term is the difference between a diagram and a tree.
Fourth order. The silhouette is essentially settled by branches this shallow; what
comes after is texture.
Thinning
bp.StrokeWidth = StrokeWidth / MathF.Pow(LineWidthFalloff, depth);
Stroke width falls by a factor of 1.6 per level. At depth one that is about sixteen
pixels; at depth twelve it is a tenth of a pixel, which the rasteriser draws as the
faintest line it can. The tree fades out rather than stopping.
Each segment carries its own stroke width in the shape list and the renderer sets
it at draw time, so there is nothing to restore between siblings.
Eighth order.
The whole tree. Leaves are drawn only in the last two levels:
if (LeafRadius > 0 && depth >= MaxDepth - 2) { … canvas.DrawCircle(x, y, r, lp); }
Each is a filled circle of Gaussian radius around 4, in the leaf colour shifted by up
to ±5 in hue, saturation and lightness. So the canopy has variation without having a
palette.
Withheld, not pruned
The stages above withhold deep branches rather than stopping the recursion. That
distinction matters: the tree is grown to full depth in every stage and the random
stream is consumed identically, so the trunk in stage one is exactly the trunk in
stage five. Pruning the recursion instead would give every stage a different tree.
The growth also separates rendering into two passes: all branch segments are drawn
before any leaf dot, so the canopy sits in front of the wood at every stage.
Six trees
The six variants are six species, not six palettes. They share one generator but
disagree on almost everything it reads: the growth parameters, the seed, the canvas
position, and a few switches the species above never used.
| Rendering | Seed | Design |
|---|
| Neon Blue | 42 | the monument — a large, centred tree with a dense crown, cyan on black |
| Ember Orange | 71 | the gale — a strong curve and a weak upward pull sweep the whole tree sideways |
| Toxic Green | 29 | the canopy — a deeper recursion with heavy budding grows a tight, round crown |
| Winter | 7 | the bare tree — no leaves, sparse buds, charcoal ink on warm paper |
| Blossom | 55 | the orchard — a wide crown of large pink leaf dots on cream paper |
| Dusk | 17 | the grove — three small trees under a moon disc, indigo and amber |
The differences come from the same knobs, turned further or held differently:
Scale = 178f, CurveAmount = 0.17f, Spread = 0.54f, UpAmount = 0.006f, // Neon
StartX = 360f, Scale = 205f, CurveAmount = 0.26f, UpAmount = 0.004f, // Ember
MaxDepth = 13, Branchiness = 0.07f, Spread = 0.55f, // Toxic
LeafRadius = 0f, StartX = 620f, Scale = 168f, // Winter
LeafRadius = 7.5f, LeafColorDelta = 4f, Spread = 0.56f, // Blossom
TrunkCount = 3, DiscCenter = (500, 165), DiscRadius = 72f, // Dusk
LeafRadius = 0 suppresses the leaf dots entirely — Winter is bare wood. TrunkCount
plants additional trunks to the right of StartX, each grown from the same random
stream in turn. The disc is a flat backdrop layer drawn before the tree, so in Dusk
the moon sits behind the canopies at every stage.
Scale and position are composition knobs as much as growth knobs: each preset was
placed so its species fills the plate deliberately — the monument centred with sky
above it, the gale sweeping across the middle rather than hugging a corner, the bare
Winter tree held small and centred in its paper margins, the grove spread across the
frame under the moon.
The museum plate
The gallery closes with the lead presented as a finished piece: the neon monument
hung on a gallery wall behind a mat and a dark frame. The frame is a separate layer —
MuseumFrame — that owns no generative state and knows nothing about trees; it
contain-fits any artwork into an opening, and the mat and frame are fractions of
the plate, so the treatment scales with whatever size the plate is. It is
presentation, not growth, so it appears once, as the closing plate, rather than as
a framed copy of every variant.
Determinism
SeededRandom — the sine-based generator — is seeded from
the preset and consumed depth-first in the recursion’s own order: a starting angle per
trunk, then per branch a length, a curve direction, a bud test per segment, a terminal
direction, and for the last two levels three colour offsets and a radius. Same seed,
same tree, on any machine.