# The (Finite) State of Reactive Animations

Canonical: https://davidk.dev/talks/the-finite-state-of-reactive-animations/
Video: https://www.youtube.com/watch?v=wOsqWoszMDI
Speaker: David Khourshid (@DavidKPiano), creator of XState
Type: Conference talk · Event: RxJS Live (2019) · Year: 2019 · Duration: 30:30 · Views: 3013 (as of 2026-10-19)
Topics: CSS & animation, Reactive programming, State machines, Event-driven architecture

## Summary
Reactive animations are more than just timelines. They adapt and respond to user input, creating a lively, exciting, and meaningful user experience. However, these dynamic user interfaces can quickly become complex, especially as features are added. So how can we build them in a manageable, scale-able, flexible, and declarative way? Enter finite state machines (FSMs).

## Key takeaways (quoted from the transcript)
- Over time, I got more interested in reactive animations, get over time, so I wanted to, like, play around with different streams of events and how those interacted with animations. (3:00)
- Using these animation frames, I decide to make some more experiments with of reactive animations, and this animation frame scheduler allowed us to have a steady tick up. (6:00)
- Put the values of the mouse and you would animate those smoothly instead of just having it be the exact position of the mouse. (9:00)
- RxJS is a finite state machine, has five parts. First, finite state machines have one initial states. (14:00)
- The idea is that you would star in the state, such as searching, and then, based on what events you had, it would tell you what the next state is. (16:00)

## Chapters
- 0:00 Introduction
- 3:00 I have this other example over here…
- 6:00 In this case, we have our LERP function…
- 14:00 If I pan up now, I'm in the NAV 2 state…
- 20:00 When we're in the dragging state we have to listen to…
- 26:00 Application logic, but being able to be visualized, analyzed, tested, simulated…

## Transcript
[0:00] [MusIc] Hi everyone. I am so excited to be at the first RxJS live and I'm excited to be a little last talk. To which I promise I'll keep it under two hours, so I hope you find. Also, I'm really intrigued to be talking at the movie theater, because usually when I talk at movie theaters, people something to show that. So I, I have always said. The time is the most neglected variable in programming, which is why we've came up with so many different concepts to work with.

[0:00] Managing time. Like callbacks promises, a sink awaits, or what we're here for observables, which is my favorite. So I actually got into this whole RxJS thing in sort of a different way than most of you. I was actually really interested in animations, and so when I was looking up RxJS, I saw that there was array. This concept has talked about the beginning of the day, called a reactive animation, or FRAN. And the paper that I read said there was one involving discrete changes due to potentially continuous events.

[1:00] And so, by describing these functional reactive animations, we could express the what of an animation and using that we can automate the hell or the implementation details of how that works. And so this idea was really intriguing to me, and it's an old idea, dating back from 1997. However, the paper talks about continuous events as opposed to discrete events, because digital signals are always discreet and not continuous. So they're sort of that fundamental difference, and that's why some people say, yeah, RxJS is not exactly FRP.

[2:00] And actually, the weird thing, too, is that the animation examples are just rotating their kids heads. So so we're, if you want to read the paper, I mean, that's fine, but like, there's ice, all that fun stuff. So anyway, my name is David Khourshid. I go. I'm known on Twitter and everywhere as David key piano. I'm sort of a black sheep here because I'm not in RxJS core team member, but I did make a PR once.

[2:00] I do not work at Google, I work at Microsoft, not sure if they're a competitor. I don't even use Bing, so I'm not a Google developer. Extra, whatever that means, Baidu, Google things to pretend that I'm an expert developer and I'm also not. An I.n.c. Euler developer, I'm sort of a reactive Elle price oratory around with a lot of things reactive. You may be a little bit of Angular, but my bread and butter is React.

[2:00] Also, I do a weekly Twitch stream called the key Framers.

[3:00] And on this, we take these complex UI animations made in dribble using different programs, and we actually try to recreate it using HTML, CSS, and JavaScript. And I've always been interested in these animations using as little code as possible. Or, like, just as few technologies as possible, such as this, CSS only dog well, HTML and CSS. But over time, I got more interested in reactive animations, get over time, so I wanted to, like, play around with different streams of events and how those interacted with animations.

