
High-performance APNG parsing and rendering with precomposed frames, Compose-style painters, declarative and imperative playback controls, optional HTTP network loading and disk LRU caching.
A high-performance APNG (Animated PNG) parsing and rendering library built with Kotlin Multiplatform(KMP). It provides consistent animation rendering across Android, iOS, Desktop (JVM), and Web (JS/Wasm).
| Android | Desktop (JVM) | iOS | macOS | JS | WasmJS |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Add the dependencies you need to your build.gradle.kts:
// Core: APNG parsing + frame composing + Compose animation rendering (required)
implementation("io.github.lugf027:apng-core:<version>")
// Network: Ktor-based URL loading with disk LRU caching
implementation("io.github.lugf027:apng-network:<version>")
// Resources: Load APNG from KMP Resources (Res)
implementation("io.github.lugf027:apng-resources:<version>")
apng-networktransitively includesapng-network-core(disk cache layer). You only needapng-network-coredirectly if you want caching without the built-in Ktor client.
// From byte array
val composition by rememberApngComposition(
ApngCompositionSpec.Bytes(byteArray, cacheKey = "my_anim")
)
// From URL (requires apng-network)
val composition by rememberApngComposition(
ApngCompositionSpec.Url("https://example.com/animation.apng")
)
// From Compose Resources (requires apng-resources)
val composition by rememberApngComposition(
ApngCompositionSpec.ComposeResource { Res.readBytes("files/animation.apng") }
)Declarative (recommended):
val progress by animateApngCompositionAsState(
composition,
isPlaying = true,
iterations = Apng.IterateForever,
)
val painter = rememberApngPainter(composition, progress = { progress })
Image(painter = painter, contentDescription = null)Imperative:
val animatable = rememberApngAnimatable()
LaunchedEffect(composition) {
animatable.animate(composition, iterations = Apng.IterateForever)
}
val painter = rememberApngPainter(composition, progress = { animatable.progress })
Image(painter = painter, contentDescription = null)Shorthand:
val painter = rememberApngPainter(composition, isPlaying = true)
Image(painter = painter, contentDescription = null)val progress by animateApngCompositionAsState(
composition,
isPlaying = true,
speed = 1.5f, // Playback speed
iterations = Apng.IterateForever, // Loop forever (or pass an Int)
clipSpec = ApngClipSpec.Frame(2, 8), // Play only frames 2–8
)| Module | Artifact | Description |
|---|---|---|
| apng-core | io.github.lugf027:apng-core |
APNG parsing, frame composing, and Compose animation rendering |
| apng-network-core | io.github.lugf027:apng-network-core |
Disk LRU cache and network caching strategy |
| apng-network | io.github.lugf027:apng-network |
Ktor-based network loading with built-in disk caching |
| apng-resources | io.github.lugf027:apng-resources |
KMP Resources integration |
apng-core ← standalone
apng-network-core ← apng-core + okio
apng-network ← apng-core + apng-network-core + ktor-client-core
apng-resources ← apng-core + compose.components.resources
| Class / Function | Purpose |
|---|---|
ApngComposition |
Pre-composed frame data container |
ApngCompositionSpec |
Loading specification (Bytes / Url / ComposeResource) |
rememberApngComposition() |
Composable that loads and caches a composition |
animateApngCompositionAsState() |
Declarative animation driver |
rememberApngAnimatable() |
Imperative animation controller |
rememberApngPainter() |
Creates a Painter from a composition + progress |
ApngClipSpec |
Frame/progress range clipping |
Apng.IterateForever |
Constant for infinite looping |
| Feature | Android | Skiko (JVM / iOS / macOS / JS / WasmJS) |
|---|---|---|
| Image decoding | BitmapFactory |
Image.makeFromEncoded (Skia) |
| Frame composing | Android Canvas + PorterDuff | Skia Canvas + BlendMode |
| Disk cache | ✅ (FileSystem.SYSTEM) |
JVM/Native ✅ · Web ❌ |
# Desktop
./gradlew :example:desktopApp:run
# Android
./gradlew :example:androidApp:installDebug
# Web (JS)
./gradlew :example:webApp:jsBrowserDevelopmentRun
# Web (WasmJS)
./gradlew :example:webApp:wasmJsBrowserDevelopmentRun
# iOS — open in Xcode
open example/iosApp/iosApp.xcodeprojMIT License
Copyright (c) 2026 lugf027
See LICENSE for details.
A high-performance APNG (Animated PNG) parsing and rendering library built with Kotlin Multiplatform(KMP). It provides consistent animation rendering across Android, iOS, Desktop (JVM), and Web (JS/Wasm).
| Android | Desktop (JVM) | iOS | macOS | JS | WasmJS |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Add the dependencies you need to your build.gradle.kts:
// Core: APNG parsing + frame composing + Compose animation rendering (required)
implementation("io.github.lugf027:apng-core:<version>")
// Network: Ktor-based URL loading with disk LRU caching
implementation("io.github.lugf027:apng-network:<version>")
// Resources: Load APNG from KMP Resources (Res)
implementation("io.github.lugf027:apng-resources:<version>")
apng-networktransitively includesapng-network-core(disk cache layer). You only needapng-network-coredirectly if you want caching without the built-in Ktor client.
// From byte array
val composition by rememberApngComposition(
ApngCompositionSpec.Bytes(byteArray, cacheKey = "my_anim")
)
// From URL (requires apng-network)
val composition by rememberApngComposition(
ApngCompositionSpec.Url("https://example.com/animation.apng")
)
// From Compose Resources (requires apng-resources)
val composition by rememberApngComposition(
ApngCompositionSpec.ComposeResource { Res.readBytes("files/animation.apng") }
)Declarative (recommended):
val progress by animateApngCompositionAsState(
composition,
isPlaying = true,
iterations = Apng.IterateForever,
)
val painter = rememberApngPainter(composition, progress = { progress })
Image(painter = painter, contentDescription = null)Imperative:
val animatable = rememberApngAnimatable()
LaunchedEffect(composition) {
animatable.animate(composition, iterations = Apng.IterateForever)
}
val painter = rememberApngPainter(composition, progress = { animatable.progress })
Image(painter = painter, contentDescription = null)Shorthand:
val painter = rememberApngPainter(composition, isPlaying = true)
Image(painter = painter, contentDescription = null)val progress by animateApngCompositionAsState(
composition,
isPlaying = true,
speed = 1.5f, // Playback speed
iterations = Apng.IterateForever, // Loop forever (or pass an Int)
clipSpec = ApngClipSpec.Frame(2, 8), // Play only frames 2–8
)| Module | Artifact | Description |
|---|---|---|
| apng-core | io.github.lugf027:apng-core |
APNG parsing, frame composing, and Compose animation rendering |
| apng-network-core | io.github.lugf027:apng-network-core |
Disk LRU cache and network caching strategy |
| apng-network | io.github.lugf027:apng-network |
Ktor-based network loading with built-in disk caching |
| apng-resources | io.github.lugf027:apng-resources |
KMP Resources integration |
apng-core ← standalone
apng-network-core ← apng-core + okio
apng-network ← apng-core + apng-network-core + ktor-client-core
apng-resources ← apng-core + compose.components.resources
| Class / Function | Purpose |
|---|---|
ApngComposition |
Pre-composed frame data container |
ApngCompositionSpec |
Loading specification (Bytes / Url / ComposeResource) |
rememberApngComposition() |
Composable that loads and caches a composition |
animateApngCompositionAsState() |
Declarative animation driver |
rememberApngAnimatable() |
Imperative animation controller |
rememberApngPainter() |
Creates a Painter from a composition + progress |
ApngClipSpec |
Frame/progress range clipping |
Apng.IterateForever |
Constant for infinite looping |
| Feature | Android | Skiko (JVM / iOS / macOS / JS / WasmJS) |
|---|---|---|
| Image decoding | BitmapFactory |
Image.makeFromEncoded (Skia) |
| Frame composing | Android Canvas + PorterDuff | Skia Canvas + BlendMode |
| Disk cache | ✅ (FileSystem.SYSTEM) |
JVM/Native ✅ · Web ❌ |
# Desktop
./gradlew :example:desktopApp:run
# Android
./gradlew :example:androidApp:installDebug
# Web (JS)
./gradlew :example:webApp:jsBrowserDevelopmentRun
# Web (WasmJS)
./gradlew :example:webApp:wasmJsBrowserDevelopmentRun
# iOS — open in Xcode
open example/iosApp/iosApp.xcodeprojMIT License
Copyright (c) 2026 lugf027
See LICENSE for details.