Generate 16 unique avatar faces from 40 lines of easy code

Generate 16 unique avatar faces from 40 lines of easy code
Mads Cordes's photo
Mads Cordes

Published on Oct 17, 2021

6 min read

Subscribe to my newsletter and never miss my upcoming articles

my-avatars


In v1.0.0 I've added the ability to change the color of images. The updates are specified at the bottom of this post. Link.


NB. Thanks to Lucas for this comment. The math is now correct 🥳

If anything is wrong, please do not hesitate to correct me either 😍

I've for a while had an idea to create avatars like getavataaars.com and such other services. Though I don't have the necessary graphical skills nor the imagination, but what I do have is code experience.

Thus, I decided to create a npm-package for fun. This is not the most useful package, but it's a package nonetheless. It's been quite a while since I created my last package, and to be honest, none of my packages are worth anything 🤷 But you'll only better by trying!

Why only 16?

Well, to answer this question shortly, this is because I'm not good at creating graphics, nor am I not enjoying doing so. But I do enjoy to code. So I threw together a small face made in (inkscape)[inkscape.org/], this wasn't enough, so I decided to create another, much uglier, one too.

Gave me the option to pick from one of two faces. Easily! But is two enough? Nop, and now that I had two faces to choose from, I decided to create a library, that could take the individual unique pieces from each face, and generate a new unique one!

But how did I come to the 16 unique faces? Well, the math is simple. I have four (4) item-collections, each with two (2) items in them. By using these collections, I am able to generate 16 completely unique avatars. This can be calculated by 2 * 2 * 2 * 2 which equals to 16.

How about another?

If I would add another, say, mouth the equation would be 2 * 2 * 2 * 3 which now equals to 24! That's an increase of 50%!

How about that. One more mouth and an 50% increase. That's not too bad.

One more of each?

What if, on the other hand, I'd add another face, pair of eyes, a nose and a mouth. Then the equation would be 3 * 3 * 3 * 3 which equals to 81! This an 406.25% increase of unique faces! Whoop.

More

You can probably imagine how the equations only go up from there. Add two more mouths, a pair of eyes and five funny noses. No new faces added here. The equation is now: 3 * 4 * 8 * 5 which equals to 480 unique avatars!

Holy moly! This is an increase of 2900% from the original 16. And it only goes up from there.

A piece a day, keeps the repetition away

What if we made an experiment, and wanted to make 2500 unique faces by the end of November. But didn't want to start until November 1st?

  • By the end of the first day you only have a single piece, which is one (1) unique face.
  • By day two (2) you'll still have one (1) unique face, as a face needs at least four (4) items to be complete, thus you'll need four (4) days to generate one unique face.
  • On the fifth (5) day, you have two (2) unique faces!
  • On the sixth (6) day, you have four (4) unique faces!
  • ...
  • On the ninth (9) day, you have 24 unique faces!
  • ...and so fourth...

Thus, in these 30 days you'll have 2500 unique avatars! That's a great start.

Imagine if you had a great day, and made two (2) or three(3) pieces instead of one (1)! This could easily become 15000 unique faces!

Code

Enough talk, show me some code, please!

To use this library, you can choose to use my library my-avatars, which can be downloaded from npm. Or take a look at the source code on github.

Anyways, let's get to the code.

// import the library and take the important classes from it. 
import { Generator, Layer, Item } from 'my-avatars'

As you can see, there is only three (3) things to import, these will make up the complete generator at last.

// create two face items from the example images.
const faceItems = [
  new Item('./images/face1.png'),
  new Item('./images/face2.png'),
]

// add some eyes.
const eyeItems = [
  new Item('./images/eyes1.png', { y: 50 }),
  new Item('./images/eyes2.png', { y: 50 }),
]

// a few noses.
const noseItems = [
  new Item('./images/nose1.png', { y: 200 }),
  new Item('./images/nose2.png', { y: 200 }),
]

// and last, but not least. The mouths.
const mouthItems = [
  new Item('./images/mouth1.png', { y: 270 }),
  new Item('./images/mouth2.png', { y: 270 }),
]

Take a look at the second argument, it states where to draw the item. Also note that not all of the has it. Making it optional.

Lot's of repetition, I'll have to figure a better way, but that will come soon enough.

Next up, we'll create the layers.

// create four new layers.
const faceLayer = new Layer('faces', 0)
const eyeLayer = new Layer('eyes', 1)
const noseLayer = new Layer('noses', 2)
const mouthLayer = new Layer('mouths', 3)

Keep in mind the latter number, it's like the z-index in css, it's super important. The first argument, on the other hand is just for your own convenience. This will be thrown in an error, if one occurs.

Now, let's add the items to the layers.

// add each items to the corresponding layer.
faceItems.forEach((x) => faceLayer.addItem(x))
eyeItems.forEach((x) => eyeLayer.addItem(x))
noseItems.forEach((x) => noseLayer.addItem(x))
mouthItems.forEach((x) => mouthLayer.addItem(x))

Now for the last, but still important part.

// fetch where to put the generated avatar.
const app = document.getElementById('app')

// create the generator with 400x400 avatars, and put in the parent elm.
const g = new Generator(400, 400, app)

// add each layer to the generator.
g.addLayer(faceLayer)
g.addLayer(eyeLayer)
g.addLayer(noseLayer)
g.addLayer(mouthLayer)

Now for that all of that boilerplate is out there, let's get to the fun part!

g.draw()

Boom! You've drawn a new, unique, avatar!

Congratulations! 🥳

NB. The repo with everything is right here.


v1.0.0

As per v1.0.0 you can add now manipulate colors, it's not fast but it's there.

The API documentation goes as:

  • Item
    • new Item(path, opts)
      • path is the path to the image
      • opts
        • x: Number - x-coord to place the image
        • y: Number - y-coord to place the image
        • check: Boolean - check if the image can be found - required to replace-colors
        • replace: Array of Object's - filled with colors to replace from and to
          • from: String - color to replace from, in the format of r,g,b - e.g. 128,128,128
          • to: String - color to replace to, in the format of r,g,b - e.g. 128,128,128
  • Layer
    • new Layer(name, idx)
      • name: String - a friendly name to recognise
      • idx: Number - the z-index of the layer
  • Generator
    • new Generator(width, height, parentElm)
      • width: Number - width of the avatar
      • height: Number - height of the avatar
      • parentElm: Element - element to place avatar within

{ Best, Mads Bram Cordes }

 
Share this

Impressum

Programming and things

Proudly part of