This project, Animorphs (named after the book series about teenagers who can morph into different animals), is the first of several experiments to figure out what's possible.
The Anatomy of an Animorph
At a high level, these entities are drawn by connecting multiple Bezier curves together, filling it in with black, then drawing its mirror image to ensure the final entity is symmetrical.
To show step by step how this works, we'll walk through how the following animorph is drawn:
The first thing to understand is that this algorithm is only responsible for the left half of the entity. Once it's rendered, the right half is drawn as a mirror image of the left half. Here's the same figure but with the right half rendered in a different color:
Therefore, we'll just focus on the left half that the main part of the algorithm is responsible for:
This entity is comprised of three Bezier curves:
The first curve, the red curve, starts near the bottom of the canvas. The second curve, the green curve, starts where the red curve ends and the final curve, the blue curve, starts where the green curve ends. When we fill in the shape, the final point (the end of the blue curve) connects to the starting point (the start of the red curve) which is why in the filled in half above you see a straight line.
Here's what both sides look like when drawn but not filled in:
The Animorph Genome
This animorph's appearance is determined by one chromosome per curve where each chromosome consists of six genes.
For example, the biomorph above (which consists of three curves) is created from the following genome:
[ -21, 21, -98, -168, -98, 56 ], [ 147, -77, 49, 133, 140, -7 ], [ -126, -126, 168, 98, 154 ] ]
The [ -21, 21, -98, -168, -98, 56 ] chromosome renders the first curve:
Here's how it works:
A cubic Bezier curve is represented by four points: one start point, two control points, and one end point. However, the first curve in an animorph starts at a fixed position and subsequent curves start at the end of the last curve, so we really only need three points to represent a single curve. Each point consists of an X and a Y component and since we only need three points per curve, we need six values to represent a single curve.
You can think of the [ -21, 21, -98, -168, -98, 56 ] chromosome like this:
Control Point 1 is represented by (-21, 21)
Control Point 2 is represented by (-98, -168)
The End Point is represented by (-98, 56)
These values are used to calculate how far from the last point in the curve the next point is. The Start Point for this curve is fixed at (300, 230). To figure out the coordinate of Control Point 1 on the first curve, we add (-21, 21) to the Start Point, putting it at (279, 251). Control Point 2 is (-98, -168) away from Control Point 1, putting it at (181, 83). Finally, the End Point is (-98, 56) away from Control Point 2 putting it at (83, 139).
We repeat this process for the rest of the curves in the left side of the entity before drawing the mirror curves and filling it in.
While the animorphs in this project appear to be evolving, they're not really evolving in the biological sense of the word.
When you hit Start, the animorph generates a random set of chromosomes for a new entity (the target). The animorph then looks at which of its genes differ from the new entity's genes and then it moves them closer one at a time until its genes perfectly match the target entity's genes. Once its genes match the target entity's genes, it picks a new random entity to work towards. It repeats this process indefinitely until you pause it or close your browser window.
This process differs from biological evolution in many ways, notably:
- In evolution, there is no target; organisms survive based on how well suited they are for their environment
- In evolution, genes mutate randomly; in this project the target is chosen at random and then the animorphs move step by step towards that target
- In evolution, there is reproduction; here we're just modifying a single animorph's genes for the duration of the animation
I did try to address these issues in the first version of this project, but the animation wound up being very choppy so I simplified things a bit for the sake of the visualization.
Future projects will address these points so stay tuned :)