Before you go, check out these stories!

0
Hackernoon logoA Tutorial On Creating Generative Characters Using SVG by@georgedoescode

A Tutorial On Creating Generative Characters Using SVG

Author profile picture

@georgedoescodeGeorge Francis

UI ⚡ + Generative art ❤️

There are two things I love in this world: blobby shapes and putting googly eyes on things. This tutorial combines both of my great loves and hopefully provides a gentle introduction to generative art whilst doing so.

Here's what we'll be making:

It is a somewhat simple example of what you can achieve with a generative approach to design/art, but hopefully something you can expand on.

Prerequisites ℹ️

No prior generative art knowledge is required! This tutorial is ideal for folks who are already familiar with JavaScript / HTML / CSS and are looking to get started with generative art.

What... is generative art? 🤔

The simplest definition I can find of generative art is on the Tate gallery website —

“Generative art is art made using a predetermined system that often includes an element of chance – is usually applied to computer based art”

I think this is perfect and worth keeping in mind as we progress through this tutorial, especially if you are new to this stuff.

Let's build! 🔨

For this tutorial we are going to use SVG to render our character, JavaScript to decide what to render, and a little sprinkling of CSS to make sure they sit nicely on the page.

I have also included a couple of external JS libraries to keep our code simple and clean.

I have set up a CodePen that you can fork here which has all this stuff pre-added. Once you have forked the pen or set up your environment, we are ready to start creating our characters!

Package installation

If you are creating your own environment from scratch rather than forking the CodePen, you can install the required packages with:

npm install @georgedoescode/spline @svgdotjs/svg.js

They can then be imported into your JavaScript like so:

import { spline } from "@georgedoescode/spline";
import { SVG } from "@svgdotjs/svg.js";

Note: if you plan on setting up your own environment, remember you will likely need a bundler such as Parcel or Webpack to handle these kinds of module imports.

A blank canvas 🖼️

If you started your project by forking the above CodePen, then you already have this CSS set up.

If not, feel free to add the following to your project to place the 

<svg />
nicely in the centre of the viewport. Or don’t! In the words of the Isley Brothers — it's your thing, do what you wanna do.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 100vh;
  display: grid;
  place-items: center;
}

svg {
  width: 75vmin;
  height: 75vmin;
}

The birth of a Blob 👶

There will be no birds, bees, storks etc here. Just a code editor of your choice and some ES6 class syntax 🤖

Straight off the bat, we need to define a few properties:

  • width: the viewBox width of the SVG
  • height: the viewBox height of the SVG
  • target: where the 
    <svg />
     element should be added in the DOM
  • svg: the 
    svg.js
     instance we will use for rendering
  • x: the horizontal position of our character within the SVG viewBox
  • y: the vertical position of our character within the SVG viewBox

With all this in mind, we should have a class constructor that looks something like this:

class BlobCharacter {
  constructor(width, height, target) {
    // viewBox width & height dimensions
    this.width = width;
    this.height = height;

    // position of our character within the viewBox (the center)
    this.x = this.width / 2;
    this.y = this.height / 2;

    // <svg /> element (svg.js instance) we are using to render
    this.svg = SVG()
      .addTo(target) // mount instance to our target
      .viewbox(0, 0, this.width, this.height); // set the <svg /> viewBox attribute
  }
}

Once we have defined the class constructor, we can create a brand new Blob character by calling:

// viewBox w x h, target to append the <svg /> element to
const character = new BlobCharacter(200, 200, document.body);

You won’t see anything just yet, and that’s cool.

Note: the viewBox attribute

If you are new to SVG and wondering what viewBox is, it essentially defines a coordinate space that you can draw anything relative to. In our case 200px by 200px.

Once you have defined a viewBox, the SVG will draw everything relative to the space you have defined whilst scaling to any resolution. This makes creating responsive generative works nice and easy!

If you would like to read more about viewBox - here is a great article on CSS tricks.

All creatures great and small 🐭 🐘

Now that we have all the “boilerplate” setup for our character, it’s time for some fun!

The first thing we need to think about is the overall size of our character. Should they be large or small? How do we define that?

As we will be basing our character's shape on a circle (more on this later) we can use the circle’s radius to define our character's size. Don’t worry, I’m no mathematician and this isn’t going to get too deep.

A number between 50 and 80 should do perfectly given our viewBox dimensions of 200px by 200px. Here's an infinitely useful utility function that we can use to generate a random number within a range:

// choose a number within a range, integer (whole number) by default
function random(min, max, float = false) {
  const val = Math.random() * (max - min) + min;

  if (float) {
    return val;
  }

  return Math.floor(val);
}

Using this very handy utility function, we can define a random size for our character like so:

