What You Will Build

A layered water wave animation where three sine wave layers undulate continuously against a deep blue background. This effect is used in weather apps, hydration trackers, and meditation interfaces to create calming ambient motion.

Why Sine Wave Animation Teaches Important Concepts

Sine waves are the foundation of most organic-looking animations. By combining rememberInfiniteTransition with trigonometric math on Canvas, you learn the pattern for any continuous, smooth, looping animation: water, audio visualizers, loading indicators, and heartbeat monitors.

Key Compose Concepts

  • rememberInfiniteTransition for animations that loop forever without user interaction.
  • Canvas with Path for drawing complex custom shapes.
  • Phase shifting to create the illusion of wave movement by offsetting the sine function over time.

Step 1: Set Up the Infinite Phase Animation

val infiniteTransition = rememberInfiniteTransition(label = "wave")
val phase by infiniteTransition.animateFloat(
    initialValue = 0f,
    targetValue = 2f * Math.PI.toFloat(),
    animationSpec = infiniteRepeatable(
        animation = tween(3000, easing = LinearEasing)
    ),
    label = "phase"
)

Step 2: Draw Layered Waves on Canvas

Three wave layers with different amplitudes and alpha values create depth. Each layer is a filled Path that follows a sine curve:

Canvas(Modifier.fillMaxSize()) {
    val w = size.width
    val h = size.height

    for (layer in 0..2) {
        val path = Path()
        path.moveTo(0f, h * 0.6f)

        var x = 0f
        while (x <= w) {
            val y = h * 0.6f + sin(
                (x / w * 4 * Math.PI + phase + layer).toDouble()
            ).toFloat() * 30f * (layer + 1)
            path.lineTo(x, y)
            x += 5f
        }
        path.lineTo(w, h)
        path.lineTo(0f, h)
        path.close()

        drawPath(
            path,
            color = Color(0xFF42A5F5)
                .copy(alpha = 0.3f - layer * 0.08f)
        )
    }
}

Tips and Pitfalls

  • Layer offset by index: Adding + layer to the sine phase shifts each wave layer, creating parallax depth.
  • Amplitude scaling: 30f * (layer + 1) makes deeper layers taller, reinforcing the depth illusion.
  • LinearEasing is mandatory for smooth continuous waves. Any other easing creates visible speed changes at loop boundaries.
  • Step size of 5f balances smoothness and performance. Smaller values create smoother curves but more path segments.
  • Path.close() is essential to fill the area under the wave down to the bottom of the screen.

Minimum SDK: API 24+ with Compose BOM 2024.01+

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.