DEV Community

Sivasubramanyam A
Sivasubramanyam A

Posted on • Originally published at siva.dev

How are identicons generated?

We have seen them everywhere. From GitHub to Stack Overflow, identicons have been the face of users who do not have an associated Gravatar or a profile photo.

Identicon

I wanted to figure out how these were generated and came across this blog post by GitHub where they announced the introduction of identicons on their site.

Our Identicons are simple 5×5 “pixel” sprites that are generated using a hash of the user’s ID. The algorithm walks through the hash and turns pixels on or off depending on even or odd values. These generated patterns, combined with hash-determined color values, ensures a huge number of unique Identicons.

While the identicon itself is a 5x5 image, one can notice that it is symmetrical. The fourth and fifth columns mirror the second and first columns respectively. This implies that it's sufficient to express these in a 3x5 matrix. So, a 15 digit(3 x 5 = 15) sequence is sufficient to represent an identicon. As for the color, we can use a 6 bit sequence from this 15 bit number itself and generate a color code in the hex format.

Let's try and build this now.

The 15 digit sequence has to be based on the given string and should be reproducible. Let's use hashing to solve this. We can use this example from MDN to generate the hash. The SHA-1 algorithm shouldn't be used for anything serious but since this is a fun little project, let's go with it.

function hexString(buffer) {
  let byteArray = new Uint8Array(buffer);

  let hexCodes = [...byteArray].map(value => {
    let hexCode = value.toString(16);
    let paddedHexCode = hexCode.padStart(2, '0');
    return paddedHexCode;
  });

  return hexCodes.join('');
}

function getHash(message) {
  let encoder = new TextEncoder();
  let data = encoder.encode(message);

  return window.crypto.subtle.digest('SHA-1', data);
}

async function handleInput() {
  let userInput = document.getElementById('message').value;
  let hash = hexString(await getHash(userInput));

  console.log(hash);  // logs f10e2821bbbea527ea02200352313bc059445190
}
Enter fullscreen mode Exit fullscreen mode

Now, we need to convert this 40 character long hash into a 15 bit sequence.

Grid Placement

In the above matrix, the value of n at position (i, j) is given by i*3 + j. We can then simply look at the nth position of the hash for 0 <= n <= 14.

This can be represented in code as:

for (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    let n = hash.charAt(i*3 + j);
  }
}
Enter fullscreen mode Exit fullscreen mode

To determine the mirrored values to fill the fourth and fifth columns, we can modify the above code to:

for (let i = 0; i < 5; i++) {
  for (let j = 0; j < 5; j++) {
    let J = j > 2 ? (4 - j) : j;
    let n = hash.charAt(i*3 + J);
  }
}
Enter fullscreen mode Exit fullscreen mode

Using the above code, we have arrived at the hash f10e2821bbbea52. We now need to find a way to use this hash to determine which of the boxes in the grid have to be filled. One trivial solution to this is to take each of these characters and check if it is divisible by 2 in the decimal form.

for (let i = 0; i < 5; i++) {
  for (let j = 0; j < 5; j++) {
    let J = j > 2 ? (4 - j) : j;
    let n = hash.charAt(i*3 + J);
    let shouldFill = parseInt(n, 16) % 2;
  }
}
Enter fullscreen mode Exit fullscreen mode

All that's left is to determine the color of the fill. While we can just use a sequence of 6 digits from the hash f10e2821bbbea52 that we generated above, let's use some of the extra characters from the 40 character long hash that we generated previously.

function getColor(hash) {
  return `#${hash.slice(-6)}`;
}

let color = getColor('f10e2821bbbea527ea02200352313bc059445190');
console.log(color);  // logs #445190
Enter fullscreen mode Exit fullscreen mode

Now putting this all together using some JavaScript and HTML Canvas, we get a working generator that produces identicons similar to the ones generated by GitHub.

Top comments (1)

Collapse
 
laurentpayot profile image
Laurent Payot

I’ve just discovered this post, great read.
I generate SVG identicons with a custom synchronous hash function: github.com/laurentpayot/minidenticons

Cheers,
Laurent