[3:00] So, for example, what if I want to make this dog's head, you know? Follow my mouse the problem is, when I start doing this, there were so many different parts that needed to follow the mouse to make this look realistic. But then I started discovering techniques with RxJS, where, for example, if I had a stream of mouse moves, I could pipe those using the map operator into XY positions, then I could feed those XY positions directly into CSS using CSS variables, and then I could use those variables.

[4:00] Everywhere. I promise, either in here somewhere, but just everywhere, that I needed to, without having to worry about the implementation. Details of how this dog was created. So so I took this idea a little bit further, and so I also made this expanding house animation. Where when you change the range of the slider, the house just sort of, you know, added, and, you know, decreased room. So I wish I could do this on Zillow, would be pretty cool.

[4:00] But and all we're doing over here is, you see, we have a range observable which takes these input events from here. And we're starting with zero, so we're starting at the first value, which is 3, and then we update the state of how many rooms the house has. So all pretty simple stuff, nothing too complex yet, and all right, thank you. Conference, Wi-Fi. And so I have this other example over here, where I made this CSS only dog I like making.

[5:00] CSS only dogs? But I decided to make this one reactive too, and the reason I made this one reactive was because. Someone wrote a comment one time saying that the dog is a little bit hyperactive. But it's cool. So what I did was I. I piped the input events from this range, just like I did with the house. Over the CSS variable, and using that, I was able to use those CSS variables and change the duration of the animation.

[5:00] And so the result is we could actually make the dog a little bit slower. Now this goes both ways, if you go here, the dog goes super hyper. So now, if you remember the first talk yesterday by Ben, he said that if you see someone doing this.

[6:00] The animation frame using an animation frame scheduler and using that to create animation, ticks on every request the animation frame. Then. It's not something you should follow, it's not something that you should necessarily do. Because that person probably knows what he or she is doing, so that's exactly me, except I don't know what I'm doing, I just know that this works. And so using these animation frames, I decide to make some more experiments with of reactive animations, and this animation frame scheduler allowed us to have a steady tick up.

[6:00] Request animation frames. And I found this really useful for things like scroll events, as was talked about in the previous talk, and other events that didn't exactly fit in request. The animation frames or events that needed to be spread out so that they could be calculated not be some duration, but rather based on animation frame ticks. So I took these animation frames and let's say that I have this dream of balls.

[7:00] What I could do is I could use the with latest from operator and basically make it so that the balls are completely in sync with the animation frames. So we could have some fun with this, we could use what's called linear interpolation. And what this means is. It's a smooth way to go from one value to another value. Just by going it, by going by it every fraction. And so it's a.

[7:00] It's a simple equation, but all sort of illustrate how that works. Let's say you have two values 200 and 300. What you would do first is you would find the Delta between these values. In this case, it's 100. You would take that Delta and you would multiply it by a fraction, in this case 1/10. So you would add that fraction of the Delta to the first value, and so that would bring the first value a little bit closer to the last value.

[7:00] So why would we want to do this, and why is this such a popular, well used technique in animation? Well, it's because, depending on what fraction you use for linear interpolation, you could get different smoothing effects between two values. Obviously, if you use one, you're going to go from one value to the other really quick. Or if you use a smaller value, like 0-2, you're going to gradually make your way to that final value.

[8:00] And so by doing this, we could take these numbers, which smoothly go between these values, and we could use these two to just make some more fluid, reactive animations. So in this case, we have our LERP function, we have our animation frames, and we could use the with latest from operator to match those, you know, those animation frames to those LER values. And so the results, if this loads is just a very smooth way.

[9:00] In this case, I'm capturing where the mouse position is related to these goats and just have a very smooth, smooth experience with that. I wrote the article of while ago Sore describing this effect. And so the idea is that you would first just to illustrate it, eventually, if it's here, well, just to illustrate. You would have the Goat values first, and then not the Goat values. Put the values of the mouse and you would animate those smoothly instead of just having it be the exact position of the mouse.

[9:00] Because that sort of a jarring effect and that's not a smooth user experience, and so here's another demonstration of that effect. If you look at this over here and we click like right here, there we go, you can see it moved smoothly, moves from one position to the other, especially when I follow my mouse. And again, we're taking those values from the from over here, and we're feeding those into CSS variable so that we could animate various parts of this and.

