The first six chapters covered “how to make the glass look good”; this chapter covers “how to actually get it running”.
This chapter is an engineering manual: environment versions, build commands, where artifacts land, where to install, and how to fix errors. Every version number written here is a combination verified to compile in this project — not a suggested value.
⚠️ This repository does not include an APK. This is intentional: the APK is an artifact, not source code. To verify this Liquid Glass setup actually runs, build one yourself following this chapter — if you can build it, the environment is correct.
| Item | Value | Why this value |
|---|---|---|
| JDK | 17 (verified 17.0.20.1+1 Temurin) |
AGP 9 requires at least 17; using 11 fails to start (see §6.1) |
| Android Gradle Plugin | 9.3.2 |
agp in gradle/libs.versions.toml |
| Gradle | 9.7.1 (bundled wrapper) |
gradle/wrapper/gradle-wrapper.properties |
| Kotlin | 2.4.10 (with Compose compiler plugin) |
AGP 9 has built-in Kotlin support; no separate kotlin-android plugin needed |
| compileSdk | 37 |
app/build.gradle.kts |
| buildToolsVersion | 37.0.0 |
must match the version installed in the local SDK |
| targetSdk | 34 |
— |
| minSdk | 31 |
hard floor, see §1 |
| Compose | 1.12.0 |
runtime / ui / foundation / animation, same version |
| backdrop | io.github.kyant0:backdrop:2.0.0 |
the Liquid Glass engine itself |
| shapes | io.github.kyant0:shapes:1.2.0 |
Apple G2 continuous curvature (§3.3) |
Artifact size reference: app-release.apk ≈ 1.25 MB (with R8 fully enabled + resource compression).
minSdk ≥ 31This is the make-or-break line for whether the whole approach works — confirm it first.
The foundation of Liquid Glass is AGSL (Android Graphics Shading Language), which compiles shaders at runtime via android.graphics.RuntimeShader. RuntimeShader was only introduced in API 31 (Android 12).
minSdk = 31 ✅ has RuntimeShader, glass runs
minSdk = 30 ❌ no RuntimeShader, glass component crashes on first draw
What the crash looks like (on a real device):
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/graphics/RuntimeShader;
Or after R8 it becomes NoSuchMethodError / VerifyError.
If you must support API 30 and below, there is only one path: replace all
drawBackdropwith a degraded implementation ofModifier.blur()+ semi-transparentbackground(), and branch by version. That is not Liquid Glass — it is frosted plastic. This chapter does not cover that route.
Two other runtime prerequisites:
AndroidManifest.xml already declares android:hardwareAccelerated="true"; shaders run on the GPU, and software rendering fails outright or is extremely slow.Modifier.graphicsLayer: AGSL is only executed by the GPU pipeline.experiments/
├── README.md ← master overview
├── docs/ ← docs 01~08 (you are reading 07)
└── LiquidGlassShowcase/ ← the compilable project itself
├── gradlew / gradlew.bat ← wrapper (bundles gradle 9.7.1 download config)
├── gradle/wrapper/gradle-wrapper.jar ← ⚠️ must be committed too, otherwise the wrapper won't start
├── gradle/libs.versions.toml ← the single source of all version numbers
├── settings.gradle.kts / build.gradle.kts
├── gradle.properties ← jvmargs / parallel / cache switches
├── keystore/liquidglass-demo.jks ← demo signing (password is liquidglass throughout)
├── local.properties ← ⚠️ not in git, you must create it yourself (§3.1)
└── app/
├── build.gradle.kts ← minSdk / signing / R8 / packaging config
└── src/main/
├── AndroidManifest.xml
├── java/com/liquidglass/showcase/ ← all Kotlin source
└── res/ ← wallpaper webp / theme / icons
This repository does not include: APK, build/, .gradle/, local.properties, ~/.gradle cache. These are all artifacts or local-machine environment that you generate yourself after cloning.
local.properties (at the root of LiquidGlassShowcase/) is not in version control — you must create it yourself:
# Point to your local Android SDK root directory
sdk.dir=/path/to/android-sdk
You can also use an environment variable instead: ANDROID_HOME=/path/to/android-sdk (either one works, local.properties takes higher priority).
The SDK must actually have two things installed:
platforms/android-37.0 ← compileSdk = 37 needs this
build-tools/37.0.0 ← buildToolsVersion needs this
If missing, install with sdkmanager:
sdkmanager "platforms;android-37" "build-tools;37.0.0"
cd LiquidGlassShowcase
# ⚠️ Key: you must explicitly specify JDK 17. The system default java is most likely 11 and will fail directly.
export JAVA_HOME=/path/to/jdk-17
# Artifact: app/build/outputs/apk/release/app-release.apk
./gradlew assembleRelease
To just quickly verify the code compiles:
./gradlew assembleDebug # no obfuscation, no compression, debug-signed, fastest
./gradlew compileReleaseKotlin # compile Kotlin only, no packaging, faster
The first build downloads the Gradle 9.7.1 distribution + all dependencies, so network is required. After that everything goes through the ~/.gradle cache; in practice the second build onward drops from minutes to seconds.
In a persistent CI environment, pinning JAVA_HOME into the wrapper is also more convenient:
# LiquidGlassShowcase/gradle.properties (optional, for convenient handover)
org.gradle.java.home=/path/to/jdk-17
ls -lh app/build/outputs/apk/release/
# app-release.apk ← signed with keystore/liquidglass-demo.jks, directly installable
# output-metadata.json
Success indicator (what the verified output ends with):
BUILD SUCCESSFUL in 4m 1s
43 actionable tasks: 1 executed, 42 up-to-date
Seeing
42 up-to-datemeans it was an incremental build — only the one changed task was recompiled. That is normal, and it also confirmsorg.gradle.caching=trueis in effect.
adb install -r app/build/outputs/apk/release/app-release.apk
# list installed packages (applicationId = com.liquidglass.showcase)
adb shell pm list packages | grep liquidglass
The device must meet: Android 12 (API 31) or above + a GPU that supports hardware acceleration. An emulator works too, but pick an API 31+ image and enable hardware acceleration (GPU mode), otherwise the glass will not render.
After launch you should see: splash screen (1.6 s Liquid Glass loading animation) → fade-out scale 1.2 s → iOS system shell. If it goes black or crashes after the splash, troubleshoot in the order of §6.2.
Each block in app/build.gradle.kts exists for a reason:
| Config | Value | Why |
|---|---|---|
minSdk |
31 | AGSL floor, see §1 |
targetSdk |
34 | decoupled from compileSdk 37 to avoid too-new behavior changes |
isMinifyEnabled |
true | full R8 obfuscation, compresses 1.25 MB to acceptable |
isShrinkResources |
true | works with R8 to strip unreferenced resources |
signingConfigs.release |
built-in demo keystore | lets assembleRelease directly produce an installable package, no self-signing needed |
packaging.dex.useLegacyPackaging |
true | compatibility with older installers |
dependenciesInfo.includeInApk |
false | strip dependency metadata, smaller package |
lint.checkReleaseBuilds |
false | don’t run lint on release builds, avoid unrelated warnings interrupting CI |
vcsInfo.include |
false | don’t write VCS info into the APK |
⚠️ The demo keystore is public (password
liquidglass, see §7 license). It is only used so “this sample can be installed directly” — do not use it to sign your own production app.
| Symptom | Root cause | Fix |
|---|---|---|
Android Gradle plugin requires Java 17 to run. You are currently using Java 11. |
default java is 11 |
export JAVA_HOME=/path/to/jdk-17, or write org.gradle.java.home into gradle.properties |
Unsupported class file major version 6x |
same as above, JDK version mismatch | same as above |
SDK location not found. Define a valid SDK location with an ANDROID_HOME environment variable or by setting the sdk.dir path in your project's local.properties file. |
no local.properties and no ANDROID_HOME |
see §3.1 |
Failed to find Build Tools revision 37.0.0 |
that build-tools version is not installed in the SDK | sdkmanager "build-tools;37.0.0" |
Failed to find platform 'android-37' / compileSdk 37 not found |
platform 37 not installed in the SDK | sdkmanager "platforms;android-37" |
Could not resolve io.github.kyant0:backdrop:2.0.0 |
no network / proxy not configured | check HTTP_PROXY / HTTPS_PROXY; confirm mavenCentral() is reachable (the repo is configured in settings.gradle.kts in §2) |
Could not find io.github.kyant0:shapes |
only added backdrop, not shapes | both libs.versions.toml + app/build.gradle.kts need it (§2) |
./gradlew: Permission denied |
wrapper script has no execute bit | chmod +x gradlew |
wrapper won’t start / Could not find or load main class org.gradle.wrapper.GradleWrapperMain |
gradle/wrapper/gradle-wrapper.jar was not committed |
confirm this file exists in the repo |
| Symptom | Root cause | Fix |
|---|---|---|
NoClassDefFoundError: android.graphics.RuntimeShader |
minSdk < 31 or running on a device with API < 31 |
switch to an API 31+ device; confirm minSdk = 31 |
| glass is all-black / fully transparent square | backdrop didn’t capture content | most common: check whether Modifier.layerBackdrop(...) is attached to the node that actually draws the wallpaper (README §1 Iron Rule 1, docs/02-Backdrop-Layered-Architecture §2) |
| glass shows “the previous frame” | the recording node draws later than the sampling node | the content layer must draw before the overlay (docs/02-Backdrop-Layered-Architecture §2.4, §6) |
| frame drops on any movement | unconditionally attached layerBackdrop |
switch to conditional recording (docs/02-Backdrop-Layered-Architecture §7, docs/05-iOS-Shell-in-Practice §3.2) |
| entire UI is gray, no color | missing vibrancy() |
add vibrancy() as the first line of effects (docs/01-Optical-Model-of-Liquid-Glass §2) |
| red/blue fringing on edges | chromatic aberration left on in static state | chromaticAberration = false, only enable on press/drag (README Iron Rule 4) |
| corners look “wrong”, like ordinary rounded corners (non-refracting outline) | used androidx.compose.foundation.shape.RoundedCornerShape |
use com.kyant.shapes.RoundedRectangle (G2 continuous curvature, docs/03-API-Complete-Reference §3) — but NOT as the drawBackdrop shape when lens() is present; there it crashes (docs/10 §1) |
| noticeable heating on real device | recording layer does a full-screen offscreen render every frame | see §8 |
| Symptom | Root cause | Fix |
|---|---|---|
Unresolved reference: lens / vibrancy |
no matching import in effects, or wrong backdrop version |
confirm dependency is 2.0.0; lens / blur / vibrancy are all members of BackdropEffectScope |
Unresolved reference: RoundedRectangle |
used the foundation package | import com.kyant.shapes.RoundedRectangle |
@Composable invocations can only happen from ... |
called rememberLayerBackdrop() etc. outside a Composable scope |
rememberLayerBackdrop must be called inside @Composable |
Type mismatch: inferred type is Float but Dp was expected (or vice versa) |
forgot .dp.toPx() |
length params for lens / blur are always xx.dp.toPx() (README Iron Rule 3) |
| Compose compiler reports version mismatch | Kotlin and Compose compiler plugin versions inconsistent | this project uses AGP 9 built-in Kotlin support + org.jetbrains.kotlin.plugin.compose same version (2.4.10); do not manually add composeOptions.kotlinCompilerExtensionVersion |
When something goes wrong, rule out in this order; 90% of glass problems surface in steps 1–2:
Step 1 Replace the backdrop of some drawBackdrop with a fixed-color EmptyBackdrop / solid-color texture
→ If the glass "lights up", the problem is backdrop recording, not material parameters
Step 2 Temporarily remove the tint in onDrawSurface
→ If refraction appears, the tint was covering the glass (README Iron Rule 6)
Step 3 Temporarily turn off highlight / shadow
→ To confirm whether "wrong edges" is a highlight-parameter issue or a shape issue
Always confirm “is the background captured” first, then tune parameters. Parameters can’t conjure content.
| Object | License | What you must do |
|---|---|---|
io.github.kyant0:backdrop |
Apache-2.0 (Kyant0/AndroidLiquidGlass) | keep NOTICE, declare under Apache-2.0 |
io.github.kyant0:shapes |
Apache-2.0 (Kyant0/AndroidShapes) | same as above |
| files in this project ported from upstream | Apache-2.0 | do not delete the Copyright 2025 Kyant headers in files like core/utils/* |
new code in this project (ios/, GlassMaterials.kt cleanup, etc.) |
Copyright 2026 The Liquid Glass Showcase authors |
see LICENSE / NOTICE |
keystore/liquidglass-demo.jks |
demo use only, public password (liquidglass) |
do not use for production release signing |
icons assets from ui-icons-hub |
see the license summary table in docs/06-Icons-and-Asset-Pipeline §9 |
that section has per-pipeline license notes |
The only systematic cost of Liquid Glass is the layerBackdrop recording layer: a full-screen offscreen render every frame. How to verify:
Draw / Sync in the bar chart. When the glass is static and no overlay is open, the bar height should be clearly lower than when a panel is open.adb shell dumpsys gfxinfo com.liquidglass.showcase framestats
Look at the janky frames ratio.layerBackdrop stay attached),
the frame rate should drop noticeably — that difference is what “conditional recording” saved you.Optimization checklist (ordered by benefit, details in docs/02-Backdrop-Layered-Architecture §9):
layerBackdrop when an overlay actually needs to read it (biggest benefit);If you are getting this effect running from scratch, follow this order strictly:
① Read README.md → build a mental model (background texture → sampling → refraction), memorize the ten iron rules
② Read docs/01-Optical-Model-of-Liquid-Glass → understand the mapping of 6 visual cues ↔ 6 APIs
③ Read docs/02-Backdrop-Layered-Architecture → clarify LayerBackdrop / Combined / feedback-loop prevention
④ Read docs/03-API-Complete-Reference → go through all parameters of drawBackdrop
⑤ Copy the recipes of docs/04-Material-Recipe-Table → don't invent parameters yourself
⑥ Build a page following the minimal example in docs/03-API-Complete-Reference, confirm the glass displays
└─ If it doesn't display → go back to §6.4 and rule out step by step, first confirm whether backdrop captured content
⑦ Need a full App shell → read docs/05-iOS-Shell-in-Practice (z-order, backdrop allocation table, conditional recording)
⑧ Need icons → read docs/06-Icons-and-Asset-Pipeline (difference between the two pipelines + the Apple icon-zone switching scheme)
⑨ Won't compile → this chapter
Four engineering constraints that must not be violated (violating them doesn’t mean “ugly”, it means “won’t run”):
minSdk ≥ 31;effects order is always color → blur → lens;.dp.toPx().sdk.dir in local.properties → point JAVA_HOME at JDK 17 → ./gradlew assembleRelease.app-release.apk, it means the complete Liquid Glass environment has been successfully recreated.At this point, README + docs/01–docs/08 cover: optical principles → architecture → API → recipes → shell in practice → assets → build and troubleshooting.