Generative blob characters using SVG!
Tags: webdev, tutorial, javascript, codepen
Published: 2021-01-11 13:23:15

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:

{% codepen https://codepen.io/georgedoescode/pen/dypjOgv %}

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](https://www.tate.org.uk/art/art-terms/g/generative-art) —

> “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.

- https://svgjs.dev/docs/3.0/ (used add / remove / modify SVG elements such as `<circle>`)
- https://www.npmjs.com/package/@georgedoescode/spline (used to draw smooth curves through multiple points)

I have set up a CodePen that you can fork [here](https://codepen.io/georgedoescode/pen/29d323ad399e2f3b383dbc3c117b5a92) 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: 

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

They can then be imported into your JavaScript like so:

```javascript
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. 

```css
* {
  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:

```javascript
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: 

```javascript
// 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 200 x 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](https://css-tricks.com/scale-svg/)

---

## 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 200x200. Here's an infinitely useful utility function that we can use to generate a random number within a range:

```javascript
// 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: 

```javascript
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)_: 

```javascript
...
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: 

```javascript
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!

![An organic blob shape](https://dev-to-uploads.s3.amazonaws.com/i/ls5usjlbmrr96bmnnrvw.png)

**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](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline). 

You can find the source code for the version I created [here](https://github.com/georgedoescode/splinejs). 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](https://dribbble.com/search/blobs) 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: 

```javascript
// 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.

```javascript
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()`:

```javascript
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! 

![An organic blob shape with googly eyes added](https://dev-to-uploads.s3.amazonaws.com/i/z205exdukm8n1oxpt32y.png)

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: 

![A screenshot of an SVG DOM node](https://dev-to-uploads.s3.amazonaws.com/i/s279mk5860mi4igha60e.png)

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: 

```javascript
  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:

```javascript
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:

```javascript
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!

![A colorful blob character](https://dev-to-uploads.s3.amazonaws.com/i/6z9ppgq4pl7oimxnf622.png)

---

## 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: 

```javascript
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: 

```javascript
// 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...

![A gif showing different blob character generations](https://dev-to-uploads.s3.amazonaws.com/i/0k8pbrs3to4i0nf79hro.gif)

---

## 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](https://twitter.com/georgedoescode) and / or on CodePen [@georgedoescode](https://codepen.io/georgedoescode).**

**You can also support my tutorials by [buying me a coffee ☕](https://ko-fi.com/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!