Haze 2.0 is here! Haze brings background blur and glass effects to Compose Multiplatform. There’s a lot to play with in this release: Glass brings refraction and lighting to your Compose Multiplatform interfaces, blur gets performance improvements, and a new API makes room for effects of your own. Let’s start with Glass.
Glass#
Glass combines refraction, blur, tint, and lighting to create surfaces that respond to the content behind them. The effect follows the shape of your surface, from a rounded button to a larger card or panel.
There are two built-in styles to start from:
GlassStyle.regulargives surfaces a more diffused appearance. Blur and depth adapt to the surface size, keeping detail in compact controls and softening the background more on larger cards and panels.GlassStyle.clearkeeps more of the background visible, with a shallow blur that stays consistent across surface sizes. Start here when you want the content behind a control to remain prominent.
Both can be customised with then, or you can define your own GlassOptics configuration for finer control.
Glass can also respond to hover, focus, and press interactions with changes in lighting, optics, and motion. Apps can provide accessibility settings for reduced transparency, increased contrast, and visible borders through LocalGlassAccessibilitySettings.
Glass remains experimental in 2.0. I’m aiming to stabilise its API in Haze 2.1. The Glass guide covers the available styles and how to use them.
Try Glass yourself in the interactive Playground below. Use the style selector to compare Regular and Clear.
Open the sample in a new tab for more room.
A new API and foundation#
The new hazeBlur and hazeGlass modifiers make the effect explicit, with reusable styles keeping appearance separate from input and performance choices.
then. Haze provides its own effect-specific style types.For example, start with a Glass preset and give it a shape and tint:
// Requires opting in to ExperimentalHazeApi.
Modifier.hazeGlass(
input = HazeInput.Sources(hazeState),
style = GlassStyle.regular.then {
shape(RoundedCornerShape(20.dp))
tint(Color.White.copy(alpha = 0.16f))
},
)Here, hazeState connects the effect to content registered with hazeSource. You can reuse the same style across several surfaces.

Compose has progressive blur. Do you still need Haze?
Build your own effects#
Blur and Glass are built on a shared effects API that you can use too. Haze handles capturing content, keeping it positioned as the UI moves, and managing the renderer’s lifecycle. Your effect supplies the drawing logic, whether that’s a colour treatment, a distortion, or another visual effect.
Define a style for your effect’s configuration and a HazeEffectFactory that creates its HazeEffectRenderer, then apply it with Modifier.hazeEffect. Each modifier gets its own renderer, so you can share styles and factories while keeping shaders and mutable caches local to each instance.
Custom effects can work with content captured by hazeSource, or with the composable’s own content through HazeInput.Content. The custom effects guide walks through a complete example, from drawing the input to releasing renderer resources.
Performance#
Blur and Glass share HazePerformanceMode, which controls the balance between rendering quality and cost. The default, Adaptive, adjusts the resolution used to process captured content based on the effect’s workload. Blur considers its radius, capture area, and how often the input changes. Glass considers the pixels in its rendering layers and how frequently the content, geometry, or interactions change. Larger, frequently updating effects can use a lower resolution to reduce the work per update, with a margin between switching thresholds to avoid bouncing between levels.
You can also choose Quality, Balanced, or Performance, or use Fixed(qualityFraction) for a level in between. Set it on an individual effect, or use LocalHazePerformanceMode to provide a default across part of your UI.
Underneath, Haze now avoids re-recording unchanged sources when unrelated content redraws and skips source capture when no effects need it.
In the earlier comparison of two sample interactions, Haze 2 reduced CPU frame time:
| Sample interaction | Haze 1 | Haze 2 | Reduction |
|---|---|---|---|
| Images List scrolling | 14.9 ms | 9.3 ms | 37% |
| Credit Card dragging | 11.5 ms | 9.8 ms | 15% |
These are P90 CPU frame durations—representing the slower end of the measured frames—from 32 iterations per workload on a Pixel 6 running Android 17 at 60 Hz. The comparison details have the full context.
For Glass, the Pixel 8a measurements show how the performance modes compare in the changing-content sample. At 60 Hz, a frame budget is roughly 16.7 ms:
| Performance mode | Average P90 deadline margin (higher is better) | Margin as a share of the frame budget |
|---|---|---|
| Adaptive (default) | 8.9 ms | 53% |
| Quality | 7.0 ms | 42% |
| Balanced | 8.1 ms | 48% |
| Performance | 9.0 ms | 54% |
The adaptive default left just over half a frame budget to spare, even towards the slower end of the measured frames. These sample results average the P90 margins from two passes of eight iterations per mode on Android 17 QPR2, with fixed-performance mode enabled. The benchmark results include the full timings, memory use, and test conditions.
Android native backdrops#
Haze 2.0 also supports Android 17 QPR2’s native backdrop API. HazeInput.Backdrop lets an effect read pixels already drawn behind it in the same window, with a portable source fallback on other configurations.
The native rendering path is experimental and opt-in, enabled through HazeFeatureFlags.isPlatformBackdropEnabled. See the backdrop documentation for eligibility and fallback behaviour.
// Enable once at app startup, before Haze effect nodes attach.
HazeFeatureFlags.isPlatformBackdropEnabled = true
// Use your existing HazeState as the portable source fallback.
Modifier.hazeBlur(
input = HazeInput.Backdrop(hazeState),
)Keep your hazeSource(state = hazeState) registration for the fallback. Haze uses the native path when eligible and falls back to those sources otherwise.
The two rendering paths have different tradeoffs: in the Pixel 8a samples, native Backdrop used less peak GPU memory, while source-backed Glass had lower CPU frame cost. There are more details in the backdrop comparison.
Explore Haze 2.0#
Head to the documentation to explore the effects and get started. Upgrading from Haze 1 involves dependency and API changes; the migration guide walks through them. The release notes have the full list of changes.
