Github has these default avatars they call identicons that are 5x5 grids with a single color. They are generated using a hashing function and a username as a seed. Despite the grid being 5x5, they are always symmetrical, so the grid is actually 3x5 with column 4 being the same as column 2 and column 5 being the same as column 1.
A 3x5 grid has only 15 squares, and a square is either on or off. It dawned on me one day that the total number of permutations isn't actually that large. 2^15 = 32,768.
As a hobbyist generative artist, this screamed small multiples to me.
Generating an avatar
An avatar can be thought of as a 15 digit binary number, where each bit represents one of the fifteen squares in the 3x5 grid. Then the first two columns are reflected across the middle column.
avatar = 100 010 001 010 001 1 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 1
Using Processing (with Python instead of Java), this binary avatar can be turned into lovely rectangles using the following set of loops and bitwise operations:
def avatar(seed, avatarSize = 50): paddingSize = floor(avatarSize * 0.05) tileSize = ceil(avatarSize * 0.9 / 5) noStroke() # Set a gray background fill(0, 0, 245) rect(0, 0, avatarSize, avatarSize, paddingSize, paddingSize, paddingSize, paddingSize) # Choose a randomish color for the avatar fill(random(255), random(25, 75), random(175, 225)) pushMatrix() translate(paddingSize, paddingSize) for i in range(5): for j in range(3): # If the bit in this square's position in the seed int is flipped, then draw a square if seed & (1 << (i * 3 + j)): rect(tileSize * j, tileSize * i, tileSize, tileSize) # For the first two columns, also draw a square in the reflected column if j != 2: rect(tileSize * (4 - j), tileSize * i, tileSize, tileSize) popMatrix()
Generating all the avatars
Since generating an avatar has been reduced to a binary seed, generating all the avatars is as simple as iterating over all the binary values between
000 000 000 000 000 and
111 111 111 111 111. Since binary is just a base 2 number, we can instead think of these seeds as base 10 numbers. This makes the range
for i in range(32768): avatar(i)
This will work for generating every single avatar, but I also wanted to see everything at once. I wanted the small multiples. Withouth thinking too hard, I just found a couple reasonable factors of 2^15 to make a nice arrangement. 2^7 × 2^8 = 2^15. Good enough!
pushMatrix() translate(50, 50) for x in range(128): for y in range(256): pushMatrix() translate(23 * x, 23 * y) avatar(y * 128 + x, 22) popMatrix() popMatrix() save('tiles-full.png')