class BlobCharacter {
  constructor(width, height, target) {
    ...
    // choose a random size / radius for our character
    this.size = random(50, 80);
  }
}

Perfect! We still won't see anything, but the code is looking good.

Drawing the body ✏️

I think a good next step is to draw our character’s body. This is probably the trickiest part of the tutorial, but don’t worry if it’s a little confusing!

Take your time, play around with the code, break it, put it back together.

I find that often in generative art I use a code snippet for a while before I truly understand it. I think this is fine, no one is going to look at your beautiful creations and see that you didn’t fully understand some maths. If it looks cool, it looks cool. We are making art here not publishing a research paper!

Anyway, onto the code...

To draw our character’s body, we are going to:

  1. Plot equidistant points around the circumference of a circle
  2. Add a little randomness to the 
    {x, y}
     values of each point
  3. Draw a smooth curve through all of the points

The code for this can be added to a 

drawBody()
 function on our BlobCharacter class (all code is commented to outline its functionality in-context):

...
drawBody() {
  // choose a random number of points
  const numPoints = random(3, 12);
  // step used to place each point at equal distances
  const angleStep = (Math.PI * 2) / numPoints;

  // keep track of our points
  const points = [];

  for (let i = 1; i <= numPoints; i++) {
    // how much randomness should be added to each point
    const pull = random(0.75, 1, true);

    // x & y coordinates of the current point
    const x = this.x + Math.cos(i * angleStep) * (this.size * pull);
    const y = this.y + Math.sin(i * angleStep) * (this.size * pull);

    // push the point to the points array
    points.push({ x, y });
  }

  // generate a smooth continuous curve based on the points, using bezier curves. spline() will return an svg path-data string. The arguments are (points, tension, close). Play with tension and check out the effect!
  const pathData = spline(points, 1, true);

  // render the body in the form of an svg <path /> element!
  this.svg
    .path(pathData)
    .stroke({
      width: 2,
      color: '#000'
    })
    .fill('transparent');
}

Once added to the class, it can be called like so:

character.drawBody();

Ok! Drumroll, please...

If you check out your browser / CodePen window you should now be seeing an awesome random blob shape. If you refresh your browser or re-run the code, you should hopefully see a new shape each time!

We are making generative art!

Note: the 

spline()
 function

The 

spline()
 function you see here is another incredibly useful utility. It simply draws a smooth curve through a set of 
{ x, y }
 points. The shapes it creates should always “close” perfectly leaving you with a very satisfying, natural end result. The technical name for it is a Catmull-Rom spline.

You can find the source code for the version I created here. Thanks to https://twitter.com/cassiecodes for introducing me to the magic of splines 🙌

Drawing the eyes 👀

OK, so we have an awesome organic blob shape. We could almost stop here. You see these blobs all over the web and they can be an incredibly versatile design asset. A quick Dribbble search should show you a few examples!

We should add some googly eyes though. Everything looks better with googly eyes.

Let’s add a 

drawEye() 
function to our
BlobCharacter
class:

// x position, y position, radius / size
drawEye(x, y, size) {
  // create a new svg <group /> to add all the eye content to
  const eye = this.svg.group();
  // <group /> elements do not have an x and y attribute, so we need to "transform" it to the right position
  eye.transform({ translateX: x, translateY: y });

  // add the outer ring of the eye (an svg <circle /> element) to our eye <group />
  eye
    .circle(size)
    // cx / cy are the { x, y } values for the svg <circle /> element
    .cx(0)
    .cy(0)
    .stroke({
      width: 2,
      color: '#000'
    })
    .fill('#fff');

  // add the inner part of the eye (another svg <circle /> element) to our eye <group />
  eye
    .circle(size / 2)
    .cx(0)
    .cy(0)
    .fill('#000')
}

This isn’t going to do too much right now. Let’s add another

drawEyes()
function that will call 
drawEye()
with some values.

drawEyes() {
  // ensure the width of two eyes never exceeds 50% of the characters body size
  const maxWidth = this.size / 2;
  // if a random number between 0 and 1 is greater than 0.75, the character is a cyclops!
  const isCyclops = random(0, 1, true) > 0.75;
  // the size of each (or only) eye.
  const eyeSize = random(maxWidth / 2, maxWidth);

  if (isCyclops) {
    // draw just 1 eye, in the centre of the character
    this.drawEye(this.x, this.y, eyeSize);
  } else {
    // draw 2 eyes, equidistant from the centre of the character
    this.drawEye(this.x - maxWidth / 2, this.y, eyeSize);
    this.drawEye(this.x + maxWidth / 2, this.y, eyeSize);
  }
}

We can then call 

drawEyes()
 in the same way as 
