“Turbocharging your Animations” — How to speed up with Lottie

06 February 2019
Lottie Animation

With the Lottie library, you can use dynamic animations in your application with just one JSON file. This animation can be anything. It can be a loader, a tutorial, or a Mario character who jumps on a shark and eats a mushroom (insert better joke here). Lottie is available for iOS, Android, React Native and Web.

“Turbocharging your Animations” — How to speed up with Lottie

Do you get impatient when an instructional animation moves too slow? Are you frustrated when the onboarding flow of an application is full of information you already know, but you’re afraid to skip through it in case you might miss something? In this article, I’ll be taking a look at a solution to these problems with Lottie.

What is Lottie good for?

What was my goal?

It was to create a cool animation using only one Lottie JSON file and to allow users to speed it up or play it in reverse if they want. I wanted to achieve this with a simple GestureDetector: if the user swipes right then the animation speeds up; swiping left makes the animation play in reverse.

Using GestureDetector

I implemented two methods: swipeRight() and swipeLeft() in the OnSwipeListener class. Both are calling the handleSpeedChanged() method with different values.

lottieAnimationView.setOnTouchListener(object : OnSwipeListener(applicationContext) {

override fun onSwipeRight() = handleSpeedChanged(ANIMATION_INCREASED_SPEED.unaryMinus())

override fun onSwipeLeft() = handleSpeedChanged(ANIMATION_INCREASED_SPEED)

In the handleSpeedChanged() method, we set the values that the algorithm will use (reversed, targetFraction). When the animation finishes, we can start it again at the correct speed. In reverse mode, we must set the speed first before the animation starts to play backwards. In this case, it would be a mistake to call the playAnimation() method first, because then the whole animation would play from the beginning, and the speed would only change afterwards.

private fun handleSpeedChanged(speed: Float) {
reversed = speed < 0

if (!lottieAnimationView.isAnimating) {
if (reversed) {
lottieAnimationView.speed = speed
} else {
lottieAnimationView.speed = speed
} else {
lottieAnimationView.speed = speed

targetFraction = getTargetFraction()
actualSpeedValue.text = getString(R.string.actual_speed_label, speed.toInt())

In the next section, I will talk a little bit about the fractions, which are the most important part of our algorithm.


To separate the animation and speed up some periods, we must know the elapsed/interpolated fractions of the animation (from-to).

In this case, it has steps:

If we speed it up in reverse, we will need the inverse value of the fraction.

In the example app, every reverse swipe will go back to the start value of the previous step.

As you can see, there are only two backward steps, because if forward and backward steps were divided the same way, then the user would not be able to go back to the beginning of the animation.

Target fraction

The value of targetFraction tells us when a period is over. This method returns the next correct value:

private fun getTargetFraction() = with(lottieAnimationView) {
when {
reversed && progress > SECOND_STEP_END_FRACTION ->

We must check the reversed flag in every line, because if it is true, then we return the inverse value of the fraction of the previous steps.

The targetFraction is instrumental in the AnimationUpdateListener implementation, where we can know the current animated fraction value, which means the current animation progress. If the animatedFraction is greater than the targetFraction, then we recalculate the targetFraction value and reduce the speed if necessary.

lottieAnimationView.addAnimatorUpdateListener { animation ->
val step = if (reversed) {
getAnimationStepFromFraction(1 - animation.animatedFraction)
} else {

if (circleIndicator.selectedItem != step) {
circleIndicator.selectedItem = step

if (targetFraction <= animation.animatedFraction) {

Besides this, we set the value of the circleIndicator.selectedItem here, which represents the current step. The reversed flag and the getAnimationStep() method can help us. This is almost the same method as the getTargetFraction(), but we are using the animatedFraction value to calculate the current step.

Notice: The lottieAnimationView.progress value is not necessarily equal to the animatedFractionValue. For example, if the animation speed were negative (-4f), then the progress value (0.6) and the animatedFraction (0.4) value would be different. In that case, the animation end would be the animation start, which is why the animatedFraction value would be 1 - 0.6 = 0.4.

private fun getAnimationStepFromFraction(fraction: Float): Int {
return when {


For this project, I used Kotlin, AndroidX and KotlinX.

In the example application, you can swipe left or right and the Mario animation will speed up or play in reverse. If you play it backwards to the beginning then it will start again at normal speed. As I mentioned before, the circleIndicator can tell you the current step. Below that, you can also see the current speed of the animation.

Lottie can be an effective way of giving information to the user, since it can show an animation that the user might otherwise want to skip through. Being able to adjust the speed is essential, so the user doesn’t get bored. Fortunately, with Lottie this is simple.

So, put on your Mario cap, jump on a shark, eat a big mushroom and start turbocharging your Lottie animations! :)

Example application:


Example Lottie JSON files:


At Supercharge we build high impact digital products that make life easier for millions of users. If you liked this article, check out some of Supercharge’s other articles on our blog, and follow us on LinkedIn, and Facebook. If you’re interested in open positions, follow this link.

“Turbocharging your Animations” — How to speed up with Lottie was originally published in Supercharge's Digital Product Development Guide on Medium, where people are continuing the conversation by highlighting and responding to this story.