<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wrack</title>
    <description>The latest articles on DEV Community by Wrack (@wrack).</description>
    <link>https://dev.to/wrack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042542%2F2bfa0d54-06e2-422e-91c2-2d40e3921ce4.png</url>
      <title>DEV Community: Wrack</title>
      <link>https://dev.to/wrack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wrack"/>
    <language>en</language>
    <item>
      <title>Liquid Glass on Android in React Native — I refracted the backdrop with a real-time AGSL shader (the keyword-optimized title from your posting kit)</title>
      <dc:creator>Wrack</dc:creator>
      <pubDate>Mon, 27 Jul 2026 10:16:36 +0000</pubDate>
      <link>https://dev.to/wrack/liquid-glass-on-android-in-react-native-i-refracted-the-backdrop-with-a-real-time-agsl-shader-1dp4</link>
      <guid>https://dev.to/wrack/liquid-glass-on-android-in-react-native-i-refracted-the-backdrop-with-a-real-time-agsl-shader-1dp4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — React Native has no built-in Liquid Glass on Android. The library &lt;a href="https://github.com/himanshu-lal4/react-native-liquid-glassmorphism" rel="noopener noreferrer"&gt;&lt;code&gt;react-native-liquid-glassmorphism&lt;/code&gt;&lt;/a&gt; reproduces it: every frame it captures the backdrop, blurs it with &lt;code&gt;RenderEffect&lt;/code&gt;, then runs a hand-written &lt;strong&gt;AGSL refraction shader&lt;/strong&gt; (&lt;code&gt;RuntimeShader&lt;/code&gt;, Android 13 / API 33+) that bends the backdrop through a modelled glass slab — edge refraction, chromatic dispersion, a Fresnel rim, and a device-tracking specular. On iOS 26 the same &lt;code&gt;&amp;lt;LiquidGlassView&amp;gt;&lt;/code&gt; uses Apple's native &lt;code&gt;UIGlassEffect&lt;/code&gt;. This post walks the Android pipeline end to end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhimanshu-lal4%2Freact-native-liquid-glassmorphism%2Freleases%2Fdownload%2Fv0.1.0%2Freel-android.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhimanshu-lal4%2Freact-native-liquid-glassmorphism%2Freleases%2Fdownload%2Fv0.1.0%2Freel-android.gif" alt="react-native-liquid-glassmorphism rendering glass on Android in real time: the AGSL shader refracts and magnifies the backdrop at the edges of a translucent card while content scrolls behind it" width="300" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android gives you only two primitives for this — &lt;code&gt;RenderEffect.createBlurEffect&lt;/code&gt; and &lt;code&gt;RuntimeShader&lt;/code&gt; (AGSL). Everything Apple's material does for free, you model yourself.&lt;/li&gt;
&lt;li&gt;What makes it read as &lt;em&gt;glass&lt;/em&gt; and not &lt;em&gt;blur&lt;/em&gt;: refracting the backdrop through a surface normal, chromatic dispersion, a mirrored edge echo, and a Fresnel + specular rim.&lt;/li&gt;
&lt;li&gt;The performance work is in &lt;strong&gt;backdrop capture + dirty-tracking&lt;/strong&gt;, not the shader.&lt;/li&gt;
&lt;li&gt;The full lens needs Android 13 / API 33+ (and the iOS 26 SDK on iOS); it degrades gracefully below both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When Apple shipped Liquid Glass with iOS 26, it landed as a &lt;em&gt;system material&lt;/em&gt;. You add &lt;code&gt;UIGlassEffect&lt;/code&gt; to a view and the OS does the rest: it captures what's behind you, blurs it, warps it through a modelled slab of glass, and paints a specular highlight that tracks the device. Real edge refraction, chromatic dispersion at the rim, an inverted echo of the content just outside the frame. Not a blur — a lens.&lt;/p&gt;

