Jetpack Compose — Earthquake Effect

Did you ever think how would your UI behave when there is an e҉a҉r҉t҉h҉q҉u҉a҉k҉e҉?
Well… I can show you!

Now, imagine that you can even control how the earthquake would be!
Yes, yes… I will show you how to do it.
Getting Started
The project is on my GitHub, feel free to check. (Good option if you are lazy! Stay here if you are curious to learn 😁)
As of pre-requisites, you’ll need Android Studio Canary. Currently, Jetpack Compose only works in these Android Studio versions. Use the last version, and you should be ok.
💡 The Idea
Jetpack Compose makes it a lot easier to gamify applications. You may be asking who might need this.. Actually I’m thinking the same. Anyway, I have developed some games in Unity some years ago, and one cool thing that I often used were Camera Shakes! So, guess what, I got inspired on that! 🎉
What is a Camera Shake?
Camera Shake, is when what you are seeing, shakes! Seems simple, right? What I used to do to reproduce this effect, was to shake the Camera instead of shaking the visible content.
In Jetpack Compose, we do not have a Camera instance to shake… So what? We have the visible content! Compose makes easier to us to create a container that holds our visible content. If we shake this content, we end up reproducing a similar result.
How to “shake” our container?
To reproduce a “shake” effect, we need to move at least 3 properties of our container: x, y and rotation.
Yes! If we keep moving these, we end up with something close to the wanted effect. I have also added a little alpha tweak, to make it more cool.
📐 Architecture

WOW! This may seem overwhelming. Let’s go step by step.
EarthquakeBox, in the middle, is our Composable Container, or the visible content host, that will be shaking.
Inside EarthquakeBox, we are using:
- EarthquakeState - Holds the state of the Earthquake;
- IEarthquakeScope - This is a “contract” between the Earthquake and the usage side;
- IEarthquakeController - Controls the earthquake;
- IEarthquakeMover - Handles each shake movement of the earthquake.
Scope, Mover and Controller are abstract. So, we will need to provide concrete implementations of these. Let’s investigate each one of these.
EarthquakeState
We can say these are the Earthquake “Properties”. These can be changed, and whenever they are changed, they will cause a recomposition to happen, which will update our UI. Simple as that. The class is marked with the Stable annotation, because we want to guarantee to the compose compiler that whenever a public property of these changes, composition will be notified.
IEarthquakeScope
This is the contract between the Earthquake and the visible content you define. This means that, from your composable components inside the Earthquake container, you will be able to:
- startShaking() - Start the earthquake;
- stopShaking() - Stop the earthquake;
- val isShaking - Verify if it is shaking;
- var shakeDuration - Verify and set the shakeDuration;
- var shakesPerSecond - Verify and set the shakesPerSecond;
- var shakeForce - Verify annd set the shakeForce.
To make the changes be reflected from usage side to the state itself, we need to provide a concrete implementation.
As you can see, our EarthquakeScope class implements the IEarthquakeScope abstraction, so it is its concrete implementation.
We get the state on the constructor, and on each property and function, the state gets updated.
IEarthquakeController
The IEarthquakeController controls the earthquake. It can start, or stop it. The interface is marked with the Immutable annotation, because we want to promise to the compose compiler that all publicly accessible properties and fields will not change after the instance is constructed.
The concrete implementation gets on its constructor:
- scope - This is a coroutine scope that we use to create the timer and move the content;
- mover - This is some concrete implementation of the IEarthquakeMover;
- onEarthquakeFinished - This is a lambda that will be executed whenever the timer finishes.
To be able to stop and start the earthquake whenever we want, we use the nullable timerJob variable of type Job.
Whenever we start the earthquake timer, we:
- Set the timerJob to the Job that “flowTimer” returns (the timer itself may be handled in another article)
When we want to stop the earthquake, we:
- Cancel the timerJob and set it to null;
- Use the mover to get the container back to the original position;
- Call onEarthquakeFinished to warn that it has finished.
IEarthquakeMover
Since each property (x, y, rotation and alpha) will be its single source of truth, it is recommended to use Animatable as described here.
The function “animateTo()” of Animatable, is a suspend function, so it must be called from inside a coroutine scope. So, to make this easier, both “move” and “stop” are marked as suspend functions.
The rest of the functions are used to return new offsets, new rotations and alpha values when moving the container.
On the begin of the class, all the properties are specified with their default values.
The “move” function is called on each shake. So, on each shake, it generates a new offset, rotation and alpha, and animates the properties with these values.
The “stop” function is called to animate every property to its initial state, and then stop them.
The rest of the functions are used to generate the random values.
🎨 Earthquake Composable
So, as arguments, it receives:
- The lambda that will be called whenever the earthquake finishes;
- The Composable content, which is marked as an extension of IEarthquakeScope to allow the earthquake properties to be changed from the usage side.
- All the necessary concrete implementations are provided in the begin;
- LaunchedEffect is used to detect “state.isShaking” changes on each recomposition, using these to start or stop shaking;
- Your content is drawn inside a Box where all the earthquake properties are applied.

Where to go from here?
If you want to check the code in a more cleaner way, feel free to check my GitHub. There is also one usage example.
If you want to extend your knowledge on Jetpack Compose, check the following resources:
Clap to say “thank you” and to help others find this article.
If you enjoyed it, and if this helped you, you can also consider paying me a coffee. :]
Thank you!