Self reference in nature

With a hint of malice I decided to test the following code.

<g id="branch">
	<path d="M 0 0 v -10" />
	<g transform="translate(0 -10)">
		<g transform="rotate(25)">
			<use href="#branch" />
		</g>
		<g transform="rotate(-25)">
			<use href="#branch" />
		</g>
	</g>
</g>

If you are not familiar with SVG syntax the snippet will probably go over your head. If you are, do not worry, the browser did not crash under the pressure of drawing far more than a single line.

The intent

As it may not be blatantly obvious by the premise, the plan was to honor the upcoming spring season by drawing a tree. Recursively.

On top of drawing one line, straight up from the bottom of the SVG canvas, I tried to delegate the process to the browser.

Move up at the end of the line.

<g transform="translate(0 -10)">
	<!-- ... -->
</g>

Rotate, now on one side, now on the other.

<g transform="rotate(25)">
	<!-- ... -->
</g>
<g transform="rotate(-25)">
	<!-- ... -->
</g>

And in each group, repeat the same structure.

<g transform="rotate(25)">
	<use href="#branch" />
</g>

I wasn’t expecting much, but knowing the flexible and forgiving nature of HTML, the hope was the window would try and draw the branch, at least a handful of times before calling it quits.

Unfortunately, but reasonably, the specification for SVG is firm on the subject. Circular references are dropped, ignored, throwing the possibly ill-conceived concept out of the window.

You could use the path itself, repeating the line at different angles. And ultimately, this is exactly the markup structure you would find on the page.

<path id="branch" d="M 0 0 v -10" />
<g transform="translate(0 -10) rotate(25)">
	<use href="#branch" />
</g>
<g transform="translate(0 -10) rotate(-25)">
	<use href="#branch" />
</g>

To handle more than one level, however, and save a few keystrokes at least in the authoring process, you need to consider the recursive logic outside of the markup language. For instance with Javascript. And why not, with one of the snazziest Svelte features.

Components

Start with the canvas, a structure you are definitely not going to repeat over and over.

<svg viewBox="0 0 80 50">
	<g transform="translate(40 49)" <!-- --></g>
</svg>

To materialize the line, set a visible stroke. Thick enough to be visible, but not enough to overwhelm the length of the line.

<g stroke="#8a6f68" stroke-width="1" stroke-linecap="square">
	<!-- ...  -->
</g>

stroke-linecap helps to have the multiple lines tightly connected. Past this preference, include a reference to a component to-be.

<g stroke="#8a6f68" stroke-width="1" stroke-linecap="square">
	<Branch />
</g>

Just be sure to create and import the piece.

<script>
	import Branch from './Branch.svelte';
</script>

Moving on to the branch, add the obvious line, but also the group elements.

<path d="M 0 0 v -10" />
<g transform="translate(0 -10)">
	<g transform="rotate(25)">
		<!-- ... -->
	</g>
	<g transform="rotate(-25)">
		<!-- ... -->
	</g>
</g>

The idea is to add the logic to repeat the same structure, but outside of the reliable world of HTML, we need to be judicious with the logic. We need a stopping rule. One way to prevent an infinite loop is with a property, updated with each iteration.

Say we make the thickness of the stroke into a variable for the branch component.

<script>
	export let strokeWidth = 1;
</script>

<g stroke-width={strokeWidth}>
	<!-- ... -->
</g>

We can then include the additional group elements only when the width is above an arbitrary threshold.

{#if strokeWidth > 0}
	<g transform="translate(0 -10)">
		<!-- ... -->
	</g>
{/if}

And this is where Svelte truly shines. Not only you are able to handle the logic with the if statement, injecting additional elements if need be. With svelte:self you can then reference the component in one quick declaration.

<g transform="rotate(25)">
    <svelte:self strokeWidth={strokeWidth - 0.3} />
</g>
<g transform="rotate(-25)">
    <svelte:self strokeWidth={strokeWidth - 0.3} />
</g>

Just be sure to update the variable to mercifully end the loop.

A beautiful, fractal tree.

Animation

The static picture is already impressive, but as you create the branches with HTML you are able to animate the elements with CSS. What is more, through the mighty <path> element you can actually draw the line in increments.

Start by specifying the length of the segment with the pathLength attribute.

<path class="line" pathLength="1" d="M 0 0 v -10" />

The instruction saves you the considerable hassle of considering the actual length — although you could approximate the value given the simple d attribute.

With the fixed measure the idea is to add dashes of the same length.

.line {
	stroke-dasharray: 1;
}

Through the stroke-dasharray property you instruct the browser to draw a dash of length 1, then a gap with the same value. You could be explicit and detail the pair.

.line {
	stroke-dasharray: 1 1;
}

But the two snippets are ultimately equivalent. You draw dashes separated by a segment of the same length. If you then offset the line with stroke-dashoffset.

.line {
	stroke-dasharray: 1;
	stroke-dashoffset: 0.2;
}

The dash disappears in favor of the void space. It’s actually quite the nifty operation.

pathLength="1"stroke-dasharray="1"stroke-dashoffset="0.2"01

Enough theory, however. Instead of applying the offset one time, we want to animate the value.

.line {
	stroke-dasharray: 1;
	animation: line 1s linear both;
}

From 1 to 0.

@keyframes line {
	from {
		stroke-dashoffset: 1;
	}
	to {
		stroke-dashoffset: 0;
	}
}

Hold your excitement for a brief moment, however. With the current setup the lines are going to be drawn, but all at the same time. To really sell the effect we’d like to animate the elements in sequence.

In the branch component add a property to consider the level of the different instances.

<script>
	export let generation = 0;
</script>

And making sure to increment the value with each generation.

<svelte:self
    generation={generation + 1}
    strokeWidth={strokeWidth - 0.3} />

You are then able to set the growing delay.

<path 
	class="line" 
	style="animation-delay: {generation}s" 
	... />

Note that the value is included as a number, and is then accompanied by thes character to describe the unit of measure — seconds. It’s not a typo to have the letter outside of the curly braces. Just a happy accident.

Since the animation lasts exactly 1 second you can get by using the counter as-is, but to be more flexible you could use a separate variable for the precise duration.

<path
	class="line"
	style="
    animation-duration: {animationDuration}s
    animation-delay: {generation * animationDuration}s
    "
/>

At the end of the day, what matters the most is that the warm season is almost upon us.

Small note: to give you the opportunity of playing the animation at your leisure I had to recreate the effect with JavaScript and the Web animation API. If you code along, however, CSS is able to complete the effect all on its own.

Additional, Svelte-related note: you may want to add the CSS in the scope of the parent component, the one where you specify the <svg> element. To then apply the animation, instruct Svelte to keep the properties through the :global helper.

<style>
	svg :global(.line) {
		stroke-dasharray: 1;
		animation: line 1s linear both;
	}
	/* @keyframe ... */
</style>

Branching with style

Mixing SVG and Svelte one more time let’s complete the celebration of the pleasant season with a last, defining feature.

If the width of the stroke allows it, we are more than glad to repeat ourselves. If not, however, we’ve reached the end of the line. Almost literally.

{#if strokeWidth > 0}
	<!-- ...self -->
{:else}
	<g transform="translate(0 -10)">
		<!--...-->
	</g>
{/if}

At this juncture we can draw something else, like a fitting, blooming flower. I’ll let you design your own blossom, but for my version, I took inspiration from a previous article dedicated to a cherished celestial body.

Spring has sprung

Hope you don’t mind the inclusion of one more branch as well. A star was born, and is now time for spring to shine.