Linear algebra has been on my list of things to learn for a while. Finally taking the time to do so in the context of building this tool was a wonderful, multifaceted experience. I wanted to reflect on it. Needless to say, I’m neither a developer nor a mathematician. I’m sure I’ve overlooked a lot of stuff. Yet informally wandering through these fields is always a delight. The problems they address are captivating and their models for solving problems are mind-bendingly helpful.
The initial problem I was trying to solve was getting more control of masks. I use track mattes a lot, but would find myself trying to use masks instead because they don't require an extra layer. The tradeoff was losing any parameters to animate. I wished for a hybrid, a parametric mask, that could even dynamically fit to its layer. I figured it wouldn't be too hard to build...
As I muddled along, the initial, seemingly compact problem of parametric mask animation presented a compelling set of progressively lower level problems. The endeavor effectively became to build a pseudo shape layer from the bottom up. This meant learning the basic yet requisite math, programming, and computer graphics.
One of the challenges I faced, rotation, had been puzzling me for years. In After Effects, moving a shape in a straight line is as simple as increasing or decreasing the position value. And this simplicity translates 1:1 to moving a shape mathematically using expressions. Rotation is a bit more complex.
To rotate a shape around a point or move it in an arc, a common method is to parent it to the point around which it should rotate. In one sense the shape is not actually rotating. Rather, it is the coordinate space of its parent that rotates. As a child of a layer, an object is located within its parent’s XY plane. It retains its XY position no matter the changes to its parent’s plane. So when the child shape appears to move in an arc, it is the parent’s plane that is rotating.
Rotating a shape around its own displaced anchor point is the same process. It’s the coordinate system that is rotating, resulting in the shape moving along an arc. However, doing it this way means any additional transformations will reference that displaced anchor.
Expressions do not provide a “parent” function that can place the shape within some arbitrary coordinate system and then rotate that coordinate system to move the shape around a point. Instead, rotating a shape mathematically is usually done with (just) trigonometry. This method preserves both the ability to transform the shape from its anchor and add value to the position to move linearly. To keep the shape oriented along its arc, however, a second expression is needed on its rotation property. Despite the sufficiency and robustness of trigonometry alone, the elegance of rotating a coordinate space still appealed to me. And the puzzle continued to nag at me.
Why couldn’t I just rotate an arbitrary coordinate system? It’s so intuitive and easy through parenting. What math was eluding me? The answer, linear transformation, is a fundamental concept in computer graphics. On the technical side of motion graphics, I was seeing some linear algebra regularly, but linear transformation and matrix math were not as common. After Effects supports
toWorldVec() out of the box; matrix math is not included. Eventually I realized a fuller understanding of linear algebra was the answer, and added it to my list of things to learn.
At the beginning of this project, I knew I wanted as much feature parity as possible with native After Effects shape layers. Position, scale, rotation, anchor point, and round corners. And I still wanted to put off learning linear transformations (bad idea). Any one of these alone would’ve been relatively easy, but putting them all together was not. In particular rounding rotated corners was so hard I wasn’t sure if it was worth it. But if I could figure it out, a circle shape would be thrown in for “free.” I plodded away and wrote a working version using only trigonometry. Got a circle. And a scaled circle would also yield an ellipse, right? Unfortunately, the tangents were not respecting my scaling method. And that finally brought me face-to-face with linear transformations.
Linear transformations work exactly like the parenting approach. They transform the coordinate space of an object. Beautifully, the math supports stacking different types of transformations. This quality turned out to be the key to correctly and seamlessly scaling the tangents of rounded corners regardless of rotation. A transformation, like scaling or rotation, can be applied to a shape by doing matrix multiplication. For example, to scale and rotate a shape, multiply the shape’s points (vectors) by the scale matrix, and then multiply that result by a rotation matrix. For efficiency, multiply all the transformations together to get a composite matrix, then just multiply the shape by that composite transform. I wrote all the matrix multiplication functions, made sure the math checked out and the shape was transforming properly. So far, so good. The idea that I could multiply two transforms together and pass a composite on a shape seemed… unrealistic. At best I anticipated a lot of debugging. To my surprise, it just worked. It felt like magic. As math so often does.
One of the other reasons I wanted to develop this is because I appreciate the power of
createPath() but spending time to wrangle it during production can be a risky exchange.
When After Effects opened up access to path points several years ago, it was very exciting. Customers had been clamoring a long time. While the method to access them is indeed powerful, it is also an expression method and somewhat challenging to use. After Effects ships with a helper script that uses
createPath() to link path points to null layers. This approach is helpful, but it also generates lots of layers. Accessing points and tangents on those paths requires three layers for every point.
I only saw this method as the problem framed by the helper script:
createPath() is a difficult way to access and animate path points. Not coincidentally, I tend see After Effects itself as lots of specific problems with niche solutions and workarounds, usually taking the form of scripts or expressions.
I felt this tension or irony that I had been using expressions to solve problems ad hoc, yet here was a fully functional “shape path expression” demonstrating the potential to build low-level tools directly inside After Effects (even without any external scripting). I looked back at my initial problem through this experience; it was re-framing not only how I saw that problem, but also my conception of After Effects as a tool.
In particular I realized the flexibility of
createPath() is not necessarily in the path part. It’s in the points. It allows users to return an arbitrary set of points and process them with a relatively full-featured programming language. (This may be possible with a text layer, but it would require converting text to numbers and feels even more hacked.) Those extra tangent arrays, which at first kept throwing me for a loop, could now be thought of as arbitrary point attributes. That, surprisingly, feels a lot like Houdini. And the goal is not necessarily to be like Houdini, but to be aware of both how a tool forms a user’s approach to problem solving as well as how the experience of using it reflexively shapes conceptions of the tool itself, what kind of problems it’s capable or incapable of solving.
Parametric mask animation seems like a basic place to start implementation. (It’s a small thing, but I appreciate having a parametric shape with feathering and expansion built in, plus a shortcut for the feathering.) The main preset and expression assume the expression is on a mask path of a solid. It can go on any any path property, though.
I mentioned in the beginning that I wanted to use automatically sized masks instead of track mattes. I love track mattes; they’re virtually bulletproof. But they require that extra layer. So this shape path expression works nicely with just two other effects to make a clean, effective layer matte. This is the way I’ll probably use it most.
This expression also makes it fairly straightforward to parametrically transform (and round) any set of path points. After Effects provides a free-transform box for mask path points, but it can’t be animated.
Finally, as previously mentioned,
createPath() can return an arbitrary set of points and point attributes (kind of hacky, but in theory it should work). I imagine it wouldn’t be too difficult to build a simple scatter preset (famous last words). Combined with the native
noise(), maybe even become a basic, if clunky, cloner tool (depending on performance).
It was a joy to finally learn linear algebra and glimpse the computer graphics underneath the hood, the principles I rely on every day yet of which I was totally unaware. I hope this is helpful to you. Happy animating! Below is a list of resources.
Essence of Linear Algebra playlist · Grant Sanderson (3Blue1Brown)
And huge thanks to the MDA community!