<svg id="canvas">

</svg>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #e9edeb;
}

svg {
  width: 75vmin;
  height: 75vmin;
  background: #141b3d;
  box-shadow: 1px 4px 32px 0px #141b3d;
}
import { SVG } from "https://cdn.skypack.dev/@svgdotjs/svg.js@3.1.1";
import {
  map,
  pointsInPath,
  random
} from "https://cdn.skypack.dev/@georgedoescode/generative-utils@1.0.37";

// set the width / height of our drawing space (viewBox!)
const width = 1024;
const height = 1024;

// create an svg.js instance
const svg = SVG("#canvas").viewbox(0, 0, width, height);

// create an initial tree
generateTree();

// every 0.5 seconds, clear the <svg> and create a new tree!
setInterval(() => {
  svg.clear();

  generateTree();
}, 1000);

function generateTree() {
  // what color should our background be?
  // all colors in this pen are from a book about old paints!
  const backgroundColor = "#141B3D";

  // what color should our branches be?
  const branchColor = "#E9EDEB";

  // what color should the little specks in the background be?
  const speckColors = ["#E9EDEB", "#CEA003", "#B52F36"];

  // render the background in the form of a <rect>
  svg.rect(width, height).fill(backgroundColor);

  // set the tree height to be 75% of the viewBox
  const treeHeight = height * 0.75;

  // create the center trunk using a <line>
  const trunk = svg
    .line(width / 2, 0, width / 2, treeHeight)
    .cy(height / 2)
    .stroke({
      color: "#E9EDEB",
      width: 4
    });

  // define how many branches we have, by splitting the trunk into equidistant points
  // this function uses svg's getTotalLength/getPointAtLength methods
  const trunkPoints = pointsInPath(trunk.node, random(16, 32, true));

  // where should we start drawing branches? startOffset is the top of the trunk
  const startOffset = random(0, trunkPoints.length / 8, true);

  // where should we stop drawing branches? endOffset is the bottom of the trunk
  const endOffset = random(0, trunkPoints.length / 4, true);

  // in degrees, define a random angle or rotation for all little branches
  const branchAngle = random(24, 48);

  // what is the largest size our branches could be?
  const maxBranchLength = random(treeHeight / 6, treeHeight / 3);

  // iterate over the range of trunk points selected
  for (let i = startOffset; i < trunkPoints.length - endOffset; i++) {
    // store the point
    const point = trunkPoints[i];

    // map current point's index to a branch length value, higher branches are smaller!
    // add a little sprinkle of randomness to the branch length
    const branchLength =
      map(
        i,
        startOffset,
        trunkPoints.length - endOffset,
        maxBranchLength / 8,
        maxBranchLength
      ) * random(0.75, 1);

    // draw the right-hand branch
    svg
      .line(point.x, point.y, point.x + branchLength, point.y)
      .stroke({
        color: branchColor,
        width: 4
      })
      .rotate(-branchAngle, point.x, point.y);

    // draw the left-hand branch
    svg
      .line(point.x, point.y, point.x - branchLength, point.y)
      .stroke({
        color: branchColor,
        width: 4
      })
      .rotate(branchAngle, point.x, point.y);
  }

  // draw lots of random circles for the background
  for (let i = 0; i < 128; i++) {
    svg
      .circle(random(1, 4))
      .cx(random(0, width))
      .cy(random(0, height))
      .fill(random(speckColors));
  }
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.