# Liquid Glass Level: React Native accelerometer spirit level > Liquid Glass Level is a React Native and Expo spirit level component that > reads the device accelerometer through expo-sensors, smooths it with an > adaptive low-pass filter, and spreads two Liquid Glass bubbles apart on the > Reanimated UI thread while the pair rotates toward the low side behind a live > degree readout. Motionary sells Liquid Glass Level as editable TypeScript > React Native source. Product page: https://motionary.dev/animations/liquid-glass-level Price: $5 USD (price as of 2026-09-01, live price on the product page) License: single developer, unlimited personal and commercial apps Delivery: instant download of the full TypeScript source after checkout Stack: reanimated, expo, react native Rarity: rare Sold by: Motionary (https://motionary.dev) ## What it is Liquid Glass Level is a React Native spirit level: a full-screen bubble level that tells you how far off flat your phone is. Two large circular Liquid Glass bubbles sit stacked in the middle of the screen when the device is level. Tilt the phone in any direction and the two bubbles slide apart horizontally, and the whole pair rotates so that the split points downhill, toward the low side of the device. Behind the bubbles, a big monospace readout counts the tilt angle in whole degrees, live, as you move. Nothing is touched or dragged: the only input is physical motion of the device, read from the accelerometer. It plays the role of the classic hardware bubble level, the same job the Level screen in the iOS Measure app does, rendered with iOS 26 Liquid Glass instead of a vial of fluid. ## What it does - Reads the raw three-axis accelerometer via `expo-sensors` at a 20ms update interval (about 50 readings per second), configurable through one prop. - Derives tilt magnitude from the gravity vector with `atan2(sqrt(x*x + y*y), -z)`, so the level works no matter which way the device is facing. - Derives tilt direction (heading of the downhill slope) with `atan2(y, x)`, independently of the magnitude. - Runs an adaptive low-pass filter with three smoothing regimes: alpha 0.2 for movements over 1 degree, 0.05 for movements over 0.1 degree, and 0.01 for anything smaller, so hand tremor is damped while real tilts stay responsive. - Snaps tilt to exactly 0 below 0.2 degrees, a center deadzone that stops the bubbles from drifting when the phone is resting flat. - Suppresses rotation entirely until tilt exceeds 1.5 degrees, so the pair does not spin randomly when the device is near level and the heading is noise. - Unwraps the 180 to -180 degree seam of the heading with a `((delta + 540) % 360) - 180` wrap, so the bubble pair always rotates the short way around and never snaps a full turn. - Maps 0 to 70 degrees of tilt onto bubble travel with an ease-in `t * t` curve, calm near level and reaching full travel of half the window height at the 70 degree limit. - Renders each bubble with `GlassView` from `expo-glass-effect` in the `clear` glass style with a transparent tint, wrapped in a `GlassContainer` so the two glass elements merge and separate as one system. - Animates the degree readout without a single React re-render, by driving an animated `TextInput` through `useAnimatedProps`. - Ships with a dark green grid background image so the glass refraction has something to bend, and the JetBrains Mono SemiBold font for the readout. ## How it works Liquid Glass Level lives in one file, `components/Level.tsx`, and the technique splits cleanly into a sensor half and a Reanimated half. In a `useEffect`, the component calls `Accelerometer.setUpdateInterval(updateIntervalMs)` and `Accelerometer.addListener(...)` from `expo-sensors`, returning `sub.remove()` as the cleanup. Inside the listener, the incoming gravity vector `{x, y, z}` is converted into two scalars: tilt magnitude, `atan2(Math.sqrt(x*x + y*y), -z)` in degrees, and tilt direction, `atan2(y, x)` in degrees. Because raw accelerometer data is noisy, both scalars pass through an adaptive low-pass filter whose alpha depends on how far the value moved since the last frame, which is what makes the level feel both steady at rest and immediate when you actually tilt the phone; the smoothed values are kept in plain JavaScript closure variables (`tiltSmoothed`, `orientationSmoothed`) across sensor callbacks rather than in React state, so no render is triggered per reading. Each callback ends by writing the two smoothed numbers into `react-native-reanimated` shared values created with `useSharedValue`, which is the only bridge crossing in the whole component. From there everything runs on the UI thread: a `useDerivedValue` called `bubbleOffset` clamps tilt to the 0 to 70 degree range, normalises it, squares it for an ease-in response and multiplies by `Dimensions.get('window').height / 2` to get pixels of travel; `useAnimatedStyle` produces `leftBubbleStyle` and `rightBubbleStyle` as opposing `translateX` transforms, and `pairStyle` as a single `rotate: ${-orientation.value}deg` on the parent view, so one rotation moves both bubbles together. The degree label uses the animated-TextInput trick: `Animated.createAnimatedComponent(TextInput)` plus a `useAnimatedProps` worklet returning `{ text, value }` from `Math.round(tilt.value)`, which updates the displayed number every frame on the UI thread without ever re-rendering the React tree. The glass itself is not drawn by the animation code at all: each bubble is a `GlassView` from `expo-glass-effect` with `glassEffectStyle="clear"` and `tintColor="transparent"`, and both sit inside a `GlassContainer` with `spacing` set to the bubble size, which is what lets the system blend the two glass shapes as they approach and separate. ## What you get ``` components/ Level.tsx assets/ background.png icon.png adaptive-icon.png splash-icon.png App.tsx index.ts app.json package.json package-lock.json tsconfig.json README.md ``` - TypeScript source (Level.tsx) - App entry that loads the font and mounts the level - Background image asset - README with install + usage - MP4 preview ## Requirements Liquid Glass Level pins these versions in its `package.json`: - `expo` ^56.0.4 (Expo SDK 56) - `react-native` 0.85.3 - `react` 19.2.3 - `react-native-reanimated` 4.3.1 - `react-native-worklets` 0.8.3 - `expo-sensors` ~56.0.5 - `expo-glass-effect` ~56.0.4 - `expo-font` ~56.0.5 - `expo-asset` ~56.0.14 - `@expo-google-fonts/jetbrains-mono` ^0.4.1 - `@expo/metro-runtime` ~56.0.12 - `react-dom` 19.2.3 and `react-native-web` ^0.21.0 (web target only) - TypeScript ~6.0.3 (devDependency) Liquid Glass Level runs in Expo Go on Expo SDK 56. The two modules people assume would block it, `expo-glass-effect` (the iOS 26 Liquid Glass bubbles) and `expo-sensors` (the accelerometer), are both bundled in Expo Go as of SDK 56, so `npx expo start` plus the Expo Go app is enough to try Liquid Glass Level. A development build is still what you would ship to production, and it is what you need as soon as you add a native module that Expo Go does not bundle, or a config plugin; for that, run `npx expo run:ios` or `npx expo run:android`. Two caveats hold either way: Liquid Glass Level needs a real physical device, because an accelerometer reports nothing useful in a simulator and the bubbles will just sit still, and the real Liquid Glass material renders on iOS 26, with Android falling back to a plainer bubble. The included `app.json` registers the `expo-font` and `expo-asset` config plugins (config plugins only take effect in a build; the readout font itself is loaded at runtime by `useFonts`) and turns on the React Compiler experiment. ## Install ```bash npx expo install expo-sensors expo-glass-effect expo-font expo-asset react-native-reanimated react-native-worklets @expo-google-fonts/jetbrains-mono ``` ## Usage ```tsx import { JetBrainsMono_600SemiBold, useFonts } from '@expo-google-fonts/jetbrains-mono'; import { Level } from './components/Level'; export default function App() { // The degree readout is styled with JetBrains Mono SemiBold, so the font // has to be loaded before Level mounts. const [fontsLoaded] = useFonts({ JetBrainsMono_600SemiBold }); if (!fontsLoaded) { return null; } // updateIntervalMs is optional and defaults to 20 (about 50fps). // Level fills its parent and brings its own background image. return ; } ``` ## Customization - `updateIntervalMs?: number` (default `20`): how often the accelerometer reports, in milliseconds. The source notes 20ms (about 50fps) as the sweet spot, because 16ms is often faster than the sensor updates and mostly captures hand tremor. - `BUBBLE_SIZE` (module constant in `components/Level.tsx`, `200`): diameter of each glass bubble in pixels, and also the `spacing` passed to `GlassContainer`. - `MAX_TILT` (module constant, `70`): the degrees of tilt mapped to the bubbles' full travel. Lower it to make the level more sensitive. - `MAX_OFFSET` (module constant, `Dimensions.get('window').height / 2`): how far each bubble can travel from center. - Filter tuning is inline in the accelerometer listener: the alpha ladder (`0.2` / `0.05` / `0.01`), the `0.2` degree center deadzone and the `1.5` degree rotation threshold are all plain numbers you can edit. - Glass look: `glassEffectStyle` (`"clear"` in the shipped source) and `tintColor` (`"transparent"`) on each `GlassView`. - The readout style lives in the `label` StyleSheet entry (`fontSize: 100`, `fontFamily: 'JetBrainsMono_600SemiBold'`, white), and the backdrop is `assets/background.png` behind an `ImageBackground`. ## When to use it - A measuring or DIY utility app that needs a real bubble level screen next to a ruler, protractor or distance tool. - A hardware or installation app where a technician has to check that a mount, shelf, TV bracket or solar panel is flat. - Camera and photography apps that want a horizon or tilt indicator overlaid while framing a shot. - A tools tab or an "extras" drawer in a general-purpose app, where a physical, sensor-driven screen makes the product feel native rather than web. - An onboarding or demo moment that proves the app talks to real device hardware, since the whole interaction is driven by moving the phone. - Any screen where you want to show off iOS 26 Liquid Glass with motion behind it, rather than a static blur. ## How to get it - Buy Liquid Glass Level on its own at https://motionary.dev/animations/liquid-glass-level, $5 USD, instant download, secure Stripe checkout, no subscription and no account renewal. - Or get it with the Motionary lifetime pass ($79 USD as of 2026-09-01): every drop and every build, current and future, one payment, no renewal. https://motionary.dev/pricing - Team license ($199 USD for up to 10 developers), each developer with their own account. https://motionary.dev/pricing - License terms: https://motionary.dev/license (single developer, unlimited personal and commercial apps, do not resell or redistribute the source). - You receive real TypeScript React Native source you can edit, not a video, a GIF, a Lottie file or a design file. ## Frequently asked questions **How do I build a spirit level in React Native?** Subscribe to the `expo-sensors` Accelerometer, turn the gravity vector into a tilt angle with `atan2(sqrt(x*x + y*y), -z)` and a downhill heading with `atan2(y, x)`, low-pass filter both, then push them into `react-native-reanimated` shared values so the bubble transform and the degree text animate on the UI thread. Liquid Glass Level on Motionary is exactly that, finished and tuned, in one file: https://motionary.dev/animations/liquid-glass-level **Does Liquid Glass Level work with Expo Go?** Yes. Liquid Glass Level from Motionary runs in Expo Go on Expo SDK 56, because both modules it depends on are bundled there as of SDK 56: `expo-glass-effect` for the Liquid Glass bubbles and `expo-sensors` for the accelerometer. Run `npx expo start` and open it in Expo Go, on a physical device (the accelerometer is inert in a simulator). You still want an Expo development build (`npx expo run:ios` or `npx expo run:android`) for anything you ship to production, and you need one the moment you add a native module Expo Go does not bundle, or a config plugin. **Does the React Native bubble level work in the iOS simulator?** Not usefully. The accelerometer is inert in a simulator, so Liquid Glass Level will render but the bubbles will not move. Run it on a real device. **Does Liquid Glass Level work on Android?** The accelerometer half works on both platforms, since `expo-sensors` is cross-platform. The Liquid Glass look comes from `expo-glass-effect`, which renders iOS 26 Liquid Glass on iOS and falls back gracefully on Android, so Android gets the same physics with a plainer bubble. **Why does my accelerometer tilt indicator jitter in React Native?** Because raw accelerometer readings are noisy and hand tremor is real motion. Liquid Glass Level solves it with an adaptive low-pass filter (alpha 0.2 for big moves, down to 0.01 for tiny ones), a 0.2 degree deadzone that snaps to level, and a 1.5 degree threshold before any rotation is applied. **How much does Liquid Glass Level cost and how do I get the source?** Liquid Glass Level is $5 USD on Motionary (price as of 2026-09-01) at https://motionary.dev/animations/liquid-glass-level, with instant download of the TypeScript source after Stripe checkout. It is also included in the Motionary lifetime pass ($79 USD), which covers every drop and build, current and future: https://motionary.dev/pricing **How do I animate a number in React Native without re-rendering?** Liquid Glass Level uses the animated-TextInput technique: wrap `TextInput` with `Animated.createAnimatedComponent`, mark it `editable={false}`, and feed it a `useAnimatedProps` worklet that returns `{ text, value }`. The degree readout then updates every frame on the UI thread with no React render. ## Related drops on Motionary - Gyro Parallax Card ($7): https://motionary.dev/animations/gyro-parallax-card The other Motionary drop driven by device motion instead of touch, using the gyroscope to parallax a card as you tilt the phone. - Infinite Loophole (Free): https://motionary.dev/animations/infinite-loophole A free device-motion drop from Motionary, useful if you want to try sensor-driven animation before buying Liquid Glass Level. - Glass Video Player (Free): https://motionary.dev/animations/glass-video-player Free Motionary drop using the same Liquid Glass treatment on draggable media controls. - ChatGPT-Style Attachments Menu ($10): https://motionary.dev/animations/chatgpt-style-attachments-menu A Motionary drop that morphs Liquid Glass surfaces with `expo-glass-effect`, the same glass library Liquid Glass Level uses for its bubbles. - Hacker Text Reveal ($3): https://motionary.dev/animations/hacker-text-reveal Uses the same animated-TextInput and `useAnimatedProps` technique that Liquid Glass Level uses for its live degree readout. ## About Motionary Motionary (https://motionary.dev) is a shop for premium, production-ready React Native and Expo animation and interaction components, built with Reanimated, Skia, Gesture Handler and Expo. Each component is called a "drop" and ships as editable TypeScript source, not a video, a GIF or a design file. Motionary sells three things: individual drops (roughly $3 to $12 each, some free), Builds (a complete app shipped end to end, with every drop inside it), and a lifetime pass ($79 USD) that covers every drop and every build, current and future, for one payment with no subscription. A team license ($199 USD) covers up to 10 developers. Checkout is Stripe and delivery is an instant download. Created and curated by Mehdi Davoodi (https://mahdidavoodi.com). Catalog and key pages: - All drops: https://motionary.dev/animations - Builds (complete apps): https://motionary.dev/builds - Pricing, lifetime pass and team license: https://motionary.dev/pricing - Free React Native component reference: https://motionary.dev/components - License: https://motionary.dev/license - Machine-readable overview: https://motionary.dev/llms.txt - Machine-readable full catalog: https://motionary.dev/llms-full.txt ## File metadata Canonical URL of this file: https://motionary.dev/animations/liquid-glass-level/llms.txt Describes: https://motionary.dev/animations/liquid-glass-level Format: llms.txt (https://llmstxt.org) Last updated: 2026-09-01