Introducing Renature: Experimenting with Physics for UI Animation
At the beginning of December Formidable kicked off its inaugural open source fellowship. The Fellowship Program gives one employee per quarter the opportunity to pursue a novel technical idea, full-time, over the course of six weeks. The shape this idea takes is left up to the selected applicant; it can be a new open source library, a design tool like our stunning UX Deck, or technical writing that explores new practices in our ever-evolving field.
The goal of the Fellowship Program is to give our team members time and space to experiment freely, to see what creative and challenging visions they can bring to life in a period of focused study. It's also part of our effort to make open source a sustainable practice available to all Formidables regardless of their commitments outside of work.
For this first fellowship, Parker Ziegler, in collaboration with others at Formidable, is working on a new physics-based animation library for React focused on modeling natural-world forces like gravity, friction, air resistance, and fluid dynamics. The project is called
renature, harkening to the inspiration it takes from the physics of our universe. It's targeted at UI developers, digital artists, and physics nerds alike. In this post, we'll introduce the motivations behind writing
renature, highlight some of the technology we're using to build it, and show off the progress we've made in these first two weeks!
If you're familiar with popular animation libraries in the React ecosystem like
react-pose you may be wondering — why do we need another physics-based animation library for React? Don't these libraries already fulfill the animation needs of most developers building for the web?
react-pose are incredibly powerful animation libraries and impressive feats of engineering in their own rite. However, they focus on one particular physics primitive to power their animation logic — the spring. Spring motion is particularly well-suited to UI animation because it's familiar to the human eye. We see springs working in our everyday environments, and we're naturally adept at tracking their movement. This means that spring-based animations often feel more intuitive and realistic to our users than traditional animation approaches based on durations, easing curves, and tweens.
However, it is because spring physics is such a well-explored realm of UI animation that
renature is exploring other realms of physics like gravity and fluid dynamics. We're curious to see what kinds of interactions result from moving elements according to, say, the force of gravity at Jupiter's surface, the fluid resistance of a vat of molasses, or the orbital path of our own moon. We're not entirely sure of what we're going to find — that's part of the fun!
That being said, there is some prior art in this domain that we're looking to for guidance. Much of the inspiration for this project comes from the world of Processing, a language designed specifically for graphics programming and digital art. Specifically, the writing of Daniel Shiffman in The Nature of Code has been a foundational influence for me in experimenting with the notion of using natural world physics to guide UI animation. Using some of the fundamentals he introduces – namely vector creation and manipulation, basic force modeling, and state management of interaction systems – we have a strong basis on which to build.
renature in particular, we're really interested in technologies that offer high performance alongside type safety and mathematical correctness. We need a language that can accurately and ergonomically model forces like gravity acting in two dimensions. We also need to be able to animate CSS properties smoothly over time in response to this motion, which requires various interpolators. And of course, we might even want the ability to apply multiple forces at a time and have UI elements interact without dropping a frame.
We're also using TypeScript in
renature to handle the public-facing API, including our React hooks like
useFluidResistance. This gives end users of the library a familiar language to interface with while still reaping the benefits of a compile-time type checking step. A terrific tool called
genType from Cristiano Calcagno allows us to autogenerate TypeScript definitions for our Reason code, making interop between the two fully seamless. We're excited to keep experimenting with using TypeScript and Reason in the same codebase for maximum safety and speed.
The first two weeks of the fellowship began with developing the mathematical foundation on which
renature will be built, specifically core vector operations like
lerp. With these in place, we started creating a nice abstraction around
requestAnimationFrame to act more or less like Processing's
draw loop. My colleague, Max Yinger, and his writings on reactive animations were instrumental in getting this working. With these two core pieces in place, we could start modeling our first force — gravity! Some early experimentation yielded pretty cool results:
We've been making awesome progress on \\`renature\\` here at @FormidableLabs. gravity is the first force we tackled! Here's a body mass of 2000kg being attracted to a body 50,000 times its size. — parkie-doo (@parker_zeigler)December 9, 2019
However, we hit an interesting stumbling block almost right away. In the real world, everything is exerting a gravitational force on everything else, at all times; the force of gravity never stops acting. This doesn't quite fit with the notion of finite animations where we often want to animate a CSS property from a starting state to a finish state. How can we reconcile this dichotomy?
renature, we simplify things currently by defining a
mover and an
mover will be pulled towards an
attractor according to the gravitational force exerted by the
attractor, which is proportional to the two entities' masses and inversely proportional to the distance between them. As the
mover is pulled closer, it will accelerate until it reaches the
attractor. At that point, we stop the animation. Let's look at this through the example of changing an element's opacity over time.
When the animation is running, we calculate the position of the
mover object on every frame. By comparing this value to the
attractor's position, we can interpolate the value of
opacity for that frame. This results in smoothly animating values according to real world physics! For example, here's a
div animating its opacity from 0 to 1 at the rate that a 100,000kg body would be pulled towards a body 10,000 times its mass!
Of course, we don't want to limit the values users can animate to opacity. For that we need additional interpolators to map from a
mover position value to a CSS property, like
background-color. Let's see what that looks like with the same physics.
We're even beginning to lay the foundation for better visualizations of the two-dimensional interplay of an
mover with gravity. Houston, we've achieved orbit!
Where Are We Headed?
We're excited to keep moving forward on
renature in these next four weeks. There's still a ways to go with implementing more interpolators to handle all the variation present in the CSS world, and we're looking to both
popmotion to see how these problems have been handled in the past. We're also going to start modeling more forces like friction, air resistance, and fluid dynamics soon, using similar ideas to those we used for gravity. Finally, we're working on specific two-dimensional APIs for all of our forces, which should allow developers to build more engaging, interactive experiences for their users.
One day, we hope to build all the logic into
renature to support visualizations with many interacting bodies, like particle systems and cellular automata. We even want to expose most of the core primitives so that developers can make up their own forces entirely and create worlds subject to their own physics! It's early days right now and we want to make sure we get the foundation right in the course of these first six weeks.