Skip to main content
Haze 2.0: A Pluggable Visual Effects Engine

Haze 2.0: A Pluggable Visual Effects Engine

·584 words·3 mins

Haze is a library for backdrop-style visual effects in Jetpack Compose and Compose Multiplatform. Up until now, blur has been the headline feature and the architecture has been built around that assumption.

In 2.0.0-alpha01, that changes. Blur is now just one effect implementation, built on top of a more general rendering pipeline. That makes the core smaller, the APIs more flexible, and opens the door to custom effects beyond blur.

The Problem
#

Haze started as a background blur library for Compose Multiplatform, and it did that job well. But blur is just one visual effect, and the monolithic design of v1 made it hard to add new effects, compose multiple effects together, or let anyone build their own. Adding something like a gradient effect shouldn’t require understanding the blur internals.

In reality, blurring turned out to be a small piwce of the overall problem of ‘background blurring’: the majority of the code in Haze has always been concerned with drawing background content.

Introducing VisualEffect
#

Haze 2.0 introduces a new VisualEffect interface. The core library no longer knows about blur specifically. Instead, it provides the effect-agnostic infrastructure: coordinate calculation, layer management, scroll-aware positioning, noise texturing, and progressive rendering. Effects like blur implement the VisualEffect interface and plug into that infrastructure.

This means:

  • Blur is now one implementation. Extracted to a separate haze-blur module.
  • Custom effects are possible. Want gradient overlays, color shifting, or custom shaders? Implement VisualEffect and plug it in.
  • Smaller core module. If you don’t need blur, you don’t pay for it.

That architectural split is the main goal of 2.0. Blur is still there, but it no longer defines the entire library.

Migration at a Glance
#

If you’re using blur you now need the blur module:

implementation("dev.chrisbanes.haze:haze-blur:2.0.0-alpha01")

Blur properties now live inside a blurEffect {} block:

// v1
Modifier.hazeEffect(state = hazeState) {
    blurRadius = 20.dp
    tint = HazeTint(Color.White.copy(alpha = 0.3f))
}

// v2
Modifier.hazeEffect(state = hazeState) {
    blurEffect {
        blurRadius = 20.dp
        colorEffects = listOf(HazeColorEffect.tint(Color.White.copy(alpha = 0.3f)))
    }
}

Key renames:

v1v2
HazeStyleHazeBlurStyle
HazeTintHazeColorEffect
HazeProgressiveHazeProgressive (unchanged, but moved to haze-blur package)
rememberHazeState(blurEnabled)blurEffect { blurEnabled = ... }

Full migration guide: chrisbanes.github.io/haze/latest/migrating-2.0/

What’s New Beyond the Refactor
#

Alongside the architecture work, 2.0.0-alpha01 also ships with a few other notable changes:

  • Configurable HazePositionStrategy: fixes blur misalignment in split-window scenarios on Android, where window-relative positioning could previously be wrong
  • Migration helpers: blurRadius convenience extension on HazeEffectScope for the common single-effect blur case
  • Materials API promoted to stable: @ExperimentalHazeMaterialsApi annotation removed
  • Performance improvements: per-frame allocation eliminated, effect updates gated, snapshot edge fixes

Getting Started
#

The important thing to note here is that the core module now provides the infrastructure. If you want the familiar blur effect, you need haze-blur.

val hazeState = rememberHazeState()

Box(
    modifier = Modifier
        .fillMaxSize()
        .hazeSource(state = hazeState)
) {
    LazyColumn { /* your content */ }
}

Box(
    modifier = Modifier
        .fillMaxSize()
        .hazeEffect(state = hazeState) {
            blurEffect {
                blurRadius = 20.dp
            }
        }
)

What’s Next
#

This is an alpha. The VisualEffect interface and the blurEffect {} API block are the shape I’m aiming for, but I want community feedback before locking them in as stable.

If you try it out, I’d especially love feedback on:

  • Whether VisualEffect feels flexible enough for custom effects
  • Whether blurEffect {} feels ergonomic in day-to-day use
  • Which built-in effects you’d want to see beyond blur?

If you hit issues or have feedback, please file them on GitHub.