
Real-time backdrop blur overlay capturing and blurring live content behind UI, offering uniform or variable per-pixel blur, gradients, blend modes, tinting, and GPU-accelerated performance.
Real-time native backdrop blur for Compose Multiplatform (Android + iOS).
Blurs whatever is behind it in the view hierarchy — like iOS UIVisualEffectView or CSS backdrop-filter, but cross-platform with a single Compose API.
| Android | iOS | |
|---|---|---|
| Blur engine | OpenGL ES 2.0 Dual Kawase | CABackdropLayer (GPU compositor) |
| Min version | API 23 | iOS 15 |
| Variable blur | OpenGL pyramid compositing | CAFilter variableBlur + mask |
| Performance | ~39 texture samples/pixel | Zero-cost compositor capture |
Add the dependency to your KMP module:
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.ezoushen:blur-cmp:<version>")
}
}
}The blur engine is bundled — no additional dependencies needed.
Blurs whatever is behind it. Place it on top of any content:
@Composable
fun MyScreen() {
val blurState = rememberBlurOverlayState(
initialConfig = BlurOverlayConfig(radius = 20f)
)
Box(Modifier.fillMaxSize()) {
// Your scene — this gets blurred
MyContent()
// Blur overlay — blurs everything behind it
BlurOverlay(state = blurState) {
// Sharp controls on top
Text("Hello", color = Color.White)
}
}
}If you want the blur composable to manage both background and foreground:
@Composable
fun MyScreen() {
val blurState = rememberBlurOverlayState()
BlurOverlayHost(
state = blurState,
background = { PhotoGallery() },
content = { OverlayControls() },
)
}BlurOverlayConfig(
radius = 20f, // blur radius in logical pixels (0 = no blur)
tintBlendMode = BlurBlendMode.Normal, // blend mode for tint
tintOrder = TintOrder.POST_BLUR, // POST_BLUR (default) or PRE_BLUR
downsampleFactor = 4f, // Android only: higher = faster, lower quality
gradient = null, // null = uniform blur, or BlurGradientType
isLive = true, // true = updates every frame
)BlurOverlayConfig.Default // radius 16, no tint
BlurOverlayConfig.Light // radius 10, white tint 25%
BlurOverlayConfig.Dark // radius 20, black tint 40%
BlurOverlayConfig.Heavy // radius 50, white tint 50%Use the withTint extension to set a Compose Color:
val config = BlurOverlayConfig(radius = 20f)
.withTint(Color.White.copy(alpha = 0.2f))Read it back:
val tintColor: Color? = config.tintColorVariable blur lets the blur intensity vary across the surface using a gradient.
// Top-to-bottom: full blur at top, clear at bottom
BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.Linear(
startX = 0.5f, startY = 0f, // top center
endX = 0.5f, endY = 1f, // bottom center
startIntensity = 1f, // full blur
endIntensity = 0f, // no blur
),
)
// Convenience factory
BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.verticalTopToBottom(),
)// Sharp center, blurred edges
BlurOverlayConfig(
radius = 25f,
gradient = BlurGradientType.Radial(
centerX = 0.5f, centerY = 0.4f,
radius = 0.4f,
centerIntensity = 0f, // sharp
edgeIntensity = 1f, // blurred
),
)
// Convenience factory
BlurOverlayConfig(
radius = 25f,
gradient = BlurGradientType.spotlight(centerX = 0.5f, centerY = 0.4f, radius = 0.4f),
)BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.Linear(
startX = 0.5f, startY = 0f,
endX = 0.5f, endY = 1f,
stops = listOf(
BlurGradientType.Stop(0.0f, 1.0f), // full blur at top
BlurGradientType.Stop(0.3f, 0.0f), // clear zone
BlurGradientType.Stop(0.7f, 0.0f), // clear zone
BlurGradientType.Stop(1.0f, 1.0f), // full blur at bottom
),
),
)12 blend modes for tint compositing:
BlurOverlayConfig(
radius = 15f,
tintBlendMode = BlurBlendMode.ColorDodge,
).withTint(Color.White.copy(alpha = 0.2f))Available modes: Normal, ColorDodge, ColorBurn, Multiply, Screen, Overlay, SoftLight, HardLight, Darken, Lighten, Difference, Exclusion
Color Dodge with tint creates a brightening bloom effect.
By default, tint is applied after blur (TintOrder.POST_BLUR), matching Apple's UIVisualEffectView and CSS backdrop-filter. This produces a sharp, uniform tint over the blurred result.
For a softer look where the tint gets diffused by the blur, use TintOrder.PRE_BLUR:
BlurOverlayConfig(
radius = 15f,
tintBlendMode = BlurBlendMode.ColorDodge,
tintOrder = TintOrder.PRE_BLUR, // tint blended into content before blur
).withTint(Color.White.copy(alpha = 0.2f))val blurState = rememberBlurOverlayState()
// Update config dynamically
blurState.config = BlurOverlayConfig(radius = newRadius)
// Toggle blur on/off
blurState.isEnabled = false
// Convenience setters
blurState.setRadius(25f)
blurState.setTintColor(Color.Blue.copy(alpha = 0.1f))
blurState.setGradient(BlurGradientType.spotlight())
// Force update when isLive = false
blurState.requestUpdate()Uses BlurView / VariableBlurView hosted via AndroidView:
lockHardwareCanvas (API 26+), or software canvas fallbackglReadPixels → canvas.drawBitmap, or TextureView for TBDR GPUsRenderNodeBlurController uses RenderEffect for uniform blur (zero-copy GPU path)Uses CABackdropLayer extracted from UIVisualEffectView:
CABackdropLayer captures live window content at the GPU compositor level (zero-copy)CAFilter (gaussianBlur or variableBlur) applies blur nativelyrootViewController.view above CMP's MetalViewUIWindow via ComposeUIViewController(opaque = false)
Apache License 2.0
Real-time native backdrop blur for Compose Multiplatform (Android + iOS).
Blurs whatever is behind it in the view hierarchy — like iOS UIVisualEffectView or CSS backdrop-filter, but cross-platform with a single Compose API.
| Android | iOS | |
|---|---|---|
| Blur engine | OpenGL ES 2.0 Dual Kawase | CABackdropLayer (GPU compositor) |
| Min version | API 23 | iOS 15 |
| Variable blur | OpenGL pyramid compositing | CAFilter variableBlur + mask |
| Performance | ~39 texture samples/pixel | Zero-cost compositor capture |
Add the dependency to your KMP module:
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.ezoushen:blur-cmp:<version>")
}
}
}The blur engine is bundled — no additional dependencies needed.
Blurs whatever is behind it. Place it on top of any content:
@Composable
fun MyScreen() {
val blurState = rememberBlurOverlayState(
initialConfig = BlurOverlayConfig(radius = 20f)
)
Box(Modifier.fillMaxSize()) {
// Your scene — this gets blurred
MyContent()
// Blur overlay — blurs everything behind it
BlurOverlay(state = blurState) {
// Sharp controls on top
Text("Hello", color = Color.White)
}
}
}If you want the blur composable to manage both background and foreground:
@Composable
fun MyScreen() {
val blurState = rememberBlurOverlayState()
BlurOverlayHost(
state = blurState,
background = { PhotoGallery() },
content = { OverlayControls() },
)
}BlurOverlayConfig(
radius = 20f, // blur radius in logical pixels (0 = no blur)
tintBlendMode = BlurBlendMode.Normal, // blend mode for tint
tintOrder = TintOrder.POST_BLUR, // POST_BLUR (default) or PRE_BLUR
downsampleFactor = 4f, // Android only: higher = faster, lower quality
gradient = null, // null = uniform blur, or BlurGradientType
isLive = true, // true = updates every frame
)BlurOverlayConfig.Default // radius 16, no tint
BlurOverlayConfig.Light // radius 10, white tint 25%
BlurOverlayConfig.Dark // radius 20, black tint 40%
BlurOverlayConfig.Heavy // radius 50, white tint 50%Use the withTint extension to set a Compose Color:
val config = BlurOverlayConfig(radius = 20f)
.withTint(Color.White.copy(alpha = 0.2f))Read it back:
val tintColor: Color? = config.tintColorVariable blur lets the blur intensity vary across the surface using a gradient.
// Top-to-bottom: full blur at top, clear at bottom
BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.Linear(
startX = 0.5f, startY = 0f, // top center
endX = 0.5f, endY = 1f, // bottom center
startIntensity = 1f, // full blur
endIntensity = 0f, // no blur
),
)
// Convenience factory
BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.verticalTopToBottom(),
)// Sharp center, blurred edges
BlurOverlayConfig(
radius = 25f,
gradient = BlurGradientType.Radial(
centerX = 0.5f, centerY = 0.4f,
radius = 0.4f,
centerIntensity = 0f, // sharp
edgeIntensity = 1f, // blurred
),
)
// Convenience factory
BlurOverlayConfig(
radius = 25f,
gradient = BlurGradientType.spotlight(centerX = 0.5f, centerY = 0.4f, radius = 0.4f),
)BlurOverlayConfig(
radius = 30f,
gradient = BlurGradientType.Linear(
startX = 0.5f, startY = 0f,
endX = 0.5f, endY = 1f,
stops = listOf(
BlurGradientType.Stop(0.0f, 1.0f), // full blur at top
BlurGradientType.Stop(0.3f, 0.0f), // clear zone
BlurGradientType.Stop(0.7f, 0.0f), // clear zone
BlurGradientType.Stop(1.0f, 1.0f), // full blur at bottom
),
),
)12 blend modes for tint compositing:
BlurOverlayConfig(
radius = 15f,
tintBlendMode = BlurBlendMode.ColorDodge,
).withTint(Color.White.copy(alpha = 0.2f))Available modes: Normal, ColorDodge, ColorBurn, Multiply, Screen, Overlay, SoftLight, HardLight, Darken, Lighten, Difference, Exclusion
Color Dodge with tint creates a brightening bloom effect.
By default, tint is applied after blur (TintOrder.POST_BLUR), matching Apple's UIVisualEffectView and CSS backdrop-filter. This produces a sharp, uniform tint over the blurred result.
For a softer look where the tint gets diffused by the blur, use TintOrder.PRE_BLUR:
BlurOverlayConfig(
radius = 15f,
tintBlendMode = BlurBlendMode.ColorDodge,
tintOrder = TintOrder.PRE_BLUR, // tint blended into content before blur
).withTint(Color.White.copy(alpha = 0.2f))val blurState = rememberBlurOverlayState()
// Update config dynamically
blurState.config = BlurOverlayConfig(radius = newRadius)
// Toggle blur on/off
blurState.isEnabled = false
// Convenience setters
blurState.setRadius(25f)
blurState.setTintColor(Color.Blue.copy(alpha = 0.1f))
blurState.setGradient(BlurGradientType.spotlight())
// Force update when isLive = false
blurState.requestUpdate()Uses BlurView / VariableBlurView hosted via AndroidView:
lockHardwareCanvas (API 26+), or software canvas fallbackglReadPixels → canvas.drawBitmap, or TextureView for TBDR GPUsRenderNodeBlurController uses RenderEffect for uniform blur (zero-copy GPU path)Uses CABackdropLayer extracted from UIVisualEffectView:
CABackdropLayer captures live window content at the GPU compositor level (zero-copy)CAFilter (gaussianBlur or variableBlur) applies blur nativelyrootViewController.view above CMP's MetalViewUIWindow via ComposeUIViewController(opaque = false)
Apache License 2.0