[10:00] Also, we're using the LeRP technique using these, I named it AF because animation frame, yeah. And we're piping those into smooth numbers so that instead of just getting these raw values, we're getting the fractional values between them to create the smooth effect. So compare that to, let's say, if we turn that off, it's a very rigid effects compared to just having, you know, that with latest from Scan and this. By the way, this is RxJS 5, so you might see some older syntax.

[10:00] And so, I. I use this in various different ways, and I used also things like switch, math and other operators too. So just sort of represents more complex animations such as the swinging cat. So over here, instead of just, you know, instead of just listening to the mouse position. Now I have to do two separate things, I have to make the cat follow my mouse. When I'm moving it and when I let go, it has to exhibit a completely different behavior.

[11:00] So the way I did that was, I used switch map and this is fine for simpler scenarios, and we've heard about switch map a lot in order to go between two values. But we could see that a lot of times, animations become a lot more complex than switch map, you know, lends itself to be so. The general idea, and what I've been talking about for a few years now is that we have a stream of values that come from various different sources.

[11:00] We merge them together and we render them using custom properties by subscribing to those merged values. And so that way, we have these reactive Styles, which we could play around with in CSS. But the main idea is that because we're not directly manipulating HTML using those Styles. We could actually go further and use those animated values in canvas, in WebGL, in whatever you want to use, not just CSS. And so playing with this idea that everything is a stream, including animations, only went so far.

[12:00] So this was one of the animations that I saw and I was like, alright, this is really cool. There's a lot of things happening here, but I have no idea how to do it with just dreams. And I quickly discovered that everything is a stream is not enough. So I started looking at different animation platforms, animation tools, and so this one is called Origami, designed by Origami Studio by Facebook. And it uses this idea of patches to transform values from one value to another, but it doesn't do it synchronously.

[12:00] Like, you could have what's called a Papi animation over here, which takes one value and just pops it between one and another value. Similar to how we did Lurk and this switch over here, which sort of animates between states. Then there's an vision app, which is pretty recently came out within the last couple of years. And it's the same idea where you could interpolate between values. But I saw noticing something like with the animation tools like this.

[13:00] You describe each individual state of your app, and it will automatically go between those states depending on what kind of interactions you program into it. Principle is also the same way. Came out a bit earlier than Envision Studio, where you would set up each of the different states of your app and it would just magically go between those states. And one of the newest animation tools out there is Drama app, which you can't see here because of the conference Wi-Fi.

[13:00] So just pretend that's a really cool animation anyway.

[14:00] The idea is that animations are transitions between various states, and our apps are our components. Or whatever we're doing due to events. And so this doesn't just describe animations, but it describes so much more. But this idea might be familiar to some of you. And that's the idea of finite state machines, and it's extended cousins day charts, so finite state machines are a pretty mathematical concept. But just to summarize what they are, since we're all sort of visual thinkers, I mean, that's really what got me hooked onto.

[14:00] RxJS is a finite state machine, has five parts. First, finite state machines have one initial states. This is a state that you must always start in. It has a finite number of states. For example, if we're modeling a promise, it could be pending, fulfilled, rejected, or idle, yet the promise could be not started yet. It has a finite number of dense. Which is the possible things that could happen in the app, such as fetching, or when the promise resolves or rejects.

[15:00] And then it. Has transitions which map these different states together via events. So when we are in the idle states and we have the fetch events. Now we're in the pending state. And so this is an important idea because when we're in the pending state and fetch happens again, nothing happens, and that's the whole idea. And we also have a finite number of final states, which says that our system or our app is done and it can no longer transition from those states.

[15:00] So again, if we have a machines is this, and instead of using like to like switch, map or take latest from, or whatever, the newest operator is. The idea is that nothing happens because of the rules. To find in the state machine when we have a search, events in the searching state, because there is no transition defined from searching, nothing should happen. And so this brings a sort of robustness to our applications.

[16:00] So there's many different ways that you could create finite state machines. You could use switch case statements, which seem to be popular, especially if you're using NG, RX or Redux or whatever. And the idea is that you would star in the state, such as searching, and then, based on what events you had, it would tell you what the next state is. But personally I like to use object mapping, which is just it's essentially a dictionary lookup, right?

[16:00] So, for example, let's say you're in the searching state and you have the resolved events, now, you're in the success state. And so determining what the next state is just a simple function where you take whatever state you're in. And you find the event if it exists, and the events will tell you what the next state is. Or it just defaults to the same state that you are on now with RxJS, this idea of a finite state machine and this representation of your app or your animations as finite state machines really simplifies app flow.