drawBody()
:

character.drawEyes()

If you check out your browser now you should have the awesome blob body from before but with some fresh new googly eyes (or just one eye) attached. Nice!

Now is a great time to inspect the DOM and have a poke around the 

<svg />
 element that contains all of the parts of our blob character. You should see something like this:

This is one of the great things about using SVG for generative art. It is super easy to debug/visualise as you have a visual DOM tree to explore.

Inspecting the 

<svg />
 element should highlight what 
svg.js
 has been doing for us all this time. It is simply simplifying the dynamic creation/ updating of SVG DOM elements. This can get quite wordy without a library.

Time to bust out the crayons 🖍️

Our character is looking awesome. It’s got tons of character, but I think it would be cool to add some color. You could leave it black and white if you like, though. It’s got a kind of cool kawaii sketch vibe this way.

A simple approach to introducing some color here is to define a

primaryColor
, a 
lightColor
 to replace 
#fff
 and a 
darkColor
 to replace 
#000
.

The 

darkColor
 and 
lightColor
 values are both tinted with the
baseColor
. This is something I do a lot and think it’s a nice trick to make your color palettes feel coherent. It can work great in a UI context too.

Let’s set the color values in a new 

setColors()
 function:

setColors() {
  // random hue 
  const hue = random(0, 360);
  // random saturation, keeping it quite high here as a stylistic preference
  const saturation = random(75, 100);
    // random lightness, keeping it quite high here as a stylistic preference
  const lightness = random(75, 95);

  // base color
  this.primaryColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
  // almost black, slightly tinted with the base color
  this.darkColor = `hsl(${hue}, ${saturation}%, 2%)`;
  // almost white, slightly tinted with the base color
  this.lightColor = `hsl(${hue}, ${saturation}%, 98%)`;
}

I always use HSL for colors as it I find it intuitive and easy to modify in a generative context. As displayed in the above code snippet, I am choosing random H/S/L values and combining them using JavaScript template literal strings.

We can then call 

setColors()
 in the BlobCharacter constructor:

class BlobCharacter {
  constructor(width, height, target) {
    ...
    this.setColors();
  }
}

Once the colors are defined, we can apply them throughout the code:

  • this.primaryColor
     in place of 
    transparent
     for the body fill
  • this.darkColor
     for all occurrences of 
    #000
  • this.lightColor
     for all occurrences of 
    #fff

Finally, we can set the base 

<svg />
 background color to
this.lightColor
 to create a colorful backdrop for our character:

class BlobCharacter {
  constructor(width, height, target) {
    ...
    this.setColors();

    this.svg.node.style.background = this.lightColor;
  }
}

Your character should now look something like the following image. Remember, the colors and shape will be different each time!

Variety is the spice of life 🌶️

Our character design is finished! There is one last thing we could add though...

Right now we only see one example of a character. It would be good to demonstrate the generative aspect of the piece a little more. We can do this by periodically regenerating and rendering our character.

In order to do this, let's wrap all the drawing functionality into a single

draw()
 method on the BlobCharacter class:

draw() {
  // clear the <svg /> element
  this.svg.clear();
  // generate new colors
  this.setColors();
  // set the svg background color
  this.svg.node.style.background = this.lightColor;
  // generate a new body shape and render it
  this.drawBody();
  // genearte new eye(s) and render them
  this.drawEyes();
}

We can then call this method rather than running

drawBody()
 and
drawEyes()
 directly after creating our 
character
 instance. We can then continue to call this method every 1.5s to create a new character:

// viewBox w x h, target to append the <svg /> element to
const character = new BlobCharacter(200, 200, document.body);

// draw the initial character
character.draw();

setInterval(() => {
  // every 1.5s after the initial render, draw a new character
  character.draw();
}, 1500);

Hopefully, you will now see something like this...

That’s all folks! 👋

Hooray, we made it! Hopefully, you have got to the end of this tutorial and created a cool character, whilst learning some stuff about generative art in the process.

If you got stuck at any point, check out the final example code as a reference or leave a comment here. I'm always happy to help!

If you did enjoy this post, please follow me on Twitter @georgedoescode and/or on CodePen @georgedoescode.

I’m always posting little generative experiments, and I plan on publishing an article every 2 weeks from here onwards.

Next steps ➡️

There are a bunch of places you could take this from this point: animation, different eye types, different body shapes, a custom blob generator etc, and I would love to see anything you end up creating!

If you do end up creating anything that you would like to share, add the hashtag #generativeBlobStuff to your CodePens / tweets / whatever!

Thank you very much for reading!

Also published at https://dev.to/georgedoescode/tutorial-generative-blob-characters-using-svg-1igg

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.