Porting an iOS Confetti Effect to Reanimated
As part of Shopify’s push into React Native, the Arrive package tracking app was rewritten in the framework and released on Android. One of the iOS app’s most popular features — confetti raining down when a package is delivered — originally relied on CAEmitterLayer. Since that’s an iOS-only API, the team had to decide how to bring the effect to Android: write native Kotlin/Java code for the platform, or reimplement the animation in pure JavaScript to share between OSes.
The choice was the second option, using the Reanimated library to keep the effect running at native performance levels. The rewrite also allowed for a few visual upgrades: the confetti now spreads more uniformly across the screen and rotates along all three axes, behaving more like paper in the air.
Setting Up the Confetti View
The first step is rendering a batch of confetti pieces with randomized colors, positions, and rotation angles. Each confetto gets wrapped in Reanimated’s Animated.View, which works like React Native’s standard View but accepts style properties driven by declarative animation instructions.
Choosing Between JavaScript Drives and Native Declarations
React Native offers two main approaches to animations. The first is a JavaScript function called via requestAnimationFrame that updates view properties on each frame. The second is declarative, using APIs like Animated or Reanimated, where instructions are predetermined and shipped to the native UI thread to be applied every frame.
The JavaScript-per-frame approach looks simple, but it has a catch: the new values must be computed within 16 milliseconds every frame to maintain a steady 60 FPS. Because JavaScript is single-threaded, any other task running at the same time competes for the same budget, and as an app gets more complex, hitting that limit becomes increasingly difficult.
With declarative animations, JavaScript is only involved during setup. Everything else — the per-frame math — runs natively. Instead of asking JavaScript where a view should move, you construct a small near-programming language of instructions that the UI thread executes each frame, with support for conditionals, math operations, and string concatenation.
Describing the Falling Motion
Now it’s time to animate the confetti. Rather than randomizing the starting x, y, and angle, all pieces get the same initial world-state. Instead, their velocities are randomized, so the confetti appears to all erupt from the same point and fly off in various directions and speeds. Each velocity defines how far its associated value changes over one second of animation.
Values destined for animation are wrapped in Animated.Value. The driver of the effect is an Animated.Clock, which exposes the animation’s elapsed time. A dt value expresses how many seconds have passed since the last native frame, and from it the x, y, and angle deltas are calculated.
The clock starts after a cond check verifies that it isn’t already running. Any timeDiff value is also evaluated on the first frame — since diff reports change since the previous evaluation, this first call also serves as its baseline.
The logic runs every frame as:
- if the clock isn’t running, start it
- capture the time difference since the last frame
- update each motion value by velocity ×
dt
Uniform motion works well for confetti since paper is slowed by air resistance almost immediately. For simulating heavier objects, a yAcc variable could be added to incrementally increase the yVel over time.
Delay and Staggering the Burst
The plain animation shoots all confetti out instantly. The iOS original, by comparison, fires in waves. The fix is to add a delay property that increases for each block of 10 pieces. Before the main animation math runs, the delay value gets decremented by dt, and only when it drops below zero does the original motion code kick in.
Preventing Escape from the Screen
A large share of confetti was shooting out of frame horizontally without ever crossing the full screen. To correct that, the confetti now bounces off the horizontal edges. A carefully tuned elasticity multiplier keeps the pieces from looking like rubber bands snapping back and forth. When an x position tries to exceed a screen boundary, it’s pinned to that edge, and its velocity reverses direction and is multiplied by the elasticity factor (the same treatment is applied to the opposite edge).
Two Cannons, Three Dimensions
The final polish adds two improvements. One confetti cannon at the top becomes two enough to cover the screen from both sides, and the pieces now rotate in 3D rather than just flipping in 2D. Real cleanup — deleting images of spent confetti and stopping the clock once pieces reach the screen’s bottom — is left as an exercise. The fully assembled component code is in a gist.
Native-Level Motion, Written in JavaScript
Reanimated’s API comes with a learning curve, but it makes 60 FPS cross-platform animations possible without Native Modules per platform or per-frame work in JavaScript in the bottleneck. Its capabilities extend well past simple choreographed motion, such as mapping touch gestures to declared animations.