&lt;p&gt;Android has none of this. There is no glass material, no &lt;code&gt;View&lt;/code&gt;, no Compose modifier. What Android gives you, from API 33 (Android 13), is two lower-level primitives: &lt;code&gt;RenderEffect.createBlurEffect&lt;/code&gt; (a Gaussian blur you can attach to a render node) and &lt;code&gt;RuntimeShader&lt;/code&gt; (a way to run &lt;strong&gt;AGSL&lt;/strong&gt; — Android Graphics Shading Language, Skia's dialect of SkSL — per pixel as a render effect). That's it. Everything Apple's material does for free, you have to model yourself.&lt;/p&gt;

&lt;p&gt;This is the story of doing exactly that inside a React Native view — &lt;code&gt;react-native-liquid-glassmorphism&lt;/code&gt;. The whole Android renderer is one Kotlin &lt;code&gt;ReactViewGroup&lt;/code&gt; subclass plus one AGSL string. I'll walk the pipeline back-to-front, the same order iOS composites in, and explain &lt;em&gt;why&lt;/em&gt; each stage exists — because most of the interesting decisions are about what goes wrong when you skip a step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline, in one breath
&lt;/h2&gt;

&lt;p&gt;Every frame the backdrop changes, the view does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capture&lt;/strong&gt; the hierarchy behind it into a downscaled bitmap (software canvas).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skip repaint&lt;/strong&gt; if the backdrop didn't actually change (dirty hash).&lt;/li&gt;
&lt;li&gt;Draw that bitmap into a hardware &lt;code&gt;RenderNode&lt;/code&gt; and attach a &lt;strong&gt;blur&lt;/strong&gt; &lt;code&gt;RenderEffect&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chain&lt;/strong&gt; an AGSL &lt;code&gt;RuntimeShader&lt;/code&gt; onto the blur — that's where the optics live.&lt;/li&gt;
&lt;li&gt;Let the React children draw crisply on top as normal &lt;code&gt;ViewGroup&lt;/code&gt; children.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stages 3–4 are the whole trick, but stages 1–2 are where the performance and the bugs are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing the backdrop without recording yourself
&lt;/h2&gt;

&lt;p&gt;The shader needs pixels to bend. Those pixels are &lt;em&gt;whatever is behind the glass&lt;/em&gt; — sibling views, the screen underneath. So on every &lt;code&gt;onPreDraw&lt;/code&gt;, the view rasterises the view tree behind it into a &lt;code&gt;Bitmap&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onPreDraw&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isCapturing&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="nf"&gt;captureBackdrop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The obvious approach — record the live view tree into a &lt;code&gt;RenderNode&lt;/code&gt; and read it back — deadlocks. The recorded tree keeps references back to &lt;em&gt;your own&lt;/em&gt; node, and HWUI stack-overflows walking it. So instead the capture goes through a plain software &lt;code&gt;Canvas&lt;/code&gt; backed by a bitmap, which rasterises immediately with no live references:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;scale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1f&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;captureScale&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(-(&lt;/span&gt;&lt;span class="n"&gt;locThis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;locRoot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toFloat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;-(&lt;/span&gt;&lt;span class="n"&gt;locThis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;locRoot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toFloat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c1"&gt;// draw() short-circuits while capturing so we never record ourselves.&lt;/span&gt;
&lt;span class="n"&gt;isCapturing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;isCapturing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details matter here. First, the translate offsets the root by the glass view's window position, so the bitmap holds exactly the region behind &lt;em&gt;this&lt;/em&gt; view. Second — the important one — the view guards its own &lt;code&gt;draw()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isCapturing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without that guard, &lt;code&gt;root.draw(canvas)&lt;/code&gt; would recurse into the glass view itself, which would try to capture, which would draw the root again — infinite feedback, a hall of mirrors. The &lt;code&gt;isCapturing&lt;/code&gt; flag makes the glass invisible to its own capture pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2× downscale, and why it's not free detail
&lt;/h2&gt;

&lt;p&gt;The capture isn't full resolution. It's downscaled by a constant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;captureScale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Half-resolution capture is a deliberate trade. It's cheaper to rasterise, cheaper to upload to the GPU, and — conveniently — it pre-softens the image before the blur even runs, so you can get away with a lighter blur radius and keep more structure for the lens to bend. The cost is honest: fine text in the backdrop loses crispness before it's refracted. For a &lt;em&gt;backdrop&lt;/em&gt; being blurred and warped anyway, that's invisible; if you ever wanted a near-lossless lens you'd pay for &lt;code&gt;captureScale = 1&lt;/code&gt;. (Worth flagging: the code comment next to this constant discusses values &lt;code&gt;3&lt;/code&gt; and &lt;code&gt;6&lt;/code&gt;, leftovers from earlier tuning — the shipped value is &lt;code&gt;2&lt;/code&gt;, which the docs and the render path both use.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Dirty tracking: the difference between free and 60fps-forever
&lt;/h2&gt;

&lt;p&gt;Here's the trap with &lt;code&gt;onPreDraw&lt;/code&gt; capture: if you &lt;code&gt;invalidate()&lt;/code&gt; yourself every time you capture, you've built a perpetual-motion machine. Capturing schedules a draw, the draw triggers &lt;code&gt;onPreDraw&lt;/code&gt;, which captures, which schedules a draw... 60fps forever, on a completely static screen, burning battery for nothing.&lt;/p&gt;

&lt;p&gt;The fix is to only repaint when the backdrop &lt;em&gt;actually changed&lt;/em&gt;. Comparing every pixel each frame would cost as much as the redraw. Instead the view hashes a sparse 6×5 grid of sampled pixels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;backdropHash&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captured&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;until&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;until&lt;/span&gt; &lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in &lt;code&gt;onPreDraw&lt;/code&gt;, repaint only on a hash change (or the first good frame):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;backdropHash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;haveGoodCapture&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;lastBackdropHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;lastBackdropHash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;
  &lt;span class="n"&gt;haveGoodCapture&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="nf"&gt;postInvalidateOnAnimation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a static screen the hash settles after one frame and the loop stops dead. When something behind the glass scrolls or animates, &lt;em&gt;that&lt;/em&gt; invalidation fires &lt;code&gt;onPreDraw&lt;/code&gt;, the hash differs, and exactly one fresh frame is drawn. The &lt;code&gt;postInvalidateOnAnimation()&lt;/code&gt; (rather than a plain &lt;code&gt;invalidate()&lt;/code&gt; from inside the draw pass) matters too: an &lt;code&gt;invalidate()&lt;/code&gt; issued mid-&lt;code&gt;onPreDraw&lt;/code&gt; gets unreliably coalesced by HWUI, and a dropped repaint is exactly what would let a black first-capture stick on screen as a ghost overlay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blur → shader: the RenderEffect chain
&lt;/h2&gt;

&lt;p&gt;Now the GPU stage. The captured bitmap is recorded into a hardware &lt;code&gt;RenderNode&lt;/code&gt;, and a &lt;code&gt;RenderEffect&lt;/code&gt; is attached to it. The effect is built back-to-front to mirror iOS's compositing order — blur first, then the material shader chained on top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;buildEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;RenderEffect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;radius&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GlassParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blurRadiusPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intensity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;variantClear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;density&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RenderEffect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createBlurEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CLAMP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;shader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glassShader&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;shaderActive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInputShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
  &lt;span class="n"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFloatUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"iResolution"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFloat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFloat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFloatUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"iCorner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
  &lt;span class="n"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFloatUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"iLens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;GlassParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lensStrengthPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;thickness&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ... vibrancy, tint, specular, tilt, touch uniforms ...&lt;/span&gt;

  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;material&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RenderEffect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRuntimeShaderEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;shaderActive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RenderEffect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createChainEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createChainEffect(material, blur)&lt;/code&gt; means: run &lt;code&gt;blur&lt;/code&gt; first, feed its output into the &lt;code&gt;content&lt;/code&gt; sampler of the shader. Note the blur is deliberately &lt;em&gt;light&lt;/em&gt; (a few dp, scaled by &lt;code&gt;intensity&lt;/code&gt;, and &lt;code&gt;clear&lt;/code&gt; gets a fraction of that) — a heavy blur destroys the very structure the lens needs to bend. The shader is compiled &lt;strong&gt;once&lt;/strong&gt;, at construction, and reused every frame; recompiling AGSL per frame would stutter visibly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;glassShader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RuntimeShader&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SDK_INT&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION_CODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TIRAMISU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;RuntimeShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GLASS_AGSL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the SDK is below 33, or the AGSL fails to compile, &lt;code&gt;glassShader&lt;/code&gt; is null and &lt;code&gt;buildEffect&lt;/code&gt; returns the bare blur. That's the first fallback tier, and it happens without a branch anywhere else in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shader: modelling a slab of glass, one pixel at a time
&lt;/h2&gt;

&lt;p&gt;This is the heart of it. The &lt;code&gt;content&lt;/code&gt; sampler is the blurred backdrop; the shader's job is to figure out, for each output pixel, &lt;em&gt;where in that backdrop to sample from&lt;/em&gt; so the result looks like light bending through a rounded slab of glass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Signed distance field.&lt;/strong&gt; Treat the view as a rounded rectangle and compute the exact signed distance to its edge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;sdRoundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDF is the backbone: it gives distance-to-edge for free, and its gradient gives the surface normal. From &lt;code&gt;d&lt;/code&gt; the shader derives a &lt;code&gt;rim&lt;/code&gt; ramp (0 in the interior → 1 at the very edge, across a band &lt;code&gt;lensW&lt;/code&gt; px wide).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Surface normal by differentiating the SDF.&lt;/strong&gt; The direction of steepest distance change is the outward normal — which way the glass surface tilts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;gx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sdRoundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iCorner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sdRoundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iCorner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;gy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sdRoundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iCorner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sdRoundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iCorner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1e-5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Inward refraction.&lt;/strong&gt; This is the signature move. Take a steep power of the rim ramp so almost all the bending happens in the outer ring, then push the sample coordinate &lt;em&gt;inward&lt;/em&gt; along the normal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;bend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rim&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rim&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;axisFade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;disp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;bend&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iLens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;edgeGuard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;magCoord&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;disp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;reflDisp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the shader samples from further &lt;em&gt;inside&lt;/em&gt; the backdrop near the rim, content behind the edge is magnified outward and appears to wrap around it — like the fat edge of a water droplet. The centre, where &lt;code&gt;rim&lt;/code&gt; is ~0, stays clear. &lt;code&gt;iLens&lt;/code&gt; is the edge displacement in pixels, scaled by the &lt;code&gt;thickness&lt;/code&gt; prop. &lt;code&gt;axisFade&lt;/code&gt; deserves a note: the SDF gradient collapses along a shape's medial axis (the ridge equidistant from two edges), and on a short pill the top and bottom lens rings meet there and the normal flips, producing a hard seam. Fading the lens to zero where the gradient magnitude drops kills the seam and leaves flat clear glass down the middle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Chromatic dispersion.&lt;/strong&gt; Real glass splits wavelengths at the rim. Sample R, G, B at slightly offset coordinates along the normal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;bend&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iLens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;iResolution&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;iResolution&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;iResolution&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;0.018&lt;/code&gt; factor is tiny on purpose. iOS UI glass shows almost no prismatic rainbow; crank the split and it reads as a chromatic-aberration &lt;em&gt;artefact&lt;/em&gt;, not glass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5 — The mirrored edge band.&lt;/strong&gt; Look at a glass pill on iOS and you'll see an upside-down echo of the content just outside it, folded back along the top and bottom. That's a second, broader displacement band at the rim (&lt;code&gt;reflDisp&lt;/code&gt; above, scaled independently by the &lt;code&gt;edgeReflectionStrength&lt;/code&gt; prop). Verified against iOS 26 side-by-side, the OS band is &lt;em&gt;broad and soft&lt;/em&gt; — a thin band reads as a hard streak, so this one spans roughly a third of the surface with a squared falloff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6 — Vibrancy, then adaptive frost.&lt;/strong&gt; Glass over-saturates what it transmits, so boost saturation while preserving luma, then lift only &lt;em&gt;dark&lt;/em&gt; backdrop toward neutral so foreground text stays legible — never a milky fill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;luma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;half3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2126&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7152&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;0722&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;half3&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;half3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;luma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;half3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;luma&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iSat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;adapt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iLift&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;luma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iAdapt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;half3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;adapt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 7 — Rim, Fresnel, specular, and the touch/tilt terms.&lt;/strong&gt; A clean even bright rim outline (independent of lighting, so it never clumps into blobs), a gentle Fresnel term, a broad diagonal sheen band, and a small specular hotspot whose light direction is driven by the gravity sensor when &lt;code&gt;tilt&lt;/code&gt; is on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;rimLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;fres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;float3&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;iTilt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;iTilt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the interactive touch magnifier — press and the glass optically zooms &lt;em&gt;toward&lt;/em&gt; your finger without the element changing size, exactly as iOS presses lens harder rather than resizing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coord&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;iTouch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;tFall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tmr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;magCoord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coord&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iTouchAmt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tFall&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One subtlety I'll call out because it bit me: an &lt;code&gt;edgeGuard = smoothstep(0.0, 12.0, -d)&lt;/code&gt; tapers &lt;em&gt;all&lt;/em&gt; refraction to zero across the outermost ~12px. Displacement peaks right at the rim, which is exactly where it magnifies sub-pixel normal noise into radial "cracked glass" hairlines. A thin calm bevel there erases the fringe; the crisp rim line still draws the edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Being honest about the constraints
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API 33+ for the full lens.&lt;/strong&gt; &lt;code&gt;RuntimeShader&lt;/code&gt; only exists from Android 13. That's the top tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API 31–32:&lt;/strong&gt; &lt;code&gt;RenderEffect&lt;/code&gt; blur exists, but no runtime shader, so you get blur + a Canvas-drawn tint and specular — no refraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Below API 31:&lt;/strong&gt; a translucent tint with a rim outline, and that's all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;minSdkVersion&lt;/code&gt; is 24&lt;/strong&gt;, so it installs and renders &lt;em&gt;something&lt;/em&gt; everywhere; only the lens needs 33. The view logs its actual tier once (&lt;code&gt;render tier=agsl|blur|tint&lt;/code&gt;) so you can tell "flat because fallback" from "flat because misconfigured" straight from &lt;code&gt;adb logcat -s LiquidGlass&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 2× capture&lt;/strong&gt; trades backdrop crispness for cost, per above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; the dirty hash means a static screen costs nothing after the first frame, and the shader compiles once. I'm deliberately &lt;em&gt;not&lt;/em&gt; quoting fps numbers — I don't have a rigorous benchmark, and made-up frame rates help no one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result isn't Apple's material — it's a model of it, tuned by eye against the real thing. But on a mid-range Android phone, over a photo or a scrolling feed, it reads as glass, not blur. That was the whole goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does React Native support Liquid Glass on Android?&lt;/strong&gt;&lt;br&gt;
Not natively — Android has no system Liquid Glass material. &lt;code&gt;react-native-liquid-glassmorphism&lt;/code&gt; adds it by capturing the backdrop and running a real-time AGSL refraction shader (&lt;code&gt;RuntimeShader&lt;/code&gt;) on Android 13 / API 33+, with graceful fallbacks below that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Android version do I need for Liquid Glass in React Native?&lt;/strong&gt;&lt;br&gt;
API 33+ (Android 13) for the full refraction lens. API 31–32 falls back to blur + a Canvas tint/specular; below API 31, a translucent tint. &lt;code&gt;minSdkVersion&lt;/code&gt; is 24, so it installs and renders &lt;em&gt;something&lt;/em&gt; everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this just a blur view?&lt;/strong&gt;&lt;br&gt;
No. A blur softens the backdrop in place; this &lt;em&gt;refracts&lt;/em&gt; it — displacing the backdrop through a modelled glass surface normal, with chromatic dispersion, a mirrored edge echo, and a Fresnel + specular rim. It's a lens, not a blur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is it different from expo-blur or @react-native-community/blur?&lt;/strong&gt;&lt;br&gt;
Those are blur-only. This renders real Liquid Glass optics (refraction) on Android, &lt;em&gt;and&lt;/em&gt; Apple's native &lt;code&gt;UIGlassEffect&lt;/code&gt; on iOS 26 — one &lt;code&gt;&amp;lt;LiquidGlassView&amp;gt;&lt;/code&gt; component, both platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does react-native-liquid-glassmorphism work in Expo Go?&lt;/strong&gt;&lt;br&gt;
No — it's a native module, so it needs a dev build / &lt;code&gt;expo prebuild&lt;/code&gt;. It ships an Expo config plugin to make that setup one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does it do on iOS?&lt;/strong&gt;&lt;br&gt;
On iOS 26 the same component composites Apple's native &lt;code&gt;UIGlassEffect&lt;/code&gt;; below iOS 26 it falls back to &lt;code&gt;UIBlurEffect&lt;/code&gt;. Full details and the per-OS fallback matrix are in the &lt;a href="https://himanshu-lal4.github.io/react-native-liquid-glassmorphism/android-liquid-glass/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you want to try it or read the source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;npm install react-native-liquid-glassmorphism&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/himanshu-lal4/react-native-liquid-glassmorphism" rel="noopener noreferrer"&gt;https://github.com/himanshu-lal4/react-native-liquid-glassmorphism&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://himanshu-lal4.github.io/react-native-liquid-glassmorphism/" rel="noopener noreferrer"&gt;https://himanshu-lal4.github.io/react-native-liquid-glassmorphism/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you build onboarding into your app, my other library — &lt;a href="https://github.com/himanshu-lal4/react-native-tour-guide" rel="noopener noreferrer"&gt;&lt;code&gt;@wrack/react-native-tour-guide&lt;/code&gt;&lt;/a&gt; — does spotlight tours and coach marks with auto shape-matching, and runs in Expo Go.&lt;/p&gt;

&lt;p&gt;Questions about any stage of the pipeline — the capture, the dirty-hash, the SDF math, the fallback tiers — are exactly what the comments are for. And if you've attempted glass on Android a different way (Compose, SurfaceView, Vulkan…), I'd really like to compare notes.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>android</category>
      <category>graphics</category>
      <category>shaders</category>
    </item>
    <item>
      <title>The Best React Native App Tour &amp; Onboarding Libraries in 2026 (Compared)</title>
      <dc:creator>Wrack</dc:creator>
      <pubDate>Mon, 27 Jul 2026 10:10:09 +0000</pubDate>
      <link>https://dev.to/wrack/the-best-react-native-app-tour-onboarding-libraries-in-2026-compared-354c</link>
      <guid>https://dev.to/wrack/the-best-react-native-app-tour-onboarding-libraries-in-2026-compared-354c</guid>
      <description>&lt;p&gt;Adding a guided tour, walkthrough, or coach marks to a React Native app sounds simple until you hit the real problems: measuring a component's position, dimming everything &lt;em&gt;except&lt;/em&gt; it, keeping the tooltip on screen, and scrolling to targets that are below the fold. Several libraries solve this. Here's an honest, hands-on comparison of the main options in 2026.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick answer:&lt;/strong&gt; &lt;strong&gt;react-native-copilot&lt;/strong&gt; is the long-standing default with the biggest community. &lt;strong&gt;rn-tourguide&lt;/strong&gt; adds SVG masking on top of the copilot model. &lt;strong&gt;react-native-spotlight-tour&lt;/strong&gt; is the cleanest simple option. &lt;strong&gt;react-native-tour-guide&lt;/strong&gt; is the pick if you want the spotlight to automatically match your component's shape, auto-scroll to off-screen targets, and run in &lt;strong&gt;Expo Go&lt;/strong&gt; with zero native dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngkj16qsescertw23f10.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngkj16qsescertw23f10.gif" alt="Demo of react-native-tour-guide on iOS: a spotlight overlay dims the screen, matches a circular avatar's shape exactly, and shows a positioned tooltip walking the user through the app step by step" width="300" height="652"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Expo Go&lt;/th&gt;
&lt;th&gt;Native deps&lt;/th&gt;
&lt;th&gt;Auto shape-match&lt;/th&gt;
&lt;th&gt;Auto-scroll&lt;/th&gt;
&lt;th&gt;TS&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;react-native-copilot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ needs config&lt;/td&gt;
&lt;td&gt;Some&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The long-time default, large install base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rn-tourguide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;react-native-svg&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fork of copilot with SVG masking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;react-native-spotlight-tour&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;Some&lt;/td&gt;
&lt;td&gt;Circle/rect only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Simple spotlight, good docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;react-native-tour-guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ works in Go&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt; (svg peer only)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes&lt;/strong&gt; (per-corner)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Shape-accurate spotlights, Expo Go, scrolling screens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What to look for in a React Native tour library
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Spotlight accuracy&lt;/strong&gt; — does the cutout match your component's shape (a circular avatar, a pill button, a card with 12px corners), or is it always a rectangle/circle you configure by hand?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scrolling&lt;/strong&gt; — will it scroll a target into view if it's off-screen, keeping the tooltip visible too?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo compatibility&lt;/strong&gt; — does it run in &lt;strong&gt;Expo Go&lt;/strong&gt;, or do you need a custom dev build (native modules)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooltip positioning&lt;/strong&gt; — does it auto-pick a side and stay within safe-area insets, or do you position every step manually?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New Architecture (Fabric)&lt;/strong&gt; — does the overlay render correctly without a white film over the highlight?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. &lt;a href="https://github.com/mohebifar/react-native-copilot" rel="noopener noreferrer"&gt;react-native-copilot&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The most-installed option and the one most tutorials reference. You wrap components with &lt;code&gt;copilotStep&lt;/code&gt;/&lt;code&gt;walkthroughable&lt;/code&gt; HOCs and start a tour. Mature and battle-tested, with a large community.&lt;/p&gt;

&lt;p&gt;Trade-offs: highlight shapes are basic, positioning often needs manual tuning, and getting it running under Expo/New Architecture can require extra setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://github.com/xcarpentier/rn-tourguide" rel="noopener noreferrer"&gt;rn-tourguide&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A popular fork of copilot that uses &lt;code&gt;react-native-svg&lt;/code&gt; for the mask and adds circle/rectangle shapes and some animation. A solid step up in visuals if you're already in the copilot mental model.&lt;/p&gt;

&lt;p&gt;Trade-offs: no automatic per-corner shape matching, no built-in auto-scroll, and the SVG &lt;code&gt;&amp;lt;Mask&amp;gt;&lt;/code&gt; approach can show a faint film over the highlight on Fabric in some setups.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://github.com/stackbuilders/react-native-spotlight-tour" rel="noopener noreferrer"&gt;react-native-spotlight-tour&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A clean, well-documented library built around a spotlight metaphor with a nice hook-based API. Great if you want something simple and readable.&lt;/p&gt;

&lt;p&gt;Trade-offs: spotlight shapes are limited to circle/rectangle, and scrolling to off-screen targets is on you.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://github.com/himanshu-lal4/react-native-tour-guide" rel="noopener noreferrer"&gt;react-native-tour-guide&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A newer, lightweight library focused on the two things the others make you do by hand: &lt;strong&gt;shape-accurate spotlights&lt;/strong&gt; and &lt;strong&gt;auto-scroll&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fznuqrzcf9h4nuhwxy2x1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fznuqrzcf9h4nuhwxy2x1.gif" alt="Android demo of react-native-tour-guide: the same spotlight walkthrough running on Android — dimmed overlay, shape-matched highlight and auto-positioned tooltip, identical to the iOS behavior" width="300" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto shape matching&lt;/strong&gt; — pass the same style you use on the component as &lt;code&gt;targetStyle&lt;/code&gt;, and the spotlight reads its &lt;code&gt;borderRadius&lt;/code&gt; and matches it exactly. Circular avatars get circular spotlights; a chat bubble with asymmetric corners gets an asymmetric spotlight; a &lt;code&gt;borderRadius: 12&lt;/code&gt; card stays 12px.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart auto-scroll&lt;/strong&gt; — give it a &lt;code&gt;scrollRef&lt;/code&gt; and it scrolls so the target &lt;em&gt;and&lt;/em&gt; its tooltip are both fully visible, hiding the highlight mid-scroll to avoid flicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart tooltip positioning&lt;/strong&gt; — auto-detects the best side and clamps within safe-area insets so it never renders off-screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs in Expo Go&lt;/strong&gt; — zero native dependencies (only &lt;code&gt;react-native-svg&lt;/code&gt;, which Expo ships). No custom dev build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fabric-safe&lt;/strong&gt; — the overlay is a single even-odd SVG path (a real punched-out hole), not a &lt;code&gt;&amp;lt;Mask&amp;gt;&lt;/code&gt;, so there's no white film on the New Architecture.&lt;/li&gt;
&lt;li&gt;Themeable tooltips + &lt;code&gt;createTheme()&lt;/code&gt;, pulse animation, pause/resume, conditional steps, and a &lt;code&gt;useTourPersistence&lt;/code&gt; hook for "show only once."
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @wrack/react-native-tour-guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TourGuideProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TourGuideOverlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useTourGuide&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wrack/react-native-tour-guide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Wrap once&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TourGuideProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TourGuideOverlay&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TourGuideProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Start a tour&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;startTour&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTourGuide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;startTour&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tap here to begin.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;targetStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;avatarRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;View your profile.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;targetStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// circular → circular spotlight&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trade-offs: it's newer and has a smaller community than copilot. If you need the exact copilot API or its ecosystem of examples, that's still the incumbent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which should you pick?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Want the safe, most-referenced default and don't mind manual tuning?&lt;/strong&gt; → react-native-copilot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Already like the copilot model but want nicer SVG visuals?&lt;/strong&gt; → rn-tourguide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want something minimal and well-documented, rectangles/circles are fine?&lt;/strong&gt; → react-native-spotlight-tour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want the spotlight to match your real component shapes, need auto-scroll on long screens, and want it to just work in Expo Go with no dev build?&lt;/strong&gt; → &lt;strong&gt;react-native-tour-guide&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I add an app tour or walkthrough in React Native?
&lt;/h3&gt;

&lt;p&gt;Use a tour library instead of hand-rolling the overlay: wrap your app in the library's provider, attach a ref to each component you want to highlight, and start the tour with an array of steps. The hard parts a library solves for you are measuring the target's position, punching a hole in the dimmed overlay, and positioning the tooltip.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which React Native tour library works with Expo Go?
&lt;/h3&gt;

&lt;p&gt;react-native-tour-guide runs in Expo Go out of the box because its only dependency is &lt;code&gt;react-native-svg&lt;/code&gt;, which Expo Go already ships. Most alternatives need extra configuration or a custom development build.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I highlight a component with a spotlight that matches its shape?
&lt;/h3&gt;

&lt;p&gt;With react-native-tour-guide, pass the component's own style object as &lt;code&gt;targetStyle&lt;/code&gt; — the spotlight reads its &lt;code&gt;borderRadius&lt;/code&gt; (including per-corner values) and matches the cutout exactly. Other libraries limit you to configured rectangles or circles.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I scroll to a tour step that's off-screen?
&lt;/h3&gt;

&lt;p&gt;Pass a &lt;code&gt;scrollRef&lt;/code&gt; to react-native-tour-guide and it auto-scrolls so both the target and its tooltip are fully visible, hiding the highlight mid-scroll to avoid flicker. In copilot, rn-tourguide, and spotlight-tour, scrolling to off-screen targets is largely manual.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I show an onboarding tour only once?
&lt;/h3&gt;

&lt;p&gt;react-native-tour-guide ships a &lt;code&gt;useTourPersistence&lt;/code&gt; hook for exactly this — it records completion so the tour only runs on first launch. With other libraries you'd wire this up yourself with AsyncStorage.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://himanshu-lal4.github.io/react-native-tour-guide/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; · &lt;a href="https://github.com/himanshu-lal4/react-native-tour-guide" rel="noopener noreferrer"&gt;GitHub (source + demos)&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/@wrack/react-native-tour-guide" rel="noopener noreferrer"&gt;npm package&lt;/a&gt; — if it saves you time, a ⭐ on GitHub helps other people find it.&lt;/p&gt;

&lt;p&gt;I wrote this comparison after building the fourth library on the list, so I'm biased but I've tried to be fair — if your experience with copilot, rn-tourguide, or spotlight-tour differs, tell me in the comments and I'll update the post. Also curious: what's the most annoying part of building onboarding in RN for you?&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>ux</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Best React Native Liquid Glass &amp; Glassmorphism Libraries in 2026 (Compared)</title>
      <dc:creator>Wrack</dc:creator>
      <pubDate>Mon, 27 Jul 2026 08:59:07 +0000</pubDate>
      <link>https://dev.to/wrack/the-best-react-native-liquid-glass-glassmorphism-libraries-in-2026-compared-15o6</link>
      <guid>https://dev.to/wrack/the-best-react-native-liquid-glass-glassmorphism-libraries-in-2026-compared-15o6</guid>
      <description>&lt;p&gt;Apple's &lt;strong&gt;Liquid Glass&lt;/strong&gt; (iOS 26) made translucent, refractive glass the default look for modern apps — and every React Native developer now wants it. The catch: most "glass" in React Native is really just &lt;strong&gt;blur&lt;/strong&gt;, and the libraries that render &lt;em&gt;real&lt;/em&gt; Liquid Glass are &lt;strong&gt;iOS-only&lt;/strong&gt;, leaving Android out. Here's an honest, hands-on comparison of the options in 2026, including the one that does Android too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick answer:&lt;/strong&gt; If you only need a blurred backdrop on both platforms, use &lt;strong&gt;expo-blur&lt;/strong&gt;. If you're iOS-only and want Apple's native Liquid Glass, use &lt;strong&gt;expo-glass-effect&lt;/strong&gt; or &lt;strong&gt;@callstack/liquid-glass&lt;/strong&gt;. If you want real Liquid Glass on iOS &lt;strong&gt;and&lt;/strong&gt; a matching refractive glass effect on &lt;strong&gt;Android&lt;/strong&gt;, &lt;strong&gt;react-native-liquid-glassmorphism&lt;/strong&gt; is currently the only option — it renders native &lt;code&gt;UIGlassEffect&lt;/code&gt; on iOS 26 and a real-time AGSL refraction shader on Android.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhimanshu-lal4%2Freact-native-liquid-glassmorphism%2Freleases%2Fdownload%2Fv0.1.0%2Freel-side-by-side-smooth.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhimanshu-lal4%2Freact-native-liquid-glassmorphism%2Freleases%2Fdownload%2Fv0.1.0%2Freel-side-by-side-smooth.gif" alt="Side-by-side demo: the same React Native glass scene rendered by react-native-liquid-glassmorphism on iOS 26 using native UIGlassEffect (left) and on Android using a real-time AGSL refraction shader (right)" width="376" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;expo-blur&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;@react-native-community/blur&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;expo-glass-effect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;@callstack/liquid-glass&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;react-native-liquid-glassmorphism&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real Liquid Glass&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Works on Android&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ (blur only)&lt;/td&gt;
&lt;td&gt;✅ (blur only)&lt;/td&gt;
&lt;td&gt;❌ iOS-only&lt;/td&gt;
&lt;td&gt;❌ iOS-only&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;AGSL shader&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Refraction (not just blur)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ (iOS, OS-rendered)&lt;/td&gt;
&lt;td&gt;✅ (iOS, OS-rendered)&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;both platforms&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom shapes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple backdrop blur, both platforms&lt;/td&gt;
&lt;td&gt;Bare-workflow blur&lt;/td&gt;
&lt;td&gt;iOS-only apps, least code&lt;/td&gt;
&lt;td&gt;iOS-only, native wrapper&lt;/td&gt;
&lt;td&gt;Real glass on iOS &lt;strong&gt;and&lt;/strong&gt; Android&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice the &lt;strong&gt;Works on Android&lt;/strong&gt; row: &lt;strong&gt;every other option gives Android either a plain blur or nothing at all.&lt;/strong&gt; As of 2026, react-native-liquid-glassmorphism is the only React Native library that renders real glass optics — refraction, not blur — on Android. That single difference is what this comparison mostly comes down to.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to look for in a React Native glass library
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Real glass vs. blur&lt;/strong&gt; — does it &lt;em&gt;refract&lt;/em&gt; the backdrop (bend and magnify it through a glass surface), or just blur it? Real Liquid Glass has edge lensing, a Fresnel rim, and chromatic dispersion; a blur view has none of that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt; — Apple's Liquid Glass is iOS-only. Does the library give you &lt;em&gt;anything&lt;/em&gt; comparable on Android, or does Android fall back to a flat blur (or nothing)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactivity&lt;/strong&gt; — does the glass react to touch and device tilt with a moving specular highlight, like the real thing?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shapes&lt;/strong&gt; — can the glass be a circle, a squircle, or a notched tab-bar silhouette, or is it always a rectangle?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallbacks&lt;/strong&gt; — does it degrade cleanly on iOS &amp;lt; 26 and older Android, or break?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. &lt;a href="https://docs.expo.dev/versions/latest/sdk/blur-view/" rel="noopener noreferrer"&gt;expo-blur&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The default for a quick blurred backdrop in Expo apps. Cross-platform, well-supported, trivial to use. But it's &lt;strong&gt;blur only&lt;/strong&gt; — no refraction, no iOS-26 glass material, no interactivity. Great for a frosted nav bar; not a Liquid Glass look.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://github.com/Kureev/react-native-blur" rel="noopener noreferrer"&gt;@react-native-community/blur&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The long-standing bare-workflow blur view. Same story as expo-blur: solid blur, no glass optics.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://docs.expo.dev/versions/latest/sdk/glass-effect/" rel="noopener noreferrer"&gt;expo-glass-effect&lt;/a&gt; / &lt;a href="https://github.com/callstack/liquid-glass" rel="noopener noreferrer"&gt;@callstack/liquid-glass&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;These wrap Apple's &lt;strong&gt;native&lt;/strong&gt; iOS 26 Liquid Glass material and look fantastic — because it's the real OS effect. The limitation is fundamental: &lt;strong&gt;iOS 26 only&lt;/strong&gt;. On Android (and older iOS) both simply &lt;strong&gt;fall back to a plain, opaque &lt;code&gt;View&lt;/code&gt;&lt;/strong&gt; — there's no glass. Perfect if your app is iOS-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://github.com/himanshu-lal4/react-native-liquid-glassmorphism" rel="noopener noreferrer"&gt;react-native-liquid-glassmorphism&lt;/a&gt; — the only one that does Android
&lt;/h2&gt;

&lt;p&gt;Built specifically for the gap: &lt;strong&gt;real Liquid Glass on both platforms&lt;/strong&gt; from one component. Your Android users are probably half your user base or more — with every other library on this list, they get a flat blur at best while your iOS users get glass.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS 26&lt;/strong&gt; — renders Apple's native &lt;code&gt;UIGlassEffect&lt;/code&gt; (with a &lt;code&gt;UIBlurEffect&lt;/code&gt; fallback below 26).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt; — there's no system Liquid Glass, so it reproduces the optics in a real-time &lt;strong&gt;AGSL refractive-lens shader&lt;/strong&gt;: it captures the backdrop each frame, blurs it, and bends it through a rounded-glass lozenge with &lt;strong&gt;edge refraction, chromatic dispersion, a mirrored edge reflection, a Fresnel rim, and tilt/touch specular highlights&lt;/strong&gt;. This is the part no other RN library does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive&lt;/strong&gt; — a specular bloom + optical magnification under the finger, and a specular that tracks device tilt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom shapes&lt;/strong&gt; — &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;squircle&lt;/code&gt;, &lt;code&gt;polygon&lt;/code&gt;, explicit &lt;code&gt;points&lt;/code&gt;, or an arbitrary (even &lt;strong&gt;concave&lt;/strong&gt;) SVG &lt;code&gt;path&lt;/code&gt; (a notched tab bar, for example). The glass lenses the backdrop &lt;em&gt;through&lt;/em&gt; the silhouette.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degrades cleanly&lt;/strong&gt; — Android 12 → blur + tint; below 12 → translucent tint. iOS &amp;lt; 26 → blur.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-native-liquid-glassmorphism
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LiquidGlassView&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-liquid-glassmorphism&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LiquidGlassView&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"regular"&lt;/span&gt; &lt;span class="na"&gt;tintColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rgba(10,132,255,0.5)"&lt;/span&gt; &lt;span class="na"&gt;interactive&lt;/span&gt; &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Frosted glass content&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LiquidGlassView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trade-offs: it's a native module (needs a dev build / prebuild, not Expo Go), it's newer with a smaller community than expo-blur, and it's mobile-only (no web yet). If you only need a blur, expo-blur is simpler; if you're iOS-only, expo-glass-effect is less code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which should you pick?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Just need a blurred backdrop on both platforms?&lt;/strong&gt; → expo-blur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS-only and want Apple's native Liquid Glass with minimal code?&lt;/strong&gt; → expo-glass-effect / @callstack/liquid-glass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want real Liquid Glass on iOS &lt;em&gt;and&lt;/em&gt; a matching refractive glass effect on Android, with custom shapes and interactivity?&lt;/strong&gt; → &lt;strong&gt;react-native-liquid-glassmorphism&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Liquid Glass work on Android?
&lt;/h3&gt;

&lt;p&gt;Not natively — Liquid Glass is an Apple system material and Android has no equivalent. The only way to get the look on Android today is to reproduce the optics with a shader, which is what react-native-liquid-glassmorphism does: per-frame backdrop capture, a &lt;code&gt;RenderEffect&lt;/code&gt; blur, and an AGSL refraction shader (Android 13+, with blur/tint fallbacks below that).&lt;/p&gt;

&lt;h3&gt;
  
  
  Is expo-blur the same as Liquid Glass?
&lt;/h3&gt;

&lt;p&gt;No. expo-blur is a backdrop &lt;strong&gt;blur&lt;/strong&gt; — it softens what's behind the view. Liquid Glass is a &lt;strong&gt;lens&lt;/strong&gt;: it refracts and magnifies the backdrop at the edges, disperses color at the rim, and reacts to touch and tilt. A blur view gives you none of that.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I add a glassmorphism effect in React Native?
&lt;/h3&gt;

&lt;p&gt;For a simple frosted look, wrap your content in expo-blur's &lt;code&gt;BlurView&lt;/code&gt; with a translucent background color. For actual glass optics on both platforms, use &lt;code&gt;LiquidGlassView&lt;/code&gt; from react-native-liquid-glassmorphism — one component, no per-platform code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does react-native-liquid-glassmorphism work in Expo Go?
&lt;/h3&gt;

&lt;p&gt;No — it's a native module, so you need a development build (&lt;code&gt;npx expo prebuild&lt;/code&gt; or EAS Build). It ships a config plugin, so setup in a managed Expo project is still one line in &lt;code&gt;app.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens on older iOS and Android versions?
&lt;/h3&gt;

&lt;p&gt;It degrades tier by tier instead of breaking: iOS below 26 gets &lt;code&gt;UIBlurEffect&lt;/code&gt;; Android 12 gets blur + tint; below Android 12 gets a translucent tint with a rim outline.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://himanshu-lal4.github.io/react-native-liquid-glassmorphism/" rel="noopener noreferrer"&gt;Docs &amp;amp; guides&lt;/a&gt; · &lt;a href="https://github.com/himanshu-lal4/react-native-liquid-glassmorphism" rel="noopener noreferrer"&gt;GitHub (source + side-by-side iOS/Android demos)&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/react-native-liquid-glassmorphism" rel="noopener noreferrer"&gt;npm package&lt;/a&gt; — if it saves you time, a ⭐ on GitHub helps other people find it.&lt;/p&gt;

&lt;p&gt;I built the Android renderer for this library from scratch (backdrop capture + AGSL refraction shader), so if you have questions about how any of it works — or you think I got a comparison wrong — drop a comment. I'd genuinely like to hear what glass effect you're trying to build.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>javascript</category>
      <category>android</category>
    </item>
  </channel>
</rss>
