It started as a relatively simple idea - a p5.js hexagonal Game of Life with a more exciting visualisation of activity - it ended 46 demos later with this:
This post is for a university submission for MA Comp Arts so may be a bit clinical. Skip to demos for the fun stuff.
Game of Life
Demo #1 came about fairly quickly. The greatest difficulty by far was establishing the data structure for the hexagons and defining how they relate to one another. I used a two-dimensional array of objects, where there are twice as many rows as there are columns to make up for the layout. I used the same rules as David Siaw's fantastic implementation, Hexlife, but unlike the original square Game of Life, Hexlife stabalises very quickly so I also added a touch of random genesis to keep it alive.
In #2 I drew lines between the centres of every active hexagon, and in #3 they became curved (quadratic beziers with the center of the hexagon as the control point). The trouble with this kind of cellular automata though, is that it's hard to visualises clusters of activity because everything evolves so quickly. I attempted to solve this in #5 by making the frames semi-transluscent and adding some more visual glitter. I also decided to not display any of the hexagons with just one neighbour because the prevalance of short straight lines looked sharp and inorganic.
Still, the lack of clustering frustrated me. Perhaps cellular automata wasn't the right way to go to determine the activity of the hexagons. I next experimented with using a partical system with boid-like behaviour to activate and deactivate the hexagons, see #9. This was the type of clustering I was aiming for, but as expected performance took a complete nose dive. A simpler system was in order.
Still, the boids persisted for a bit (with a much larger hexagon size to curtail the GPU munching) while I focused on another obstacle to complexity. Currently on every active hexagon a curve was drawn between the middles of every active edge, resulting in a very regular rounded-triangle grid in dense areas. I solved this by matching up specific edges for each amount of active neighbours. This developed over #13, #14, and #15.
For hexagons with four or more active neighbours a randomly-chosen layout determines how it will be displayed. Here is every permutation of the 'central' hexagon:
To smooth the appearance of clustering further I added a transition to the background colour of each hexagon, like in #19.
Time to get back to this boid debacle. I next tried a hexagonal implementation of Langton's Ant, as in #20. The regularity of the animation is interesting, but not the sort of emergent behaviour I was aiming for. And as can be seen in #21, since the ant is either turning left or right every frame, we end up with a lot of circles. In #24 I tried having each ant move every few frames instead of every single frame in an attempt to make it look more random but it just ends up rather sluggish.
Here is where things actually started getting interesting - when I gave up on order all together and started using random agents.
Now every ant would leave a trail behind it and randomly decide to move either left, right, or go straight ahead. I expected an unpalatable clusterfuck of lines, but randomness led to the exact sort of clustering I was striving for. I toyed with the idea of introducing some kind of intelligence to balance out the screen, like the agents vagualy aiming for emptier areas of the screen, but concluded that the loss in performance would not be worth the marginal gain in aesthetics.
Since now we're only activating hexagons and not disabling them, I needed a way to stop the screen from filling up completely, and to establish a density stalemate. In #27 I introduced the 'creator' and 'destroyer' agents, and by #30 decided that we don't need many creators for very interesting visuals, with just one the frame rate is great and the little creator scurries around very quickly.
Time to level up the complexity of the aesthetics. The idea of using shapes instead of colours on a GOL grid was inspired by this example from the Generative Design book. I was enamoured by the visual complexity that can be achieve with a simple set of 16 glyphs.
I wanted something more exciting than just curves between points on a grid, but I do think the lines evoked the smoothness and trail-like quality I was aiming for. I decided instead to add another state to the hexagons. Instead of just disabled or active, it could now be double-active. What is double active you may ask? This is double active:
… in its first iteration. Then came the monumental task of having the single lines diverge into two lines, requiring an overhaul of the line-drawing functions. The criteria was that all the lines inside a double-active hexagon would be doubled, so it's the business of the single-active hexagons to determine whether each active hexagon they're adjacent to is double-active, and to draw the lines accordingly. This came with a bunch of extra complexities, such as:
- Making the double lines have a consistant gap meant moving the bezier control point for the inner line away from the centre of the hexagon.
- Identifying which side of the two edges are the 'inner' ones so the two lines don't cross over.
- Figuring out what to do with the control point when the edges are opposite one another and you want parallel lines, using the
line()function instead made the ends of the lines not quite match up, so it was a matter of calculating the mid-point.
This was starting to look very promising, but I think the double lines emphasised the triangular pseudo-grid of the 2x2 which looks very awkward in contrast to the perfect circles of the 3x3. I decided to shift the origin point closer to the corner for curving between adjacent edges to make more circular forms, and I also added rounded caps for the open ends of double lines, and made the destroyers just decrement the activity and added more of them.
At this point there are an astonishing amount of possible combinations of lines. Over 21 possible edge combinations there are 59 lines, each can be rotated 6 times, and each line can be single, double, or diverging in either direction. 1416 possible combinations for every hexagon!
Interaction time. There are 2 elements to this. The first is that I wanted to be able to control the generation on screen with toggles/buttons that eventually took the form of keyboard shortcuts (perhaps less accessible than having actual buttons on screen? but visual toggles tend to clutter), which I introduced in #41. Trying to decide which key should match up to which function was interesting. As is common practice, the space bar pauses and unpauses the animation. I used 'Q' to kill all the agents, 'W' to wipe the screen, 'C' and 'D' for making new creators and destroyers, and 'T' to toggle the visibility of agents.
The second element is the ability to draw yourself with the mouse. Dealing with what mouse interaction add and removes activity was challenging, but I feel it's got to a state of relative intuition. Broadly; dragging adds, clicking cycles through, and right clicking/dragging removes. The 'layout' is also incremented when the hexagon is activated so you can cycle through all possible combinations just by clicking. Mouse collision detection was pretty simple because I just measured the distance between the mouse and the centre of each hexagon, approximating the shape as a circle, it's accurate enough to not be annoying.
To enhance the utility of it as a drawing tool in the white-coloured version #45 the colour of the hexagon when it has 4+ active neighbours is effected by its layout, making toggling through the layouts more obvious and purposeful. I also added shortcuts to toggle the visibility of the grid ('G') and of the hexagons ('H'), so you can draw something with the aid of the grid and hex colours and the toggle them off to screenshot it. Give it a go:
Here's some of my drawings:
- Hexagons are cool, yo.
- Big picture - hook it up to a 3D printer / laser cutter
- Bigger picture - add a third dimension
- Medium-sized picture - a twitter bot à la @generativebot
- my jewellery designer is showing