Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
January 11, 2021 01:23 pm GMT

Tutorial - Generative blob characters using SVG!

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
Enter fullscreen mode Exit fullscreen mode

They can then be imported into your JavaScript like so:

import { spline } from "@georgedoescode/spline";import { SVG } from "@svgdotjs/svg.js";
Enter fullscreen mode Exit fullscreen mode

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 dont! 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;}
Enter fullscreen mode Exit fullscreen mode

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  }}
Enter fullscreen mode Exit fullscreen mode

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 toconst character = new BlobCharacter(200, 200, document.body);
Enter fullscreen mode Exit fullscreen mode

You wont see anything just yet, and thats 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

All creatures great and small

Now that we have all the boilerplate setup for our character, its 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 circles radius to define our character's size. Dont worry, Im no mathematician and this isnt 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:

// choose a number within a range, integer (whole number) by defaultfunction random(min, max, float = false) {  const val = Math.random() * (max - min) + min;  if (float) {    return val;  }  return Math.floor(val);}
Enter fullscreen mode Exit fullscreen mode

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);  }}
Enter fullscreen mode Exit fullscreen mode

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 characters body. This is probably the trickiest part of the tutorial, but dont worry if its 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 didnt 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 characters 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');}
Enter fullscreen mode Exit fullscreen mode

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

character.drawBody();
Enter fullscreen mode Exit fullscreen mode

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

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.

Lets add a drawEye() function to our BlobCharacter class:

// x position, y position, radius / sizedrawEye(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')}
Enter fullscreen mode Exit fullscreen mode

This isnt going to do too much right now. Lets 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);  }}
Enter fullscreen mode Exit fullscreen mode

We can then call drawEyes() in the same way as drawBody():

character.drawEyes()
Enter fullscreen mode Exit fullscreen mode

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

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

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. Its 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. Its 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 its a nice trick to make your color palettes feel coherent. It can work great in a UI context too.

Lets 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%)`;  }
Enter fullscreen mode Exit fullscreen mode

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();  }}
Enter fullscreen mode Exit fullscreen mode

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;  }}
Enter fullscreen mode Exit fullscreen mode

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

A colorful blob character

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();}
Enter fullscreen mode Exit fullscreen mode

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 toconst character = new BlobCharacter(200, 200, document.body);// draw the initial charactercharacter.draw();setInterval(() => {  // every 1.5s after the initial render, draw a new character  character.draw();}, 1500);
Enter fullscreen mode Exit fullscreen mode

Hopefully you will now see something like this...

A gif showing different blob character generations

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

Im 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!


Original Link: https://dev.to/georgedoescode/tutorial-generative-blob-characters-using-svg-1igg

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To