Potentially working well
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
index 7c30b6c..f1ad819 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
@@ -159,13 +159,10 @@
 fun LottieAnimation(
     compositionSpec: LottieCompositionSpec,
     modifier: Modifier = Modifier,
-    isPlaying: Boolean = true,
-    restartOnPlay: Boolean = true,
-    repeatCount: Int = 1,
+    playMode: LottiePlayMode = LottiePlayMode.Play,
     clipSpec: LottieClipSpec? = null,
     speed: Float = 1f,
-    onRepeat: ((repeatCount: Int) -> Unit)? = null,
-    onFinished: (() -> Unit)? = null,
+    iterations: Int = 1,
     imageAssetsFolder: String? = null,
     imageAssetDelegate: ImageAssetDelegate? = null,
     outlineMasksAndMattes: Boolean = false,
@@ -176,13 +173,10 @@
     LottieAnimation(
         composition,
         modifier,
-        isPlaying,
-        restartOnPlay,
+        playMode,
+        iterations,
         clipSpec,
         speed,
-        repeatCount,
-        onRepeat,
-        onFinished,
         imageAssetsFolder,
         imageAssetDelegate,
         outlineMasksAndMattes,
@@ -202,32 +196,26 @@
 fun LottieAnimation(
     composition: LottieComposition?,
     modifier: Modifier = Modifier,
-    isPlaying: Boolean = true,
-    restartOnPlay: Boolean = true,
+    playMode: LottiePlayMode = LottiePlayMode.Play,
+    iterations: Int = 1,
     clipSpec: LottieClipSpec? = null,
     speed: Float = 1f,
-    repeatCount: Int = 1,
-    onRepeat: ((repeatCount: Int) -> Unit)? = null,
-    onFinished: (() -> Unit)? = null,
     imageAssetsFolder: String? = null,
     imageAssetDelegate: ImageAssetDelegate? = null,
     outlineMasksAndMattes: Boolean = false,
     applyOpacityToLayers: Boolean = false,
     enableMergePaths: Boolean = false,
 ) {
-    val progress by animateLottieComposition(
+    val animationState = animateLottieComposition(
         composition,
-        isPlaying,
-        restartOnPlay,
-        clipSpec,
-        speed,
-        repeatCount,
-        onRepeat,
-        onFinished,
+        playMode,
+        iterations = iterations,
+        clipSpec = clipSpec,
+        speed = speed,
     )
     LottieAnimation(
         composition,
-        progress,
+        animationState.value,
         modifier,
         imageAssetsFolder,
         imageAssetDelegate,
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationResult.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationResult.kt
new file mode 100644
index 0000000..544aee7
--- /dev/null
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationResult.kt
@@ -0,0 +1,9 @@
+package com.airbnb.lottie.compose
+
+sealed class LottieAnimationResult {
+    abstract val lastFrameTime: Long?
+
+    class Cancelled(override val lastFrameTime: Long?) : LottieAnimationResult()
+
+    class Finished(override val lastFrameTime: Long) : LottieAnimationResult()
+}
\ No newline at end of file
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieComposition.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieComposition.kt
index f3ac523..09a8c5f 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieComposition.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieComposition.kt
@@ -3,7 +3,154 @@
 import androidx.compose.animation.core.*
 import androidx.compose.runtime.*
 import com.airbnb.lottie.LottieComposition
-import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.Channel
+import java.util.concurrent.atomic.AtomicReference
+
+enum class LottiePlayMode {
+    Play,
+    Pause;
+
+    operator fun not(): LottiePlayMode = if (this == Play) Pause else Play
+}
+
+@Composable
+fun rememberLottieAnimationState(
+    iterations: Int = 1,
+    clipSpec: LottieClipSpec? = null,
+    speed: Float = 1f,
+): LottieAnimationState {
+    val state = remember { LottieAnimationState() }
+    LaunchedEffect(iterations) {
+        state.iterations = iterations
+    }
+    LaunchedEffect(clipSpec) {
+        state.clipSpec = clipSpec
+    }
+    LaunchedEffect(speed) {
+        state.speed = speed
+    }
+    return state
+}
+
+@Stable
+class LottieAnimationState internal constructor() : MutableState<Float> {
+    private var job = AtomicReference<Job?>()
+
+    private val updatesChannel = Channel<Unit>()
+
+    private var _value by mutableStateOf(0f)
+    override var value: Float
+        get() = _value
+        set(value) {
+            _value = value
+            updatesChannel.offer(Unit)
+        }
+
+    var isPlaying: Boolean by mutableStateOf(false)
+        private set
+
+    var iteration: Int by mutableStateOf(1)
+
+    private var _iterations: Int by mutableStateOf(1)
+    var iterations: Int
+        get() = _iterations
+        set(value) {
+            _iterations = value
+            iteration = minOf(iteration, value)
+            updatesChannel.offer(Unit)
+        }
+
+    var _clipSpec: LottieClipSpec? by mutableStateOf(null)
+    var clipSpec: LottieClipSpec?
+        get() = _clipSpec
+        set(value) {
+            _clipSpec = value
+            updatesChannel.offer(Unit)
+        }
+
+    var speed: Float by mutableStateOf(1f)
+
+    var isAtEnd by mutableStateOf(true)
+        private set
+
+    /**
+     * Animate the Lottie composition given the state properties above.
+     * If the animation reaches the end, instead of finishing,
+     */
+    suspend fun animate(
+        composition: LottieComposition?,
+        cancellationBehavior: LottieCancellationBehavior = LottieCancellationBehavior.Immediate,
+    ): Unit = coroutineScope {
+        val oldJob = job.get()
+        oldJob?.cancelAndJoin()
+        if (composition == null) {
+            isPlaying = false
+            return@coroutineScope
+        }
+        val newJob = coroutineContext.job
+        job.compareAndSet(oldJob, newJob)
+        isPlaying = true
+        try {
+            animateImpl(composition, cancellationBehavior)
+        } finally {
+            isPlaying = false
+        }
+    }
+
+    private suspend fun animateImpl(
+        composition: LottieComposition,
+        cancellationBehavior: LottieCancellationBehavior,
+    ) {
+        val minProgress = clipSpec?.getMinProgress(composition) ?: 0f
+        val maxProgress = clipSpec?.getMaxProgress(composition) ?: 1f
+        _value = value.coerceIn(minProgress, maxProgress)
+        var lastFrameTimeNanos = withFrameNanos { it }
+        while (true) {
+            awaitReadyToAnimate(maxProgress)
+            lastFrameTimeNanos = animateLottieComposition(
+                composition,
+                progress = this@LottieAnimationState,
+                clipSpec = clipSpec,
+                speed = speed,
+                lastFrameTimeNanos = lastFrameTimeNanos,
+                startAtMinProgress = false,
+                cancellationBehavior = cancellationBehavior,
+            )
+            if (iteration < iterations) {
+                iteration++
+                _value = when {
+                    speed >= 0 -> minProgress
+                    else -> maxProgress
+                }
+            }
+        }
+    }
+
+    private suspend fun awaitReadyToAnimate(maxProgress: Float) {
+        if (iteration < iterations || value < maxProgress) {
+            isAtEnd = false
+            return
+        }
+        isPlaying = false
+        isAtEnd = true
+        for (u in updatesChannel) {
+            if (iteration < iterations || value < maxProgress) {
+                isAtEnd = false
+                isPlaying = true
+                return
+            }
+        }
+    }
+
+    override fun component1(): Float {
+        return value
+    }
+
+    override fun component2(): (Float) -> Unit {
+        return { value = it }
+    }
+}
 
 /**
  * Returns a mutable state representing the progress of an animation.
@@ -19,99 +166,30 @@
  * and pass its progress to [LottieComposition].
  *
  * @param composition The composition to render. This should be retrieved with [lottieComposition].
- * @param isPlaying Whether or not the animation is currently playing. Note that the internal
- *                  animation may end due to reaching the target repeatCount. If that happens,
- *                  the animation may stop even if this is still true. You may want to use
- *                  onFinished to set isPlaying to false but in many cases, it won't matter.
- * @param restartOnPlay If isPlaying switches from false to true, restartOnPlay determines whether
- *                      the progress and repeatCount get reset.
+ * @param playMode Whether or not the Lottie animation should be playing if it is not at the end of
+ *                 the animation.
  * @param clipSpec A [LottieClipSpec] that specifies the bound the animation playback
  *                 should be clipped to.
  * @param speed The speed the animation should play at. Numbers larger than one will speed it up.
  *              Numbers between 0 and 1 will slow it down. Numbers less than 0 will play it backwards.
- * @param repeatCount The number of times the animation should repeat before stopping. It must be
+ * @param iterations The number of times the animation should repeat before stopping. It must be
  *                    a positive number. [Integer.MAX_VALUE] can be used to repeat forever.
- * @param onRepeat An optional callback to be notified every time the animation repeats. Return whether
- *                 or not the animation should continue to repeat.
- * @param onFinished An optional callback that is invoked when animation completes. Note that the isPlaying
- *                   parameter you pass in may still be true. If you want to restart the animation, increase the
- *                   repeatCount or change isPlaying to false and then true again.
  */
 @Composable
 fun animateLottieComposition(
     composition: LottieComposition?,
-    isPlaying: Boolean = true,
-    restartOnPlay: Boolean = true,
+    playMode: LottiePlayMode = LottiePlayMode.Play,
+    iterations: Int = 1,
     clipSpec: LottieClipSpec? = null,
     speed: Float = 1f,
-    repeatCount: Int = 1,
-    onRepeat: ((repeatCount: Int) -> Unit)? = null,
-    onFinished: (() -> Unit)? = null,
-): MutableState<Float> {
-    require(repeatCount > 0) { "Repeat count must be a positive number ($repeatCount)." }
-    require(speed != 0f) { "Speed must not be 0" }
-    require(speed.isFinite()) { "Speed must be a finite number. It is $speed." }
-
-    val progress = remember { mutableStateOf(0f) }
-
-    var currentRepeatCount by remember { mutableStateOf(0) }
-    val currentOnRepeat by rememberUpdatedState(onRepeat)
-    val currentOnFinished by rememberUpdatedState(onFinished)
-
-    LaunchedEffect(composition) {
-        progress.value = when (composition) {
-            null -> 0f
-            else -> if (speed >= 0) clipSpec?.getMinProgress(composition) ?: 0f else clipSpec?.getMaxProgress(composition) ?: 1f
-        }
-        currentRepeatCount = 0
+): LottieAnimationState {
+    val state = rememberLottieAnimationState(
+        iterations = iterations,
+        clipSpec = clipSpec,
+        speed = speed,
+    )
+    LaunchedEffect(composition, playMode) {
+        if (playMode == LottiePlayMode.Play) state.animate(composition)
     }
-
-    LaunchedEffect(composition, isPlaying, repeatCount, clipSpec, speed) {
-        if (!isPlaying || composition == null) return@LaunchedEffect
-        val minProgress = clipSpec?.getMinProgress(composition) ?: 0f
-        val maxProgress = clipSpec?.getMaxProgress(composition) ?: 1f
-        if (speed > 0 && (progress.value == 1f || restartOnPlay)) {
-            progress.value = minProgress
-        } else if (speed < 0 && (progress.value == 0f || restartOnPlay)) {
-            progress.value = maxProgress
-        }
-        if (restartOnPlay || currentRepeatCount >= repeatCount) {
-            currentRepeatCount = 0
-        }
-        var lastFrameTime = withFrameNanos { it }
-        var done = false
-        while (!done) {
-            withFrameNanos { frameTime ->
-                val dTime = (frameTime - lastFrameTime) / TimeUnit.MILLISECONDS.toNanos(1).toFloat()
-                lastFrameTime = frameTime
-                val dProgress = (dTime * speed) / composition.duration
-                val rawProgress = minProgress + ((progress.value - minProgress) + dProgress)
-                if (speed > 0 && rawProgress > maxProgress) {
-                    currentRepeatCount++
-                    currentOnRepeat?.invoke(repeatCount)
-                } else if (speed < 0 && rawProgress < minProgress) {
-                    currentRepeatCount++
-                    currentOnRepeat?.invoke(repeatCount)
-                }
-                done = if (currentRepeatCount < repeatCount && !rawProgress.isInfinite()) {
-                    progress.value = minProgress + ((rawProgress - minProgress) fmod (maxProgress - minProgress))
-                    false
-                } else {
-                    progress.value = when {
-                        speed >= 0 -> clipSpec?.getMaxProgress(composition) ?: 1f
-                        else -> clipSpec?.getMinProgress(composition) ?: 0f
-                    }
-                    true
-                }
-            }
-        }
-        currentOnFinished?.invoke()
-    }
-    return progress
+    return state
 }
-
-/**
- * Floor mod instead of % which is remainder. This allows negative speeds to properly wrap around to
- * the max progress.
- */
-private infix fun Float.fmod(other: Float) = ((this % other) + other) % other
\ No newline at end of file
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
index aedf9e1..af863df 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
@@ -41,6 +41,8 @@
  * until the animation completes. [LottieCancellationBehavior.Immediate] will immediately cancel the
  * animation and return early.
  *
+ * @return the ending frame time nanos
+ *
  * @see lottieTransition
  */
 suspend fun animateLottieComposition(
@@ -49,10 +51,14 @@
     clipSpec: LottieClipSpec? = null,
     cancellationBehavior: LottieCancellationBehavior = LottieCancellationBehavior.Immediate,
     speed: Float = 1f,
-) {
+    lastFrameTimeNanos: Long? = null,
+    startAtMinProgress: Boolean = true,
+): Long {
     require(speed != 0f) { "Speed must not be 0" }
     require(speed.isFinite()) { "Speed must be a finite number. It is $speed." }
-    composition ?: return
+    var lastFrameTime = lastFrameTimeNanos ?: withFrameNanos { it }
+    composition ?: return lastFrameTime
+
     val context = when (cancellationBehavior) {
         LottieCancellationBehavior.Immediate -> EmptyCoroutineContext
         LottieCancellationBehavior.AtEnd -> NonCancellable
@@ -61,10 +67,10 @@
         val minProgress = clipSpec?.getMinProgress(composition) ?: 0f
         val maxProgress = clipSpec?.getMaxProgress(composition) ?: 1f
         progress.value = when {
+            !startAtMinProgress -> progress.value.coerceIn(minProgress, maxProgress)
             speed >= 0 -> minProgress
             else -> maxProgress
         }
-        var lastFrameTime = withFrameNanos { it }
         var done = false
         while (!done) {
             withFrameNanos { frameTime ->
@@ -80,4 +86,5 @@
             }
         }
     }
+    return lastFrameTime
 }
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
index 3045941..a63aa5c 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
@@ -62,7 +62,7 @@
 private fun Example2() {
     LottieAnimation(
         LottieCompositionSpec.RawRes(R.raw.heart),
-        repeatCount = Integer.MAX_VALUE,
+        iterations = Integer.MAX_VALUE,
     )
 }
 
@@ -73,7 +73,7 @@
 private fun Example3() {
     LottieAnimation(
         LottieCompositionSpec.RawRes(R.raw.heart),
-        repeatCount = Integer.MAX_VALUE,
+        iterations = Integer.MAX_VALUE,
         clipSpec = LottieClipSpec.MinAndMaxProgress(0.5f, 0.75f),
     )
 }
@@ -116,13 +116,11 @@
 @Composable
 private fun Example6() {
     val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
-    val progress by animateLottieComposition(
-        composition,
-        repeatCount = Integer.MAX_VALUE,
-    )
+    val animationState = animateLottieComposition(composition)
+    animationState.iterations = Integer.MAX_VALUE
     LottieAnimation(
         composition,
-        progress,
+        animationState.value,
     )
 }
 
@@ -131,16 +129,18 @@
  */
 @Composable
 private fun Example7() {
-    var isPlaying by remember { mutableStateOf(false) }
+    var playMode by remember { mutableStateOf(LottiePlayMode.Play) }
+    val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+    val progress by animateLottieComposition(
+        composition,
+        playMode = playMode,
+        iterations = Integer.MAX_VALUE,
+    )
     LottieAnimation(
-        LottieCompositionSpec.RawRes(R.raw.heart),
-        repeatCount = Integer.MAX_VALUE,
-        // When this is true, it it will start from 0 every time it is played again.
-        // When this is false, it will resume from the progress it was pause at.
-        restartOnPlay = false,
-        isPlaying = isPlaying,
+        composition,
+        progress,
         modifier = Modifier
-            .clickable { isPlaying = !isPlaying }
+            .clickable { playMode = !playMode }
     )
 }
 
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
index 65a01de..6f4c090 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
@@ -42,44 +42,34 @@
 @Composable
 fun SingleCompositionTransition(state: Int) {
     val compositionResult = lottieComposition(LottieCompositionSpec.RawRes(R.raw.bar))
+    val animationState = rememberLottieAnimationState()
 
     // This version of lottieTransition is for when your transition is segments of a single animation.
     // It just takes state and returns progress.
-    val progress = lottieTransition(state) { progress ->
+    lottieTransition(state) {
         compositionResult.awaitOrNull() ?: return@lottieTransition
         when (state) {
             0 -> {
-                // This version of animateLottieComposition takes a MutableState<Float> as a parameter
-                // and then suspends until one iteration through the animation is complete.
-                animateLottieComposition(
-                    compositionResult.value,
-                    progress,
-                    clipSpec = LottieClipSpec.MinAndMaxProgress(0f, 0.301f),
-                    cancellationBehavior = LottieCancellationBehavior.AtEnd,
-                )
+                animationState.clipSpec = LottieClipSpec.MinAndMaxProgress(0f, 0.301f)
+                animationState.iterations = 1
+                animationState.animate(compositionResult.value, cancellationBehavior = LottieCancellationBehavior.AtEnd)
             }
             1 -> {
                 // To loop a segment, just wrap this in a while loop.
                 while (isActive) {
-                    animateLottieComposition(
-                        compositionResult.value,
-                        progress,
-                        clipSpec = LottieClipSpec.MinAndMaxProgress(0.301f, 2f / 3f),
-                        cancellationBehavior = LottieCancellationBehavior.AtEnd,
-                    )
+                    animationState.clipSpec = LottieClipSpec.MinAndMaxProgress(0.301f, 2f / 3f)
+                    animationState.iterations = Integer.MAX_VALUE
+                    animationState.animate(compositionResult.value, cancellationBehavior = LottieCancellationBehavior.AtEnd)
                 }
             }
             2 -> {
-                animateLottieComposition(
-                    compositionResult.value,
-                    progress,
-                    clipSpec = LottieClipSpec.MinAndMaxProgress(2f / 3f, 1f),
-                    cancellationBehavior = LottieCancellationBehavior.AtEnd,
-                )
+                animationState.clipSpec = LottieClipSpec.MinAndMaxProgress(0.301f, 2f / 3f)
+                animationState.iterations = 1
+                animationState.animate(compositionResult.value, cancellationBehavior = LottieCancellationBehavior.AtEnd)
             }
         }
     }
-    LottieAnimation(compositionResult.value, progress)
+    LottieAnimation(compositionResult.value, animationState.value)
 }
 
 @Composable
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
index 52f637d..718ebf6 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
@@ -1,5 +1,6 @@
 package com.airbnb.lottie.sample.compose.player
 
+import android.util.Log
 import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.expandVertically
@@ -45,9 +46,7 @@
 
 @Stable
 class PlayerPageState {
-    var isPlaying by mutableStateOf(true)
-    var repeatCount by mutableStateOf(Integer.MAX_VALUE)
-    var speed by mutableStateOf(1f)
+    var playMode by mutableStateOf(LottiePlayMode.Play)
     var outlineMasksAndMattes by mutableStateOf(false)
     var applyOpacityToLayers by mutableStateOf(false)
     var enableMergePaths by mutableStateOf(false)
@@ -173,13 +172,14 @@
             ImageAssetDelegate { if (it.hasBitmap()) null else it.toDummyBitmap(dummyBitmapStrokeWidth) }
         }
     }
-    val progress = animateLottieComposition(
+    val animationState = animateLottieComposition(
         composition,
-        state.isPlaying,
-        restartOnPlay = false,
-        repeatCount = state.repeatCount,
-        speed = state.speed,
-    ) { state.isPlaying = false }
+        playMode = state.playMode,
+        iterations = Integer.MAX_VALUE,
+    )
+    LaunchedEffect(animationState.iteration, animationState.iterations) {
+        Log.d("Gabe", "Iteration ${animationState.iteration}/${animationState.iterations}")
+    }
 
     Column(
         verticalArrangement = Arrangement.SpaceBetween,
@@ -194,7 +194,7 @@
         ) {
             LottieAnimation(
                 composition,
-                progress.value,
+                animationState.value,
                 imageAssetDelegate = imageAssetDelegate,
                 modifier = Modifier
                     .fillMaxSize()
@@ -210,7 +210,7 @@
             }
         }
         ExpandVisibility(state.speedToolbar && !state.focusMode) {
-            SpeedToolbar(state)
+            SpeedToolbar(animationState)
         }
         ExpandVisibility(!state.focusMode && state.backgroundColorToolbar) {
             BackgroundColorToolbar(
@@ -219,7 +219,7 @@
             )
         }
         ExpandVisibility(!state.focusMode) {
-            PlayerControlsRow(composition, progress, state)
+            PlayerControlsRow(composition, state, animationState)
         }
         ExpandVisibility(!state.focusMode) {
             Toolbar(state)
@@ -230,15 +230,15 @@
 @Composable
 private fun PlayerControlsRow(
     composition: LottieComposition?,
-    progress: MutableState<Float>,
     state: PlayerPageState,
+    animationState: LottieAnimationState,
 ) {
-    val totalTime = ((composition?.duration ?: 0L / state.speed) / 1000.0)
+    val totalTime = ((composition?.duration ?: 0L / animationState.speed) / 1000.0)
     val totalTimeFormatted = ("%.1f").format(totalTime)
 
-    val progressFormatted = ("%.1f").format(progress.value * totalTime)
+    val progressFormatted = ("%.1f").format(animationState.value * totalTime)
 
-    val frame = composition?.getFrameForProgress(progress.value)?.roundToInt() ?: 0
+    val frame = composition?.getFrameForProgress(animationState.value)?.roundToInt() ?: 0
     val durationFrames = ceil(composition?.durationFrames ?: 0f).roundToInt()
     Box(
         modifier = Modifier
@@ -251,10 +251,10 @@
                 contentAlignment = Alignment.Center
             ) {
                 IconButton(
-                    onClick = { state.isPlaying = !state.isPlaying },
+                    onClick = { state.playMode = !state.playMode },
                 ) {
                     Icon(
-                        if (state.isPlaying) Icons.Filled.Pause
+                        if (animationState.isPlaying) Icons.Filled.Pause
                         else Icons.Filled.PlayArrow,
                         contentDescription = null
                     )
@@ -268,18 +268,18 @@
                 )
             }
             Slider(
-                value = progress.value,
-                onValueChange = { progress.value = it },
+                value = animationState.value,
+                onValueChange = { animationState.value = it },
                 modifier = Modifier.weight(1f)
             )
             IconButton(
                 onClick = {
-                    state.repeatCount = if (state.repeatCount == Integer.MAX_VALUE) 1 else Integer.MAX_VALUE
+                    animationState.iterations = if (animationState.iterations == Integer.MAX_VALUE) 1 else Integer.MAX_VALUE
                 },
             ) {
                 Icon(
                     Icons.Filled.Repeat,
-                    tint = if (state.repeatCount == Integer.MAX_VALUE) Teal else Color.Black,
+                    tint = if (animationState.iterations == Integer.MAX_VALUE) Teal else Color.Black,
                     contentDescription = null
                 )
             }
@@ -297,7 +297,7 @@
 
 @Composable
 private fun SpeedToolbar(
-    state: PlayerPageState,
+    animationState: LottieAnimationState,
 ) {
     Row(
         horizontalArrangement = Arrangement.SpaceBetween,
@@ -308,26 +308,26 @@
     ) {
         ToolbarChip(
             label = "0.5x",
-            isActivated = state.speed == 0.5f,
-            onClick = { state.speed = 0.5f },
+            isActivated = animationState.speed == 0.5f,
+            onClick = { animationState.speed = 0.5f },
             modifier = Modifier.padding(end = 8.dp)
         )
         ToolbarChip(
             label = "1x",
-            isActivated = state.speed == 1f,
-            onClick = { state.speed = 1f },
+            isActivated = animationState.speed == 1f,
+            onClick = { animationState.speed = 1f },
             modifier = Modifier.padding(end = 8.dp)
         )
         ToolbarChip(
             label = "1.5x",
-            isActivated = state.speed == 1.5f,
-            onClick = { state.speed = 1.5f },
+            isActivated = animationState.speed == 1.5f,
+            onClick = { animationState.speed = 1.5f },
             modifier = Modifier.padding(end = 8.dp)
         )
         ToolbarChip(
             label = "2x",
-            isActivated = state.speed == 2f,
-            onClick = { state.speed = 2f },
+            isActivated = animationState.speed == 2f,
+            onClick = { animationState.speed = 2f },
             modifier = Modifier.padding(end = 8.dp)
         )
     }
@@ -470,8 +470,7 @@
 @Preview
 @Composable
 fun SpeedToolbarPreview() {
-    val state = remember { PlayerPageState() }
-    state.speed = 1f
+    val state = animateLottieComposition(null)
     SpeedToolbar(state)
 }