Metal-powered frosted glass effect with real-time background blur for SwiftUI and UIKit
A Swift library that creates liquid glass visual effects using custom Metal shaders. Automatically captures and blurs the view hierarchy behind UI elements without manual screenshot management.
LiquidGlass is an iOS library that creates a translucent frosted glass effect with blur and refraction. Unlike standard UIVisualEffectView, this implementation uses custom Metal shaders and view hierarchy capturing to achieve more advanced visual effects with fine-grained control over update frequency and appearance.
| 🔍 Automatic capture | Background is captured automatically – just drop liquidGlassBackground on any view. |
| ⚡ Real‑time rendering | Optimised MTLTexture snapshots + lazy redraw; redraws only when the background actually changes. |
| 🛠 Flexible update modes | .continuous, .once, .manual via the updateMode parameter. |
| 🧩 SwiftUI + UIKit | Works seamlessly in both frameworks with native APIs and shared Metal backend. |
| 💤 Battery‑friendly | MTKView stays paused until the provider notifies it – no wasted frames. |
| 🎨 Customizable shader | Modify Metal shader code to adjust blur, refraction, and visual effects. |
Add LiquidGlass through Swift Package Manager:
https://github.com/BarredEwe/LiquidGlass.git Or via Xcode → File → Add Package Dependencies…
Select LiquidGlass and you're done.
import SwiftUI import LiquidGlass Button("Glass Text") { } .liquidGlassBackground(cornerRadius: 60) import UIKit import LiquidGlass // Using extension (recommended) button.addLiquidGlassBackground(cornerRadius: 25) // Or using LiquidGlassUIView directly let glassView = LiquidGlassUIView(cornerRadius: 30, blurScale: 0.8) containerView.addSubview(glassView) LiquidGlass uses a four-stage pipeline to achieve the glass effect:
- Hierarchy Capture —
HierarchySnapshotCapturerrenders the entire view hierarchy above the glass view into aCGImage - Texture Creation —
BackgroundTextureProviderconverts the image toMTLTextureand applies GPU-based blur - Metal Rendering —
MetalShaderView.Coordinatorrenders the effect throughMTKViewwith custom fragment shader - Update Management — Depending on
updateMode, the background updates automatically or on-demand
import UIKit import LiquidGlass class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let button = UIButton(type: .system) button.setTitle("Glass Button", for: .normal) button.setTitleColor(.white, for: .normal) // Add liquid glass background button.addLiquidGlassBackground( cornerRadius: 25, updateMode: .continuous(interval: 0.1), blurScale: 0.7, tintColor: .white.withAlphaComponent(0.1) ) view.addSubview(button) // ... setup constraints } } | Mode | What it does | Best for |
|---|---|---|
.continuous(interval:) | Captures every n seconds. | Animating backgrounds, parallax, fancy UIs. |
.once | Captures exactly one frame. | Static dialogs, settings sheets. |
.manual | Capture only when you call invalidate() | Power‑saving, custom triggers. |
Button("Glass Button") { } .liquidGlassBackground( cornerRadius: 20, updateMode: .continuous(interval: 0.1), blurScale: 0.5 ) // Using extension button.addLiquidGlassBackground(updateMode: .manual) // Manual invalidation button.liquidGlassBackground?.invalidateBackground() // Using LiquidGlassUIView directly let glassView = LiquidGlassUIView(updateMode: .once) glassView.invalidateBackground() // for manual updates .liquidGlassBackground( cornerRadius: CGFloat = 20, // Corner radius updateMode: SnapshotUpdateMode = .continuous(), // Update frequency blurScale: CGFloat = 0.3, // Blur intensity (0.0-1.0) tintColor: UIColor = .white.withAlphaComponent(0.1) // Tint color overlay ) // Initialization let glassView = LiquidGlassUIView( cornerRadius: 20, updateMode: .continuous(), blurScale: 0.5, tintColor: .gray.withAlphaComponent(0.2) ) // Properties (all animatable with UIView.animate) glassView.cornerRadius = 25 glassView.blurScale = 0.8 glassView.tintColor = .blue.withAlphaComponent(0.1) glassView.updateMode = .manual // Methods glassView.invalidateBackground() // Force update // Add glass background (fills entire view) view.addLiquidGlassBackground(cornerRadius: 20) // Add glass background with custom frame view.addLiquidGlassBackground( frame: CGRect(x: 0, y: 0, width: 200, height: 50), cornerRadius: 25 ) // Access glass backgrounds let glassView = view.liquidGlassBackground // First glass view let allGlassViews = view.liquidGlassBackgrounds // All glass views // Remove glass backgrounds view.removeLiquidGlassBackgrounds() Modify Sources/LiquidGlass/Shaders/LiquidGlassShader.metal to customize the visual effect:
sampleBackground()— Distort UV coordinates, add wave/ripple effectspostProcess()— Adjust saturation, add vignette, chromatic aberration, bloom
// In LiquidGlassShader.metal float3 sampleBackground(float2 uv, texture2d<float> bgTexture, sampler bgSampler) { // Add wave distortion float wave = sin(uv.y * 10.0 + uniforms.time) * 0.01; uv.x += wave; return bgTexture.sample(bgSampler, uv).rgb; } Optimizations:
- Snapshot covers only the area behind the glass – minimal memory footprint
- Layers above the glass are never hidden → no flicker
- Lazy redraw means nearly zero GPU usage when nothing changes
- Capture happens at reduced scale (0.8× screen scale) for memory savings
- UIKit and SwiftUI versions share the same optimized Metal backend
Recommendations:
- Use
.oncefor static UI (dialogs, modals) - Use
.continuous(interval: 0.05)(≈20 FPS) for animated backgrounds - Avoid many simultaneous glass views
- Test on real devices, not just simulator
- iOS 14.0+
- Swift 5.9+
- Xcode 15.0+
- Metal-capable device
- View hierarchy only — Cannot capture other windows
- Metal required — Won't work on very old devices without GPU support
- Performance — High update frequencies may impact older devices (A9 and below)
- SwiftUI layout — Background captures the underlying view hierarchy, not SwiftUI's logical structure
The glass doesn't update when I scroll.
Use.continuous(interval: 0.016)(≈60 fps) or trigger.manual'sinvalidate()inscrollViewDidScroll.
Can I animate the glass properties?
Yes! In UIKit, all properties (cornerRadius,blurScale,tintColor) are animatable withUIView.animate().
How do I use this in a table view cell?
Use.onceor.manualupdate mode for better performance, and callinvalidateBackground()when the cell is reused.
Can I mix SwiftUI and UIKit glass views?
Absolutely! They use the same Metal backend and work seamlessly together.
Why does this look different from Apple's Liquid Glass?
This is an independent implementation of a similar effect. Apple's official Liquid Glass is available only in iOS 26+ and uses private APIs.
What's the performance impact?
Minimal when using.onceor.manual. With.continuous, expect ~5-10% GPU usage depending on update frequency and device.
MIT © 2025 • BarredEwe / Prefire
- Inspired by Apple's Liquid Glass design language
- Metal shaders based on GPU Gems blur techniques
Made with ❤️ & Metal