[17:00] For example, if you have multiple streams of events coming in, you would first merge them and then you would pipe this into a scan operator. And then you have this incremental reducer, which basically tell you which state comes next based on which events happened. And so you're like, if you think about it, and this is one. Of the core concepts of MGR, Exxon's most of you use Angular is that most of your app can be represented in this sort of way.

[17:00] And so that's where a lot of my animations slowly evolved to. Where I would have multiple streams of events, such as a pan, keypress or anything else, I would merge these events together. And then, based on some initial state, using the scan operator, I would determine what the next state is based on the events that came in from the string. And then I would just subscribe and have those values rendered out or sunk in some sort of way.

[18:00] So here's an example of that. I had this app where basically we're detecting pan up and pan down gestures. And so, of course, you could use switch map, you could use like just whatever operator you want to use, but I wanted to use, I want to use Scan. And this made my application logic a lot clearer. Because in this reduce function, we could say when pan up or pan down happens, we changed the Y value and we set moving to true.

[18:00] And then when pan end happens, we could express the application logic. That if we didn't pan far enough, then don't move. So, like, if I do that, then it's not going to move, but if I go down far enough, it will move. But then trying to do the animation that I talked about, this got a little bit more complex. And so there's a mess of code in here. And my scan operator actually became really messy inside and I eventually found myself doing this and this was a few years ago.

[19:00] So this is a very basic state machine because there's many different ways you can navigate through this app. I had to have some explicit way of saying, here's the possible ways that the states can transition between each other. And so that's what that is over there. So if I pan up now, I'm in the NAV 2 state, which is over here. And then if I pan down from there now, I'm in the NAV 1.

[19:00] And so this also means if I pan up from here, only you get that buggy behavior. Because, again, this was a few years ago, so a couple years ago I created this library called. XState, to simplify the creation of these finite state machines and statecharts and JavaScript.

[20:00] So that object notation I was showing you. That's essentially the same API that you would use in XState, and so XState. Internally does this dictionary lookup, but it also does a lot more things. What you could do is you could take that machine, and you could subscribe to it by. In not invoking it, but by calling it, interpreting it, in calling it as a service. So what this means is that your machine is essentially an observable of states.

[20:00] And so this is a very powerful concept because you could also think about this as a behavior subject to. Because you could send events to it, the difference is that when you send it events, it doesn't necessarily in mint those exact same events. Instead. Internally, the state transitions, and based on those transitions, different states are output, and so, you know, it takes an events, it outputs states. And thinking about your app in that way makes things a lot simpler.

[21:00] So, for example, if you've ever tried to do a drag drop in the nation with RxJS. You might have what's called operator juggling, where you're just putting in multiple like take until and like with latest from and stuff like that. But instead, I thought about this as two different states where either in the idle state or were in the dragging state. And as you could see, the values update when we're in the dragging state, and then when I drop it, it, the values update so that we're in the idle state.

[21:00] And because we have an explicit representation of the state, we could style this box differently based on what state were in. We could also express complex application logic, like, for example, if we press escape while dragging, it should go back even if my mouse is still pressed down. And so that is, that is expressed in that machine. Over here, where we have pan right, pan end, as you could see, goes to idle, and then we have cancel, which cancel, it goes to idle as well.

[22:00] And that's from this escape observable over here, where we just take the key downs, filter based on whether it's the escapee or not, and just map it to a. Simple events, which is of type cancel. So I wanted to do a more complex in the mission. Then I saw this really, really fun one by Gal Sheer. It's called design your week, and what it allows you to do is just delete your weekdays and extend your weekend.

[22:00] Which would be a pretty cool app if it worked. So, again, I thought about how would I do this, especially with RxJS? And I realized that I really needed to make an explicit way of describing the various dates of the app. Could be in. Because having this mouse drag and just showing this like rectangle is not enough, because the mouse dragging across the screen exhibits a different behavior. Depending on what state you're in, so, for example, for in the selected state, we could select a few items.

