Introducing Renature: Experimenting with Physics for UI Animation
This post comes from a piece I wrote on the Formidable blog. To read the post in its original context, head to the Formidable website.
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!
Exploring New Realms of Physics-Based UI Animation
If you're familiar with popular animation libraries in the React ecosystem like react-spring
or 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?
Indeed, react-spring
and 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.
Keeping it Reason(ML)able
Another core motivation behind the Fellowship Program is to explore new languages and technologies outside of the JavaScript ecosystem to see what they can offer us. For 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.
While JavaScript can handle all this math competently enough, its lack of a native type system and imprecision with floating point calculations make it a less than ideal choice for writing a small physics engine. For this reason, we're implementing all of the math backing renature
in ReasonML, a functional, statically-typed language that compiles to highly optimized JavaScript. If you're not familiar with ReasonML, check out my talk about it from this year's StrangeLoop conference. ReasonML is a derivative of OCaml, a language with a long history in scientific computing that's known for its superb type inference, speed, and safety. In addition to its type system, ReasonML also has certain native data structures, like tuples, that are both useful for modeling physics and are missing in JavaScript. Combine that with BuckleScript, a JavaScript compiler for Reason and OCaml, and we get a library that is both runtime safe and much faster than what we could write by hand thanks to compile-time optimizations.
We're also using TypeScript in renature to handle the public-facing API, including our React hooks like useGravity
, useFriction
, and 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.
What Have We Accomplished So Far?
The first two weeks of the fellowship began with developing the mathematical foundation on which renature will be built, specifically core vector operations like add, sub, mult, div, mag, norm, and 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
(a new physics animation library for React) here at@FormidableLabs
. gravity is the first force we tackled! Here's a body with a mass of 2000kg being attracted to a body 50,000 times its size. pic.twitter.com/d1D9eMqZeW
— parkie-doo (@parker_ziegler) 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?
In renature
, we simplify things currently by defining a mover
and an attractor
. A 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 attractor
and 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 react-spring
and 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.
To keep up with progress on renature
, check out the repo on GitHub or follow me on Twitter @parker_ziegler.
You can read this post in its original form on the Formidable blog!