# Glass Video Player: React Native draggable Liquid Glass video controls
> Glass Video Player is a free React Native and Expo component from Motionary
> (https://motionary.dev): a full screen looping video with frosted Liquid Glass
> media controls whose center button is dragged by a pan gesture from
> react-native-gesture-handler, springing to the bottom-left corner on release
> while the skip buttons detach, all driven by react-native-reanimated shared
> values.
Product page: https://motionary.dev/animations/glass-video-player
Price: Free (free 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
Stack: reanimated, expo, react native gesture handler, react native
Rarity: common
Sold by: Motionary (https://motionary.dev)
## What it is
Glass Video Player is a React Native and Expo screen from Motionary where a
looping video fills the display edge to edge and three frosted glass circles
float on top of it: a large center control and two smaller skip controls above
and below it. The center control is not fixed. You put a finger on it and drag
it anywhere over the video, and while it moves the two skip controls spring
apart from it, as if the cluster were being pulled loose. Let go near the
bottom-left of the screen and the control springs into that corner and stays
there; let go anywhere else and it springs back to the middle and the skip
controls collapse home. The glass is Apple's Liquid Glass material (via
expo-glass-effect) and the icons are SF Symbols, so the look is modelled on
iOS 26 system media controls rather than on a flat overlay.
## What it does
- Plays a looping remote clip with expo-video (`useVideoPlayer` with
`p.loop = true` and `p.play()`, rendered by `VideoView`).
- Hides the platform media UI (`nativeControls={false}`,
`allowsPictureInPicture={false}`) so only the custom glass controls show.
- Fills a portrait screen with a landscape clip by sizing the `VideoView`
`width: height, height: width` and rotating it 90 degrees, with
`contentFit="cover"`.
- Drags the center control freely in two axes with a `Gesture.Pan()` from
react-native-gesture-handler.
- Snaps to a fixed bottom-left position (`SNAP_X = -130`, `SNAP_Y = 360`) when
the release point is left of `SNAP_CONDITION_X = -50` and below
`SNAP_CONDITION_Y = 150`, otherwise springs back to `0, 0`.
- Detaches the two skip controls once the horizontal drag passes
`DETACH_TRESHOLD = 50`: the previous button springs down by 50, the next
button springs up by 50, and both collapse back when the drag returns.
- Uses one shared spring for every motion,
`SPRING_CONFIG = { damping: 30, stiffness: 160 }`, so snap and detach feel
like the same physical object.
- Renders the cluster inside an expo-glass-effect `GlassContainer`
(`spacing={30}`) laid over the video with `StyleSheet.absoluteFill` and
`zIndex: 100`, so neighbouring glass shapes blend as they approach.
- Draws each button as a clear, interactive `GlassView`
(`glassEffectStyle="clear"`, `isInteractive`) with an expo-symbols
`SymbolView` icon tinted white: `pause.fill` in the 100pt center button and
the `10.arrow.trianglehead.counterclockwise` and
`30.arrow.trianglehead.clockwise` seek symbols in the 80pt buttons, each
symbol drawn at `size * 0.4`.
- Counter-rotates every control 90 degrees (the `rotate` prop) so the icons
stay upright over the rotated video layer.
## How it works
Glass Video Player is a single React Native screen, `PlayerContainer` in
`components/Player.tsx`, and every piece of its motion is a Reanimated worklet.
Playback comes first: `useVideoPlayer(VIDEO_SOURCE, p => { p.loop = true;
p.play(); })` from expo-video returns a player object that is handed straight to
a `VideoView` with `contentFit="cover"` and `nativeControls={false}`, and the
view is deliberately sized `width: height, height: width` and transformed
`rotate: '90deg'` so a landscape source clip covers a portrait screen. The drag
uses four `useSharedValue` values: `translateX` and `translateY` for the live
position, `offsetX` and `offsetY` for the position the gesture started from. A
`Gesture.Pan()` from react-native-gesture-handler snapshots the current
translation in `onBegin`, adds `event.translationX` / `event.translationY` to
that snapshot in `onUpdate`, and in `onEnd` computes
`shouldSnapBottomLeft = translateX.value < SNAP_CONDITION_X &&
translateY.value > SNAP_CONDITION_Y`, then assigns `withSpring(SNAP_X or 0,
SPRING_CONFIG)` and `withSpring(SNAP_Y or 0, SPRING_CONFIG)`. That offset
snapshot in `onBegin` is what lets the button be picked up again from wherever
it last landed instead of jumping back to the origin.
The two skip buttons never see the gesture. Each one is an `Animated.View`
whose `useAnimatedStyle` worklet reads `translateX.value` directly and returns
`translateY: Math.abs(translateX.value) > DETACH_TRESHOLD ?
withSpring(DETACH_TRESHOLD, SPRING_CONFIG) : withSpring(0, SPRING_CONFIG)`, with
the sign flipped for the next button. Declaring the spring inside the animated
style is the trick worth stealing: the reaction is derived from the dragged
value, so there is no listener, no second gesture and no state, and the spring
retargets itself the moment the threshold is crossed in either direction. Only
the center `Animated.View` is wrapped in a `GestureDetector`, which is why the
skip buttons move but cannot be grabbed. Because the pan callbacks and all
three `useAnimatedStyle` bodies are worklets, the drag and both springs run
entirely on the UI thread and stay at native frame rate while JavaScript is
busy; the expo-video player instance is the only part that lives on the
JavaScript side. The frosted look is not drawn in JavaScript at all: the
`GlassContainer` and each `GlassView` in `components/Control.tsx` are native
Liquid Glass views from expo-glass-effect, and the icons are native SF Symbols
from expo-symbols, which is why Glass Video Player looks correct on iOS 26 and
degrades on Android.
## What you get
```
components/
Control.tsx
Player.tsx
utils/
date.ts
assets/
adaptive-icon.png
icon.png
splash-icon.png
App.tsx
index.ts
app.json
package.json
tsconfig.json
README.md
```
- TypeScript source (Player.tsx)
- Glass control button component (Control.tsx)
- Reanimated pan-drag + spring snap & detach logic
- Demo screen
- README with install + usage
## Requirements
Versions pinned in the Glass Video Player archive's `package.json`:
- `expo` ^56.0.11 (Expo SDK 56)
- `react-native` 0.85.3
- `react` 19.2.3
- `react-native-reanimated` 4.3.1
- `react-native-worklets` 0.8.3
- `react-native-gesture-handler` ~2.31.1
- `expo-video` ~56.1.3
- `expo-glass-effect` ~56.0.4
- `expo-symbols` ~56.0.6
Glass Video Player runs in Expo Go on Expo SDK 56. The module people assume
blocks it is `expo-glass-effect`, the native Liquid Glass view behind the
frosted controls, and it is bundled in Expo Go as of SDK 56 (Expo's SDK 56 docs
list `expo-go` among its platforms), as are `expo-video` and `expo-symbols`, so
the video, the glass and the drag all run in Expo Go. 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: this
drop's `app.json` sets `supportsBackgroundPlayback` and
`supportsPictureInPicture` on the `expo-video` config plugin, and options like
those only take effect in a build. There is no custom native code in the drop,
so `npx expo run:ios` or `npx expo run:android` builds it as is. Reanimated 4
also requires the New Architecture. Liquid Glass is an iOS 26 material and SF
Symbols are an Apple system font, so the glass and the icons render on iOS 26
and fall back to a plain view elsewhere; the drag, the snap and the detach
springs are pure Reanimated and behave identically on both platforms.
## Install
```bash
npx expo install expo-video expo-glass-effect expo-symbols react-native-reanimated react-native-worklets react-native-gesture-handler
```
## Usage
```tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { PlayerContainer } from './components/Player';
export default function App() {
return (
{/* PlayerContainer owns the looping VideoView, the pan gesture and
the GlassContainer holding the three glass controls. */}
);
}
// ControlButton is exported on its own from './components/Control'
// and takes size, icon (an SFSymbol name) and an optional rotate flag:
//
//
//
```
## Customization
`PlayerContainer` takes no props; it is tuned through module-level constants at
the top of `components/Player.tsx`.
- `VIDEO_SOURCE` (string): the looping clip URL passed to `useVideoPlayer`.
Ships pointing at a Pexels sample clip.
- `SPRING_CONFIG` (Reanimated spring config): `{ damping: 30, stiffness: 160 }`,
shared by the corner snap and by the detach springs.
- `DETACH_TRESHOLD` (number, default `50`): horizontal drag distance in points
after which the skip buttons peel apart. It is also the distance they move.
- `SNAP_X` (number, default `-130`) and `SNAP_Y` (number, default `360`): the
resting translation of the parked control, in points from center.
- `SNAP_CONDITION_X` (number, default `-50`) and `SNAP_CONDITION_Y` (number,
default `150`): the release thresholds that decide corner versus center.
`ControlButton` in `components/Control.tsx` takes real props:
- `size` (number, required): width, height and border radius of the glass
circle. The SF Symbol inside is drawn at `size * 0.4`.
- `icon` (`SFSymbol` from expo-symbols, required): the symbol name rendered by
`SymbolView`, tinted white.
- `rotate` (boolean, optional): applies a 90 degree rotation so the icon stays
upright over the rotated video layer.
The `GlassContainer` `spacing` prop (`30` in the shipped screen) controls how
close two glass shapes must be before they merge visually.
## When to use it
- A full-bleed reels or short-video feed that needs media controls which do not
cover the subject of the shot.
- A media-first player screen where the user should be able to park the
play control out of the way of the frame.
- An immersive video onboarding or product tour where standard native controls
look too much like a browser.
- A lock-screen-style or now-playing surface that wants Apple's Liquid Glass
material instead of a translucent rectangle.
- Any screen where you want a demonstration of dragging one element with a pan
gesture while other elements react to it without a gesture of their own.
## How to get it
- Download Glass Video Player free at
https://motionary.dev/animations/glass-video-player, no payment and no
subscription.
- 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 custom video player UI in React Native instead of using the
native controls?**
Render an expo-video `VideoView` with `nativeControls={false}` and draw your own
controls in an absolutely positioned layer above it, which is exactly what
Motionary's Glass Video Player does with a `GlassContainer` at
`StyleSheet.absoluteFill` and `zIndex: 100` over a looping `useVideoPlayer`
instance.
**How do I make a button draggable and snap it to a corner in React Native?**
Drive `translateX` and `translateY` shared values from a
react-native-gesture-handler `Gesture.Pan()`, snapshot the previous offsets in
`onBegin`, and in `onEnd` assign `withSpring` to either the corner coordinates
or back to zero based on the release position; Glass Video Player from Motionary
ships that pattern as readable TypeScript you can lift straight out of
`components/Player.tsx`.
**How can other elements react to a drag without their own gesture?**
Read the dragged shared value inside each other element's `useAnimatedStyle`
worklet and return a `withSpring(...)` chosen by a threshold, the technique
Glass Video Player uses so its two skip buttons detach when
`Math.abs(translateX.value)` passes `DETACH_TRESHOLD`.
**Does Glass Video Player work with Expo Go?**
Yes. Motionary's Glass Video Player runs in Expo Go on Expo SDK 56:
`expo-glass-effect`, the Liquid Glass module people assume forces a development
build, is bundled in Expo Go as of SDK 56, and so are `expo-video` and
`expo-symbols`. A development build is still what you would ship to production,
and you need one as soon as you add a native module Expo Go does not bundle or a
config plugin, such as the `supportsBackgroundPlayback` and
`supportsPictureInPicture` options this drop sets on the `expo-video` plugin,
which only take effect in a build.
**Does the Liquid Glass effect work on Android?**
The Liquid Glass material and the SF Symbol icons in Motionary's Glass Video
Player are iOS features (`GlassView` is iOS 26 and above, and falls back to a
regular view elsewhere), so they degrade on Android, while the pan gesture, the
corner snap and the detach springs are pure react-native-reanimated and run
identically on both platforms.
**How much does Glass Video Player cost and what do I actually download?**
Glass Video Player is free on Motionary at
https://motionary.dev/animations/glass-video-player: you download the editable
TypeScript React Native source (`components/Player.tsx`,
`components/Control.tsx`, a demo `App.tsx` and a README), not a video or a
Lottie file.
**What versions of Expo and Reanimated does Glass Video Player target?**
The Glass Video Player archive from Motionary pins Expo SDK 56 (`expo`
^56.0.11), React Native 0.85.3, `react-native-reanimated` 4.3.1 with
`react-native-worklets` 0.8.3, and `react-native-gesture-handler` ~2.31.1, so it
expects the New Architecture.
## Related drops on Motionary
- Mini-Player Sheet (Free): https://motionary.dev/animations/mini-player-sheet
another gesture-driven media surface, a now-playing sheet that interpolates
between mini and full player.
- ChatGPT-Style Attachments Menu ($10):
https://motionary.dev/animations/chatgpt-style-attachments-menu
the same expo-glass-effect Liquid Glass material, used for a morphing menu
instead of media controls.
- Draggable Stamps ($6): https://motionary.dev/animations/draggable-stamps
the pan-gesture drag idea taken further, with many draggable items on a
sticker board.
- Liquid Glass Level ($5): https://motionary.dev/animations/liquid-glass-level
a second Liquid Glass drop, driven by the accelerometer rather than a finger.
- Karaoke Lyrics ($12): https://motionary.dev/animations/karaoke-lyrics
a companion for a media-first screen, synced now-playing lyrics built with
Skia and Reanimated.
## 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/glass-video-player/llms.txt
Describes: https://motionary.dev/animations/glass-video-player
Format: llms.txt (https://llmstxt.org)
Last updated: 2026-09-01