Compose HotSwan Blog

Tuning Compose Animations Without Rebuilding: Hot Reload for Dynamic Design

Jaewoong Eum
Jaewoong Eum (skydoves)

May 2, 2026 · 9 min read

Tuning Compose Animations Without Rebuilding: Hot Reload for Dynamic Design

Photo by charlysey278 @ Unsplash

Why animation development is where hot reload matters most

Animation is the one area of UI development where the gap between code and result is most painful. A duration value of 300 means nothing until you see it move. A dampingRatio of 0.4f could feel perfect or completely wrong depending on the context, and you cannot tell from the code alone.

The traditional Android development loop forces you to rebuild the entire app for every single parameter change. Change a duration from 300ms to 250ms, wait 30 seconds for Gradle, watch the app restart, navigate back to the screen, trigger the animation, and decide whether it feels right. If it does not, repeat the cycle. For something as subjective as animation feel, this loop is where most of your time goes.

With Compose hot reload, you change the value, save, and the animation plays with the new value on your running device in under a second. No rebuild. No restart. No navigating back. You stay on the exact screen, the exact state, and see the exact difference between the old value and the new one. This turns animation tuning from a guessing game into a visual, iterative process.

In this article, you will explore four real world examples of animation development with hot reload: content size transitions, grid item animations, custom wave motion, and animation spec tuning.

Tuning animateContentSize in real time

animateContentSize() is one of the most common Compose animation modifiers. It automatically animates any change to a composable's size, whether that comes from text expanding, an image loading, or a layout restructuring. The challenge is tuning how it animates: the animation spec, the surrounding padding, the shadow elevation, the corner radius.

Here is a real example from the Pokedex Compose project. The detail screen header uses animateContentSize() on a Box with a height constraint:

Box(
  modifier = Modifier
    .fillMaxWidth()
    .animateContentSize()
    .height(290.dp),
) {
  // header content
}

With hot reload, you can change the height from 290.dp to 350.dp, and watch the content size transition play out on device immediately. Every save reflects the new values, so you can converge on the right feel in seconds instead of minutes.

Pixel level grid item animation

Modifier.animateItem() controls how items in a LazyVerticalGrid or LazyColumn animate when they enter, exit, or reposition. Getting these animations right requires tuning three separate specs: fade in, fade out, and placement. Each spec has its own duration, easing, or spring parameters, and they interact with each other in ways you cannot predict from code alone.

Here is how the Pokedex Compose home screen configures grid item animations:

PokemonCard(
  modifier = Modifier.animateItem(
    fadeInSpec = tween(durationMillis = 250),
    fadeOutSpec = tween(durationMillis = 100),
    placementSpec = spring(
      stiffness = Spring.StiffnessLow,
      dampingRatio = Spring.DampingRatioMediumBouncy
    )
  ),
  pokemon = pokemon,
  backgroundColor = backgroundColor,
  onCardClick = { navigateToDetails(pokemon) }
)

With hot reload, you can change the fadeInSpec duration from 250 to 600, switch the placementSpec from a bouncy spring to a stiff one, change the grid column count from 2 to 3, adjust contentPadding to tighten or loosen the spacing between cards, or tweak the card's corner radius and elevation, all without losing your scroll position or navigation state. You can scroll through the list, trigger the animation, tweak a value, and see the difference on the very next scroll.

Custom wave motion design

Custom Canvas animations are where hot reload truly shines. A wave animation might have a dozen parameters: layer count, frequency, amplitude, phase speed, color gradients, alpha values. Each parameter changes the visual character of the animation, and the interactions between them are nonlinear. You cannot predict what BASE_FREQUENCY = 0.010f looks like versus 0.05f without seeing it.

Here is a wave field animation with every visual parameter exposed as a literal value at the top of the composable:

@Composable
fun WaveField() {
  val LAYER_COUNT = 4
  val BASE_FREQUENCY = 0.010f
  val BASE_AMPLITUDE_DP = 18f
  val BASE_PHASE_SPEED = 2.2f
  val SECONDARY_RATIO = 2.7f
  val SECONDARY_AMP = 0.35f
  val LAYER_COLOR_START = Color(0xFF7EC9EB)
  val LAYER_COLOR_END = Color(0xFF1A237E)
  val BG_TOP = Color(0xFF0A0E27)
  val BG_BOTTOM = Color(0xFFBFC2ED)
  val BASELINE_FRACTION = 0.55f
  // Canvas drawing with layered sine waves...
}

Every one of these values is a literal that HotSwan can patch in under 100ms through its literal patching pipeline. Change LAYER_COUNT from 4 to 14 and the ocean deepens instantly. Swap LAYER_COLOR_START and LAYER_COLOR_END and the entire palette shifts. You can explore dozens of visual combinations in the time it would take to do a single Gradle rebuild.

Switching animation specs by eye

One of the most common animation iterations is switching between animation specs entirely. Should this fade use tween(300) or spring(dampingRatio = 0.4f)? The only way to know is to see both on device. With hot reload, you can swap between them instantly and compare the feel side by side.

Before
@Composable
fun PulseIcon(visible: Boolean) {
    val alpha by animateFloatAsState(
        targetValue = if (visible) 1f else 0f,
        animationSpec = tween(300)
    )
    Icon(
        imageVector = Icons.Default.Star,
        modifier = Modifier.alpha(alpha)
    )
}
After
@Composable
fun PulseIcon(visible: Boolean) {
    val alpha by animateFloatAsState(
        targetValue = if (visible) 1f else 0f,
        animationSpec = spring(dampingRatio = 0.4f)
    )
    Icon(
        imageVector = Icons.Default.Favorite,
        modifier = Modifier.alpha(alpha)
    )
}

The tween produces a linear, predictable fade. The spring adds an organic overshoot that feels more alive. You can also change the icon, the target values, or even replace the entire animation approach, and see the result on device without waiting for a rebuild.

Under 100ms: literal patching for animation values

HotSwan includes a feature called literal patching that is particularly relevant for animation work. When you change a primitive literal value, a number, a string, a float, or a hex color, the update bypasses Gradle compilation entirely and reaches the device in under 100ms. No incremental compile, no DEX generation. The value is patched directly in the running app's memory.

For animation development, this means duration values, damping ratios, stiffness constants, color codes, padding values, and corner radii all update at near interactive speed. You are not waiting for a compile cycle between iterations. You change a value, and by the time your eyes move from the editor to the device, the animation is already playing with the new parameter.

This works on both emulators and physical devices. There is no difference in speed or behavior. If you are tuning an animation on a physical device to check frame rate and rendering performance under real conditions, literal patching gives you the same instant feedback loop.

To learn more about how literal patching works, see the Literal Patching documentation.

Conclusion

In this article, you have explored four examples of how Compose hot reload transforms animation development: tuning content size transitions with animateContentSize, iterating on grid item animations with animateItem, designing custom wave motion from scratch, and switching between animation specs by eye. Each example follows the same pattern: change a value, save, and see the result on device in under a second.

Animation is inherently a visual, iterative discipline. The faster your feedback loop, the more iterations you can try, and the better your animations will be. Hot reload removes the rebuild bottleneck from that loop entirely.

If you enjoy crafting animations and want to try this workflow, install Compose HotSwan and start tuning your animations by eye.

As always, happy coding!

Jaewoong (skydoves)

Recommended Reading