High Performance Liquid glass for Jetpack Compose — backdrop sampling, blur, edge refraction, specular highlights, and interactive glass controls with automatic degradation on older Android versions.
Prismal brings an iOS-style liquid glass look to Android Compose apps. Glass surfaces sample live content behind them, refract it at the edges, and respond to touch with spring physics and press ripples. On devices that cannot run AGSL shaders or RenderEffect, the library falls back gracefully so you ship one API everywhere.
Successor to Prismal (OpenGL ES)
The original styropyr0/Prismal library is still available. It targets the View system and renders glass effects with OpenGL ES. This repo (PrismalAGSL) is the Compose reimplementation: same visual language and component ideas, rebuilt on AGSL,RenderEffect, and Compose-first APIs. Use the original if you need OpenGL ES on Views; use this library for Jetpack Compose.
| Browse — toggle & slider | Refraction & blur settings |
![]() |
![]() |
| Search | Profile |
![]() |
![]() |
| Profile menu & tabs | Horizontal Scroll Selector |
![]() |
![]() |
| Widget preview | Widget preview — refraction |
![]() |
![]() |
- Screenshots
- Relation to Prismal (OpenGL ES)
- Features
- Platform tiers
- Requirements
- Installation
- Quick start
- Core concepts
- Built-in components
- Custom glass surfaces
- Effects API
- Shapes
- Adaptive luminance
- Interactive primitives
- Bottom tab bar
- Horizontal selector
- Ruler selector
- Demo app
- Project structure
- License
- Links
| Prismal | PrismalAGSL (this repo) | |
|---|---|---|
| UI | Android Views | Jetpack Compose |
| Rendering | OpenGL ES | AGSL + RenderEffect + legacy frost fallback |
| Status | Maintained separately | Compose successor |
| Repo | github.com/styropyr0/Prismal | github.com/styropyr0/PrismalAGSL |
Both libraries share the same Prismal glass aesthetic (blur, refraction, interactive controls). APIs differ because this version is idiomatic Compose — modifiers, composables, and PrismalBackdrop layers instead of OpenGL ES surfaces on Views.
- Backdrop-driven glass — sample wallpaper, scrollable content, or other composables through frosted panels
- Three automatic pipeline tiers — Legacy frost (API 25–30),
RenderEffectblur (API 31–32), full AGSL liquid glass (API 33+) - Edge lens refraction — rounded-rectangle SDF shaders with optional chromatic aberration
- Specular, depth shadow, and depth inset — decorative layers on top of the refracted backdrop
- Ready-made components — buttons, toggles, sliders, progress bars, gradient panels, horizontal selector, ruler selector, and an iOS-style bottom tab bar with a draggable selection droplet
- Custom glass builder —
drawPrismalGlass,PrismalLiquidGlass, and composable effect helpers - Adaptive luminance — optional brightness sampling to tune blur and foreground contrast against the backdrop
- Press interactions — ripple, scale, and spring motion shared across components
Prismal selects a pipeline at runtime. You normally do not branch on this in app code — unsupported effects are skipped automatically.
| Tier | API | What you get |
|---|---|---|
| Legacy | 25–30 | Sampled backdrop + frosted surface overlay (legacyFrostStrength) |
| Standard | 31–32 | Backdrop blur and color filters via RenderEffect |
| Liquid | 33+ | Full AGSL refraction, dispersion, gradient glass, custom runtime shaders |
Query capabilities when needed:
import com.styropyr0.prismal.PrismalGlass
PrismalGlass.pipeline // Legacy | Standard | Liquid
PrismalGlass.supportsBackdropBlur // API 31+
PrismalGlass.supportsRefraction // API 33+
PrismalGlass.supportsAgslEffects // API 33+
PrismalGlass.isSupported() // API 25+AGSL shader assets are loaded once at startup via AndroidX App Startup (PrismalAgslInitializer). No manual initialization is required.
| minSdk | 25 (Android 7.0) |
| compileSdk | 37+ recommended |
| UI toolkit | Jetpack Compose (library uses Compose BOM) |
| Kotlin | 2.2+ (Compose Compiler plugin required) |
Prismal is compiled and released with Kotlin 2.2.x. Apps on an older Kotlin compiler cannot consume a library built with a newer one (Kotlin metadata is not forward-compatible that way). If you hit “incompatible Kotlin metadata” errors, upgrade the app’s Kotlin Gradle / Compose Compiler plugin to 2.2 or newer.
The library depends on Compose Foundation and UI. Your app module must enable the Compose compiler:
plugins {
id("org.jetbrains.kotlin.plugin.compose")
}In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven(url = "https://jitpack.io")
}
}Replace TAG with a GitHub release tag (for example 1.0.0):
dependencies {
implementation("com.github.styropyr0:PrismalAGSL:Prismal:TAG")
}Artifact coordinates:
| Field | Value |
|---|---|
groupId |
com.github.styropyr0 |
artifactId |
Prismal |
repo |
PrismalAGSL |
Every glass surface needs a backdrop — content that will be sampled and refracted. The usual pattern is a full-screen background layer plus glass UI on top.
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.styropyr0.prismal.PrismalLiquidGlass
import com.styropyr0.prismal.components.PrismalGlassButton
import com.styropyr0.prismal.drawPrismalGlass
import com.styropyr0.prismal.prismalGlassEffects
import com.styropyr0.prismal.shapes.PrismalRoundedRectangle
import com.styropyr0.prismal.sources.prismalGlassLayer
import com.styropyr0.prismal.sources.rememberPrismalGlassLayer
@Composable
fun GlassDemoScreen() {
val backdrop = rememberPrismalGlassLayer()
Box(Modifier.fillMaxSize()) {
// 1. Record backdrop content
Wallpaper(
modifier = Modifier
.fillMaxSize()
.prismalGlassLayer(backdrop)
)
// 2. Draw glass on top, sampling the backdrop
PrismalGlassButton(
onClick = { },
backdrop = backdrop,
modifier = Modifier
.align(Alignment.Center)
.padding(24.dp)
) {
Text("Hello Glass")
}
}
}For a panel with the standard liquid-glass recipe and no extra boilerplate:
import androidx.compose.ui.platform.LocalDensity
import com.styropyr0.prismal.drawPrismalGlass
import com.styropyr0.prismal.prismalGlassEffects
val density = LocalDensity.current
Box(
Modifier
.padding(16.dp)
.drawPrismalGlass(
backdrop = backdrop,
shape = { PrismalRoundedRectangle(24.dp) },
effects = prismalGlassEffects(density)
)
) {
Text("Custom panel", modifier = Modifier.padding(20.dp))
}A pixel source that glass surfaces sample. Implementations include:
| API | Purpose |
|---|---|
rememberPrismalGlassLayer() |
Captures a composable subtree via Modifier.prismalGlassLayer() |
rememberPrismalCanvasSource { } |
Procedural draw callback |
rememberPrismalMergedSource(a, b, …) |
Layers multiple sources |
rememberPrismalWrappedSource(source) { } |
Transform or filter an existing source |
emptyGlassSource() |
Transparent / empty sample |
Attach a layer to background content:
val backdrop = rememberPrismalGlassLayer()
BackgroundContent(
modifier = Modifier
.fillMaxSize()
.prismalGlassLayer(backdrop)
)Call backdrop.readSamplingState() (or use components that do this internally) so glass redraws once the layer is positioned.
| Modifier | Description |
|---|---|
Modifier.drawPrismalGlass(...) |
Full glass: effects + optional specular, depth shadow, depth inset |
Modifier.drawPlainPrismalGlass(...) |
Effects only — no decorative edge layers |
Both accept:
backdrop— what to refractshape— clip shape (usePrismalRoundedRectangleorPrismalCapsulefor lens effects)effects—PrismalGlassEffectProviderblock (blur, lens, color controls, …)onDrawSurface/onDrawBehind/onDrawFront— custom drawing hookslayerBlock— optionalgraphicsLayertransform during sampling (press scale, etc.)
PrismalLiquidGlass.applyBase() applies the calibrated blur + vibrancy + refraction recipe used across the library. Build on it with prismalGlassEffects:
drawPrismalGlass(
backdrop = backdrop,
shape = { PrismalRoundedRectangle(20.dp) },
effects = prismalGlassEffects(density, adaptiveLuminance = true, luminance = { 0.6f }) {
// Optional overrides after the base recipe:
colorControls(saturation = 1.8f)
}
)All components take a backdrop: PrismalBackdrop and degrade effects automatically.
| Component | Package | Description |
|---|---|---|
PrismalGlassSurface |
com.styropyr0.prismal |
General glass container; optional click + tint |
PrismalGlassButton |
…components |
Capsule button; fully tunable shape, effects, specular, depth, tint |
PrismalGlassToggle |
…components |
Spring-animated switch |
PrismalGlassSlider |
…components |
Track + refracting thumb |
PrismalGlassProgressBar |
…components |
Determinate or indeterminate track |
PrismalGradientGlassPanel |
…components |
Vertical gradient blur / refraction panel |
PrismalHorizontalSelector |
…components |
iOS-style camera-mode picker with fixed liquid droplet |
PrismalRulerSelector |
…components |
Ruler / tick picker with value shown inside a fixed liquid droplet |
PrismalGlassBottomTabs |
…components |
iOS-style tab bar with sliding liquid droplet |
PrismalGlassBottomTab |
…components |
Single tab item for the bottom bar |
Example — toggle and slider:
PrismalGlassToggle(
selected = { enabled },
onSelect = { enabled = it },
backdrop = backdrop
)
PrismalGlassSlider(
value = { sliderValue },
onValueChange = { sliderValue = it },
valueRange = 0f..1f,
visibilityThreshold = 0.01f,
backdrop = backdrop,
modifier = Modifier.fillMaxWidth()
)Example — progress bar:
PrismalGlassProgressBar(
progress = { downloadProgress }, // 0f..1f
backdrop = backdrop,
indeterminate = false,
modifier = Modifier.fillMaxWidth()
)Use PrismalGlassSurface when you want a boxed layout with optional click handling:
PrismalGlassSurface(
backdrop = backdrop,
shape = { PrismalRoundedRectangle(16.dp) },
onClick = { /* optional */ },
tint = Color(0xFF5856D6),
tintAlpha = 0.28f,
blurRadius = 8.dp,
adaptiveLuminance = true,
luminance = { luminanceState.luminance },
modifier = Modifier.fillMaxWidth()
) {
Text("Content inside glass", modifier = Modifier.padding(16.dp))
}For full control, use drawPrismalGlass directly and compose specular / depth yourself:
import com.styropyr0.prismal.depth.PrismalDepthInset
import com.styropyr0.prismal.depth.PrismalDepthShadow
import com.styropyr0.prismal.specular.PrismalSpecular
Modifier.drawPrismalGlass(
backdrop = backdrop,
shape = { PrismalCapsule() },
effects = {
applyPrismalGlassEffects(
density = density,
adaptiveLuminance = false,
luminance = 0.5f,
blurRadiusPx = with(density) { 8.dp.toPx() },
refractionHeightPx = with(density) { 12.dp.toPx() },
refractionAmountPx = with(density) { 24.dp.toPx() },
chromaticAberration = 0.2f
)
},
specular = { PrismalSpecular.Default },
depthShadow = { PrismalDepthShadow.Default },
depthInset = { PrismalDepthInset(radius = 8.dp) },
onDrawSurface = { drawRect(Color.White.copy(alpha = 0.08f)) }
)Effects are configured inside a PrismalGlassEffectProvider receiver block.
| Function | Description |
|---|---|
applyPrismalGlassEffects(...) |
Standard blur + vibrancy + lens chain. With adaptive on, blurRadiusPx is the base radius scaled by adaptiveTuning. |
PrismalAdaptiveTuning |
Caps for adaptive brightness / contrast / blur scales (Standard, Subtle, or custom) |
prismalBlur(radiusPx) |
Gaussian backdrop blur (API 31+; frost fallback on Legacy) |
prismalLens(...) |
Edge refraction shader (API 33+, requires rounded shape). chromaticAberration is [0, 1] (0.2 = 20% dispersion) |
prismalGradientGlass(...) |
Vertical gradient blur panel (API 33+) |
vibrancy() |
iOS-like saturation / brightness boost |
colorControls(brightness, contrast, saturation) |
Manual color grading |
colorFilter(filter) |
Arbitrary ColorFilter |
opacity(alpha) |
Alpha composite |
effect(renderEffect) |
Append a raw RenderEffect |
prismalAGSLShaderEffect(...) |
Custom AGSL runtime shader (API 33+) |
Legacy devices accumulate legacyFrostStrength when blur is requested but unavailable — a frosted overlay is drawn on the glass surface instead.
Lens refraction requires a shape that exposes corner radii to the shader.
| Shape | Notes |
|---|---|
PrismalRoundedRectangle(radius) |
Uniform corners; continuous curvature by default |
PrismalCapsule() |
Fully rounded ends — used by buttons and tab bar |
CornerBasedShape |
Supported by prismalLens with extracted radii |
Prefer PrismalRoundedRectangle over raw RoundedCornerShape when you need refraction — it implements PrismalRoundedRectangularShape with continuous iOS-like corners.
Sample backdrop brightness and tune glass + foreground automatically:
import com.styropyr0.prismal.effects.PrismalAdaptiveTuning
import com.styropyr0.prismal.effects.rememberPrismalAdaptiveLuminance
val luminanceState = rememberPrismalAdaptiveLuminance(
enabled = true,
source = backdropLayer,
isLightTheme = !isSystemInDarkTheme(),
// initialLuminance defaults to 0.5 — avoids a white flash before the first sample
)
PrismalGlassButton(
onClick = { },
backdrop = backdropLayer,
adaptiveLuminance = true,
luminance = { luminanceState.luminance },
blurRadius = 8.dp, // base blur — still used when adaptive
adaptiveTuning = PrismalAdaptiveTuning.Subtle, // optional quieter preset
tint = Color(0xFF007AFF),
tintAlpha = 0.28f, // keep colored glass see-through
) {
Text(
"Adaptive",
color = luminanceState.contentColor
)
}Behavior notes
- Starts at luminance
0.5until the first probe sample (not1in light theme). - Adaptive color controls keep a contrast floor (
0.75Standard /0.88Subtle) so bright video does not wash glass to solid white. - When
adaptiveLuminance = true,blurRadiusPx/blurRadiusis the base radius scaled by [PrismalAdaptiveTuning] (maxBlurScale/minBlurScale) — it is not ignored. - Use
PrismalAdaptiveTuning.Subtlefor quieter UI chrome, or pass a customPrismalAdaptiveTuning(...). - When
enabled = false, luminance freezes at the neutral midpoint (0.5).
PrismalGlassButton is a capsule glass button with a press ripple, drag-follow scale, and optional tint. Every glass layer is exposed, so it can be tuned all the way down without wrapping it in a custom component.
PrismalGlassButton(
onClick = { },
backdrop = backdropLayer,
luminance = { luminanceState.luminance },
shape = { PrismalRoundedRectangle(16.dp) },
refractionHeight = 16.dp,
refractionAmount = 32.dp,
chromaticAberration = 0.2f,
specular = { PrismalSpecular.Ambient },
depthShadow = null, // null disables the layer
depthInset = { PrismalDepthInset(radius = 8.dp) },
contentPadding = PaddingValues(horizontal = 20.dp),
) {
Text("Tuned")
}| Parameter | Default | Description |
|---|---|---|
onClick / backdrop |
— | Click handler and the source sampled through the glass |
isInteractive |
true |
Disables press ripple and scale feedback when false |
shape |
PrismalCapsule() |
Glass outline |
height |
48.dp |
Fixed row height, or null to let the modifier / content size it |
blurRadius |
8.dp |
Backdrop blur; base radius when adaptiveLuminance = true |
refractionHeight / refractionAmount |
12.dp / 24.dp |
Thickness of the refracting edge band and how far it bends |
brightness / saturation |
0f / 1.5f |
Color controls applied to the sampled backdrop |
chromaticAberration |
0f |
RGB dispersion in [0, 1] (API 33+) |
depthEffect / useVibrancy |
false / true |
Extra depth pass and vibrancy on the glass |
adaptiveLuminance / luminance / adaptiveTuning |
false / { 0.5f } / Standard |
Adaptive brightness wiring |
effects |
null |
Replaces the whole built-in effect stack; receives the current luminance |
specular |
PrismalSpecular.Default |
Specular highlight; null disables it |
depthShadow |
PrismalDepthShadow.Default |
Outer depth shadow; null disables it |
depthInset |
null |
Inner depth inset |
nestedGlassSource |
null |
Layer this button contributes to for nested glass |
tint / tintAlpha |
Unspecified / PrismalDefaultTintAlpha |
Color overlay on the glass surface |
surfaceColor |
Unspecified |
Solid fill drawn on the surface |
onDrawSurface |
null |
Extra surface drawing, applied after tint and surfaceColor |
pressLift |
4.dp |
Scale-up amount while pressed |
contentPadding |
16.dp horizontal |
Padding around the content |
horizontalArrangement / verticalAlignment |
centered | Row layout of the content |
Lower-level building blocks used by components:
| Class | Use |
|---|---|
PrismalPressRipple |
Touch ripple + press progress for specular and scale |
PrismalSpringMotion |
Spring drag controller for sliders, toggles, tab droplet |
Attach PrismalPressRipple.modifier and gestureModifier to a glass surface and read pressProgress in layerBlock or effect blocks.
PrismalGlassBottomTabs renders an iOS-style bar with a draggable liquid selection pill.
import com.styropyr0.prismal.components.LocalPrismalBottomTabHighlightedIndex
import com.styropyr0.prismal.components.PrismalGlassBottomTab
import com.styropyr0.prismal.components.PrismalGlassBottomTabs
var selectedTab by remember { mutableIntStateOf(0) }
PrismalGlassBottomTabs(
selectedTabIndex = { selectedTab },
onTabSelected = { selectedTab = it },
backdrop = backdrop,
tabsCount = 4,
tintDropletContent = false, // keep icon colors from tab content
dropletContentTint = Color.Black, // when tintDropletContent = true
modifier = Modifier.padding(horizontal = 20.dp)
) {
TabItem(index = 0, icon = homeIcon, label = "Home")
TabItem(index = 1, icon = searchIcon, label = "Search")
// …
}Highlight during drag — use LocalPrismalBottomTabHighlightedIndex so icon colors follow the droplet while the thumb is down (the persisted selectedTabIndex updates on release):
@Composable
private fun RowScope.TabItem(index: Int, icon: ImageVector, label: String) {
val highlighted = LocalPrismalBottomTabHighlightedIndex.current()
val selected = highlighted == index
PrismalGlassBottomTab(onClick = { /* update selectedTab */ }) {
Icon(icon, contentDescription = label, tint = if (selected) Color.Black else Color.Gray)
Text(label, color = if (selected) Color.Black else Color.Gray)
}
}Droplet tint options
| Setting | Behavior |
|---|---|
tintDropletContent = false |
Droplet shows actual tab icon/label colors |
tintDropletContent = true, dropletContentTint = null |
Default iOS blue (#0088FF / #0091FF) |
tintDropletContent = true, custom color |
Your color in both light and dark themes |
PrismalHorizontalSelector is an iOS-style horizontal picker (camera mode switcher): a fixed liquid-glass droplet stays centered while items scroll underneath and snap into place on release.
Widths are measured synchronously with TextMeasurer — no layout flicker.
import com.styropyr0.prismal.components.PrismalHorizontalSelector
import com.styropyr0.prismal.specular.PrismalSpecular
var selectedMode by remember { mutableIntStateOf(1) }
val modes = listOf("VIDEO", "PHOTO", "PORTRAIT", "SLO-MO")
PrismalHorizontalSelector(
labels = modes,
selectedIndex = selectedMode,
onSelected = { selectedMode = it },
backdrop = backdrop,
textStyle = MaterialTheme.typography.labelMedium,
textColor = MaterialTheme.colorScheme.onSurface,
luminance = { luminanceState.luminance },
specular = { PrismalSpecular.Default },
chromaticAberration = 0.2f, // 20% RGB dispersion on the droplet lens
modifier = Modifier.fillMaxWidth(),
)Pass any item content; widths are measured once before layout.
PrismalHorizontalSelector(
itemCount = items.size,
selectedIndex = selectedIndex,
onSelected = { selectedIndex = it },
backdrop = backdrop,
chromaticAberration = 0.2f,
) { index, focus ->
// focus is 1f when centered, 0f when far away — use for alpha/scale, not layout changes
Icon(items[index], contentDescription = null)
}| Parameter | Default | Description |
|---|---|---|
labels / itemCount |
— | String list overload or item count for composable overload |
selectedIndex |
— | Currently selected item |
onSelected |
— | Called when scrolling settles on a new item |
backdrop |
— | Backdrop sampled through the droplet glass |
luminance |
{ 0.5f } |
Normalized backdrop brightness in [0, 1] |
specular |
PrismalSpecular.Default |
Specular highlight on the droplet |
chromaticAberration |
0.2f |
Droplet lens RGB dispersion in [0, 1] (0.2 = 20%) |
boldWhenFocused |
true |
Text overload only — bold the centered label |
dropletEffects |
null |
Custom effect block; replaces the default lens stack when set |
itemSpacing |
6.dp |
Gap between items |
itemPadding |
14.dp |
Horizontal padding included in each item width |
dropletExtraWidth |
10.dp |
Extra width added around the focused item for the capsule |
dropletShadow |
false |
Depth shadow under the droplet; off by default since the selector clips it into a visible box |
| Feature | Description |
|---|---|
| Fixed droplet | Centered PrismalCapsule glass with bottom-tab-style lens refraction |
| Scrolling items | Horizontal scroll with spring snap on release |
| Focus animation | Centered item scales up (0.9→1.0) and fades in (38%→100%) |
| Bold center label | Text overload bolds the focused label (boldWhenFocused = false to disable) |
| Squish on drag | Droplet stretches slightly while scrolling, springs back on snap |
| Chromatic aberration | Configurable RGB dispersion on the droplet lens (API 33+) |
PrismalRulerSelector is a measurement-style horizontal picker: a fixed liquid-glass droplet stays centered while tick marks scroll underneath. The selected value is rendered inside the droplet. Ticks pass through the glass for refraction and leave a gap only under the label:
||||||| <value> |||||||
import com.styropyr0.prismal.components.PrismalRulerSelector
import com.styropyr0.prismal.specular.PrismalSpecular
var lengthMm by remember { mutableIntStateOf(25) }
PrismalRulerSelector(
value = lengthMm,
onValueChange = { lengthMm = it },
valueRange = 0..100,
step = 1,
majorTickEvery = 5,
valueLabel = { "${it}mm" },
backdrop = backdrop,
textStyle = MaterialTheme.typography.labelMedium,
textColor = MaterialTheme.colorScheme.onSurface,
tickColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.55f),
luminance = { luminanceState.luminance },
specular = { PrismalSpecular.Default },
chromaticAberration = 0.3f,
modifier = Modifier.fillMaxWidth(),
)| Parameter | Default | Description |
|---|---|---|
value |
— | Currently selected integer value |
onValueChange |
— | Called as the focused tick changes while scrolling, and again when the snap settles |
valueRange |
— | Inclusive range of selectable values |
step |
1 |
Increment between consecutive values |
majorTickEvery |
5 |
Draw a taller tick every N steps |
tickSpacing |
10.dp |
Horizontal distance between consecutive ticks |
valueLabel |
{ it.toString() } |
Formats the value shown inside the droplet |
textStyle / textColor |
— | Droplet label typography and color |
tickColor |
textColor at 55% alpha |
Color of minor and major ticks |
backdrop |
— | Backdrop sampled through the droplet glass |
luminance |
{ 0.5f } |
Normalized backdrop brightness in [0, 1] |
specular |
PrismalSpecular.Default |
Specular highlight on the droplet |
chromaticAberration |
0.3f |
Droplet lens RGB dispersion in [0, 1] |
dropletEffects |
null |
Custom effect block; replaces the default lens stack when set |
dropletExtraWidth |
50.dp |
Extra horizontal padding around the widest label |
dropletPadding |
5.dp |
Outer padding around the droplet capsule |
dropletShadow |
false |
Depth shadow under the droplet; off by default since the ruler clips it into a visible box |
| Feature | Description |
|---|---|
| Fixed droplet | Centered PrismalCapsule glass with the live value drawn inside |
| Scrolling ticks | Horizontal tick strip with spring snap on release |
| Stable droplet width | Sized once to the widest label in range — no resize jitter while sliding |
| Tabular figures | Uses tnum so digit changes do not shift the label |
| Tick gap under label | Ticks refract through the droplet edges but clear space under the value text |
| Major / minor ticks | Shorter ticks by default; taller ticks every majorTickEvery steps |
| Edge fade | Ticks fade toward the viewport edges for depth |
| Chromatic aberration | Configurable RGB dispersion on the droplet lens (API 33+) |
The :app module in this repository is a catalog playground (not published to JitPack). It demonstrates:
- iOS-style grouped settings layout
- Glass buttons, toggles, sliders, and progress bars
- Horizontal selector (camera-mode style picker)
- Ruler selector (tick / measurement picker)
- Gradient glass panel and profile avatar
- Bottom tab bar with customizable droplet tint
- Custom wallpaper picker with persistence across app restarts
- Live glass parameter tuning in Settings
Run locally:
./gradlew :app:installDebugPrismal/
├── components/ # Ready-made UI (buttons, tabs, slider, selectors, …)
├── depth/ # Depth shadow and inset modifiers
├── effects/ # Blur, lens, gradient glass, color controls
├── interactive/ # Press ripple, spring motion, drag gestures
├── shapes/ # PrismalRoundedRectangle, PrismalCapsule, …
├── sources/ # Backdrop layers, merged sources, canvas source
├── specular/ # Edge highlight styles
├── DrawPrismalGlassModifier.kt
├── PrismalBackdrop.kt
├── PrismalGlassSurface.kt
├── PrismalLiquidGlass.kt
└── Platform.kt # Pipeline tier detection
app/ # Demo catalog (not published)
This project is licensed under the MIT License — see LICENSE.
- GitHub (this repo): styropyr0/PrismalAGSL
- Original Prismal (OpenGL ES): styropyr0/Prismal
- JitPack: jitpack.io/#styropyr0/PrismalAGSL







