Generating Pixel Profile Pictures with Cellular Automata using Node.js and StdLib

Profile picture or Rorschach test?

A typical user profile page is hardly complete without a profile picture. Before a user uploads their profile picture, they are generally offered a default image. Though simple silhouettes are common as a default, we’ve all seen more interesting takes in the wild. An example is the GitHub identicon. They generate these profile pictures by hashing the username so that everyone gets a unique picture, then they walk the hash and toggle bits in the five by five grid depending if the value is even or odd. The bits are also mirrored along the vertical axis in order to preserve symmetry.

At StdLib, we took inspiration from GitHub and built something similar. Starting with a hash of a username, we turn it into a two-dimensional array where cells exist in one of two states: either ‘alive’ or ‘dead’. Next, we use this matrix as the starting configuration for a life-like cellular automaton. Life-like cellular automata are computational models that use simple rules to manipulate two-dimensional grids of binary state entities (0 or 1, ‘alive’ or ‘dead’) over time. These cellular automata generate interesting emergent shapes and entities, as you may be familiar with from playing Conway’s Game of Life.

We run the automaton for a fixed number of iterations, mirror the results along the vertical axis, and then save it as a PNG. We’ve called the resulting image an ‘automicon’. If you’d like to skip ahead to the end result, you can use the Automicon API directly on StdLib.

Automicon API page

Without further ado, let’s get to building our own!

Life-like Cellular Automata

The Gosper glider gun, the first known finite pattern with unbounded growth

Before jumping into some code, its worthwhile to talk a little bit about cellular automata. Cellular automata, popularized by Conway’s Game of Life, are computational models that use simple rules and starting states to create a changing environment of cells over time. A cellular automaton has the following properties:

  • A space for the cells to exist in.
  • A set of allowed states for each cell. An assignment of a state to every cell is called the configuration.
  • A neighborhood function that defines which cells are considered when passing information to a given cell.
  • A transition rule which specifies how given a cell and the states of its neighbors, a new state is produced.

A cellular automaton is said to be Life-like if it meets the following four criteria:

  • Its space of cells is two-dimensional.
  • It has two states (called alive and dead here)
  • The neighborhood of a cell consists of its eight immediate neighbors (Moore neighborhood)
  • The transition rule for a cell is expressed as a function of the number of cells in its neighborhood that are alive and the cell’s own state

The rules are often defined with a simple format called rulestrings. Rulestrings are typically given in B/S notation. A B/S rulestring like B3/S23 (also known as Conway’s Game of Life or simply Life) means that a dead cell needs three alive neighbors to be born, and an already alive cell needs two or three neighbors to survive to the next generation. An alive cell that has too many neighbors dies of overpopulation and one that has too few dies of loneliness. From the initial configuration, we just apply the rules for some number of iterations. The result is the final states of the cells.

While Conway’s rules are the most popular, for the purposes of an automicon, they are probably not the best to use. The results are often described as chaotic, and a bad starting state could cause most, or all, of the cells to die quickly from under or overpopulation. Luckily, there are 2^16 possible rulestrings, so one is certain to produce consistently good results. Specifically, Life without Death (rulestring B3/S012345678 ) is a great candidate. B3 means that automaton grows in a similar manner to Conway. But, with S012345678 there is also no possibility of patterns shrinking because cells cannot die. This (almost) guarantees that there will always be something in the picture at the end.

Now that we’ve briefly covered the theory behind the automaton, we can look at the actual implementation. The starting state for the automaton is generated from a hash of a username.

Hashes a string and splits the results into an array of hex characters

Next, we take each character of the hash. If it’s below a certain threshold we mark it as alive, otherwise, it is dead. This is similar to checking the parity of the value but it also allows us to vary the initial amount of alive cells by raising or lowering the threshold.

Assigns a state to each cell based on the value of a hex character

Finally, we return this array of cells as a two-dimensional array.

Now with the starting state set, the simulation can be run using the rulestring B3/S012345678.

A simple cellular automaton implementation that takes an initial configuration, number of iterations, and rulestring (as a two-dimensional array)

After running the simulation, the results are a four by eight matrix of alive and dead cells. In order to guarantee some symmetry, we turn it into and eight by eight array by flipping it along the vertical axis. This grid is then walked through, and for each cell a square is written to a PNG, giving the final picture.

Naturally, we turned this function into a StdLib service. We expose a single endpoint, that takes one argument, seed. We use our usernames for the seed, but it can be any string. Both the hash and the automaton are deterministic, so the function will always return the same picture given the same string. It’s also important to note that, like all APIs on StdLib, this is a “serverless” function. This means that if you experience huge growth, the service will scale seamlessly.

If you’d like to check out the full source code, it’s on GitHub. You can clone the repo into a StdLib workspace and run $ lib up dev to start playing around with it. There are many interesting ways you could expand upon this concept. You could try one of the many different rulestrings, some of which have really interesting properties (for instance Life Without Death can simulate arbitrary boolean circuits!). You can also try having more, but smaller, squares make up the picture or you could let the automaton run for longer.

@keithwhor, @Hacubu ‏, and my own automicons

Thanks for reading! A profile picture generation service is just one of the many ways you can leverage StdLib’s tools. If you would like to get started with StdLib you can create a free account from our website, or from our command line tools.

If you have a neat idea you’d like to share, reach out to me directly by e-mail: steven@stdlib.com, or follow us on Twitter, @StdLibHQ.

We look forward to hearing from you, and happy building!

Resources

[1] http://www.conwaylife.com/w/index.php?title=Gosper_glider_gun

[2] http://stars.martinpelikan.net/wp-content/uploads/2012/07/david_final_paper.pdf

[3] http://conwaylife.com/wiki/Cellular_automaton