[23:00] And now when we're dragging, we're in the dragging state, so moving my mouse across the screen exhibits a different behavior. And we no longer get that blue box. Then we could dump it in there and then we could. What's tomorrow Saturday? Okay, we could extend Saturday over there and then we have this explicit behavior over here and so to accomplish this there, basically, I let's see, I have this giant machine over here, but I'm not even going to go through the code.

[23:00] Because just like marble diagrams or observables, whether you want to call it, things are best described visually. And so using this exact same code, we could generate these visual diagrams of how the machine is supposed to behave. So we have the start state, and when the mouse goes down, you're in selecting, and then it goes from selected to dragging, disposed and grabbing, basically, the different states of the animation. And it even handles complex behavior, such as if I drag this and I decide to let go, they just go back.

[24:00] And yeah, and so that's all based on just this idea of a finite state machine now. Another idea, too, is that. When we talk about subscribing and unsubscribe from services or observables, you wonder, like, when do we actually unsubscribe from them? And the answer is when we no longer need them, which is sort of a hand wavy answer. But with XState and with ZIG charts in general, you tie those observables or those services.

[24:00] That you subscribe to a state, for example. When we're in the dragging state we have to listen to mouse up offense, but when we're no longer in the dragging state, we no longer have to listen to those. And so in that state, there's this idea of invoke, and invoke means take whatever service that we invoke, such as observables or promises or anything like that. And once we're not in that state, we automatically unsubscribe from those.

[25:00] And so this is visualized over here. So when we're in the dragging states now, we're listening to both mouse moves and mouse ups, we're always listening to mouse downs. Because that's a fundamental behavior in our app that's coming through all the states. And so when mouse up occurs, that causes a state change with an XState. And so that means that this mouse up and mouse move. Observable are automatically unsubscribed until we need them again, such as in the grabbing state, and so besides being able to consume observables.

[25:00] Like I talked about. You could use this invoked state machine and actually subscribe to the state changes within, and so we could take that state machine, which is over, not that one.

[26:00] It's over here. And what we're doing is we're subscribing to it, and on that subscription were, or what we called, rendering it. So I had this function style Tver over here, which takes those variables and puts them in CSS variables. And it also does a few other things in the HTML. Things that we refer to as side effects. And so this idea is that were we have to think in states. And the behavior of our application, in our animation, in those various dates, and not just the events or the stream of values that happen.

[26:00] And I feel like, honestly, that's one of the biggest traps that we get into when we program with RxJS is that we just juggle all these operators until something works. And then what we have is something that works pretty well. But it is a completely unreadable mess because, you know, it's just a bunch of operators. And so that's why I think that visualization is so important. We could take those observable machines that we created in the examples, and we could just, you know, paste them into a visualizer such as.

[27:00] Here and be able to examine the behavior visually, and so you could do that no matter how complex the examples. So that week examines electing two, selected two, dragging. That exact same code that's used to power that animation can be copied and pasted somewhere over here. And we're able to examine our application logic and just make sure that all of our bases are covered. And that our application logic is working the way that it's supposed to.

[27:00] So there's a lot of advantages of using these date charts and state machines in your application. And don't take it from me to get from NASA, who used a chart in other coding, the Curiosity Rover, in which they said that there's visualized modeling. Like, we just showed precise diagrams that show you all the behavior of your state machine. You could also automatically generate code, which we're not going to do because that sort of nasty.

[28:00] There's comprehensive test coverage, which I just gave a talk on and just the other week. And there's also the accommodation of late-breaking requirements and changes, which is such an important point. And it's an important point because when your application logic changes, I want to think about which state does it affect, which transitions need to be added or removed. Instead of which operators do I need to change the order of her, which additional operators do I need to add?

[28:00] That gets really confusing really quickly, and so this might sound like I'm talking about the future of state management. Just like reactive X and RxJS, it's nothing new. There's a speck called SC XML, which describes deep machines in this XML format, which is a little bit unreadable because, of course, it's XML. But the problem is it's usable, or the great thing is, it's usable in things such as QT. And you can do which target in many different languages, so just like you transfer your ideas from RX.JS to different languages.

[29:00] You could do the same thing with state machines and statecharts, and so I have a lot of resources on these dang machines and statecharts. If you want to look further and see how they fit into this general model of reactive programming. But basically, the idea is, I want you to make your code do more than just expressing your. Application logic, but being able to be visualized, analyzed, tested, simulated, and more so.

[29:00] Thank you RxJS live [Applause] [Music].
