# Island Genie: React Native Dynamic Island genie effect
> Island Genie is a React Native and Expo component that warps a photo with a
> macOS-style genie effect and sucks it up under the phone's Dynamic Island,
> using a React Native Skia textured triangle mesh whose vertices are
> recomputed in a Reanimated worklet on the UI thread. Island Genie is a drop
> sold by Motionary (https://motionary.dev).
Product page: https://motionary.dev/animations/island-genie
Price: $11 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 skia, react native gesture handler, react native
Rarity: legendary
Sold by: Motionary (https://motionary.dev)
## What it is
Island Genie is a React Native screen where a rounded photo sits in the middle
of a white canvas and, on tap, stretches into a narrowing funnel that pours
itself up into the Dynamic Island at the top of the phone. The shape is the
macOS Dock genie effect: the top edge necks in and tucks under the island
first, the body follows, and the bottom edge is the last thing to disappear.
As the content slides under, the island itself swells slightly and settles
back, the way a real Dynamic Island reacts to an incoming activity. A tap on
the photo toggles the animation both ways, and a config bottom sheet holds
three sliders (progress, target size, neck) so the warp can be scrubbed frame
by frame by hand.
## What it does
- Warps a baked photo texture through a 14 by 48 quad grid (735 vertices,
1,344 triangles) drawn as a single Skia `Vertices` node.
- Rebuilds every vertex position inside one Reanimated `useDerivedValue`
worklet, so the whole warp runs on the UI thread with no JS bridge traffic
per frame.
- Tap the photo to toggle: `withTiming` to 1 over 900 ms on the way up
(`Easing.inOut(Easing.cubic)`), 700 ms on the way back.
- Sequences the squeeze with two clocks from one progress value: a quadratic
clock (`p * p`) drives the neck front so it leads, a cubic clock
(`p * p * p`) drives the body slide so the bottom edge arrives last.
- Sweeps a sigmoid "neck" down the image (`1 / (1 + exp(-(front - v) * k))`),
necked rows shrink toward the slot width and recentre under the island,
un-necked rows stay full width.
- Clamps each row so it can never travel above its own slot target, so nothing
pokes out above the notch mid-animation.
- Bumps the Dynamic Island with a raised-cosine swell over the window
[0.45, 1] of progress, peaking near p = 0.72: width up to +13 percent,
height up to +8 percent, zero slope at both ends.
- Draws the island as a Skia `RoundedRect` last in the scene graph, so the
warped photo genuinely disappears behind it instead of being masked.
- Bakes the source photo once into a rounded 900 by 990 texture so the rounded
corners warp with the mesh instead of fighting it.
- Ships a config sheet (`@lodev09/react-native-true-sheet`) with three custom
Reanimated and Gesture Handler sliders, send / back / reset buttons, and a
numeric readout that updates on the UI thread.
- Two floating action buttons: a bring-back FAB bottom left (the 700 ms
return) and a gear FAB bottom right that presents the config sheet; the
sheet's own reset button snaps back in 260 ms.
- Lays out the island and the photo against `useSafeAreaInsets()`, so the fake
island aligns with the real one on notched devices.
## How it works
Island Genie is a forward-warped textured mesh, not a shader and not a
transform. First, `components/genie/createRoundedImage.ts` bakes the source
photo once: it center-crops `assets/rail.jpg` to the target aspect ratio,
clips a rounded rect with `canvas.clipRRect` inside a `createPicture` pass, and
rasterizes the result with `drawAsImageFromPicture` at 3x, so the rounded
corners live in the texture and deform with the mesh. Then
`components/genie/SqueezeGenie.tsx` lays a `COLS x ROWS` grid (14 by 48) over
the on-screen rect as a Skia `Vertices` node in `mode="triangles"`, filled by
an `ImageShader` with `tx="clamp"`, `ty="clamp"` and `fit="none"`. The texture
coordinates and the triangle index buffer are static and built once in
`useMemo`; only the vertex positions animate. Those positions are recomputed
inside a Reanimated `useDerivedValue` worklet that reads three shared values
(`progress`, `targetSize`, `neck`), so the entire per-frame loop stays on the
UI thread and React never re-renders during the animation. For each of the 49
rows the worklet evaluates a logistic function to get `narrow`, the sigmoid
neck factor, blends the row's horizontal scale from full width toward the slot
width, blends its center from the photo's center toward the island's center,
and blends its Y between the sliding original position and its stacked position
inside the hidden slot, with a hard clamp so it never overshoots upward. The
two eased clocks (`pFront = p * p` for the neck front, `pSlide = p * p * p` for
the body) are what make the motion read as a genie rather than a scale down.
Finally a second `useDerivedValue` computes `bump`, a raised-cosine hump that
feeds four more derived values driving the island `RoundedRect`'s x, width,
height and corner radius; because the island is the last child of the `Canvas`,
the mesh passes behind it. On the JS side the only work is a transparent
`Pressable` over the photo calling `withTiming`, plus the sliders in
`components/genie/Slider.tsx`, which write directly into the same shared values
from a `Gesture.Pan()` and display their value through
`components/genie/AnimatedText.tsx` (a `TextInput` driven by `useAnimatedProps`
so the number formats inside a worklet instead of bouncing to JS every frame).
## What you get
```
App.tsx
index.ts
app.json
babel.config.js
tsconfig.json
package.json
package-lock.json
README.md
.gitignore
components/
Container.tsx
genie/
SqueezeGenie.tsx
createRoundedImage.ts
createCardImage.ts
Slider.tsx
AnimatedText.tsx
assets/
rail.jpg
icon.png
adaptive-icon.png
splash-icon.png
```
- TypeScript source (`SqueezeGenie.tsx`)
- Rounded-image texture baker (`createRoundedImage.ts`)
- Custom Reanimated and Gesture Handler slider (`Slider.tsx`)
- UI-thread numeric readout (`AnimatedText.tsx`)
- Config bottom sheet with gear and reset FABs
- README with install and usage
- A second texture baker (`createCardImage.ts`) that rasterizes a dialog card
in Skia, included as an alternative texture source for the same mesh
## Requirements
Island Genie is built and tested on Expo SDK 56, iOS and Android. The versions
pinned in the drop's `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
- `@shopify/react-native-skia` 2.6.2
- `react-native-gesture-handler` ~2.31.1
- `react-native-safe-area-context` ~5.7.0
- `@lodev09/react-native-true-sheet` ^3.10.1
- `@expo/vector-icons` ^15.0.3
- `expo-status-bar` ~56.0.4
- `typescript` ~6.0.3 (dev dependency)
Island Genie needs a development build, not Expo Go: `@shopify/react-native-skia`
draws the mesh and `@lodev09/react-native-true-sheet` presents the config sheet,
and both are native modules. Reanimated 4 also requires the New Architecture
(Fabric). Run it with `npx expo run:ios` or `npx expo run:android`. The Babel
config is plain `babel-preset-expo`, which adds the `react-native-worklets`
plugin automatically when the package is installed.
## Install
```bash
npx expo install @shopify/react-native-skia react-native-reanimated \
react-native-worklets react-native-gesture-handler \
react-native-safe-area-context @lodev09/react-native-true-sheet \
@expo/vector-icons expo-status-bar
npx expo run:ios
```
## Usage
Island Genie ships as `SqueezeGenie`, a self-contained full-screen scene. It
takes no props: mount it under a `SafeAreaProvider` and a
`GestureHandlerRootView`.
```tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { SqueezeGenie } from './components/genie/SqueezeGenie';
export default function App() {
return (
);
}
```
To swap the photo, replace `assets/rail.jpg` and keep the
`createRoundedImage(rail, RAIL_DESIGN_W, RAIL_DESIGN_H, RAIL_RADIUS, SCALE)`
call in `SqueezeGenie.tsx`, which returns the baked `SkImage` used as the mesh
texture.
## Customization
`SqueezeGenie` exposes no props. Everything is tuned through module constants
and shared values inside `components/genie/SqueezeGenie.tsx`, all of them live
and all of them editable:
- `COLS` (number, default `14`) and `ROWS` (number, default `48`): mesh
resolution. More rows than columns because the genie warp is mostly vertical
and the neck needs rows to bend smoothly.
- `progress` (shared value, starts `0`): 0 is resting, 1 is fully tucked under
the island. Driven by tap or by the `progress` slider (range 0 to 1).
- `targetSize` (shared value, starts `0.32`): squeezed width as a fraction of
the photo width. Slider range 0.12 to 0.6, capped so the content always hides
behind the island.
- `neck` (shared value, starts `11`): sharpness of the sigmoid neck. Slider
range 10 to 20; higher is a tighter, more pinched funnel.
- `ISLAND_W` (default `128`) and `ISLAND_H` (default `37`): the fake Dynamic
Island's resting size in points.
- `RAIL_DESIGN_W` (`300`), `RAIL_DESIGN_H` (`330`), `RAIL_RADIUS` (`30`) and
`SCALE` (`3`): the baked texture's design size, corner radius and
supersampling factor.
- Timing: `sendUp` is `withTiming(1, { duration: 900, easing:
Easing.inOut(Easing.cubic) })`, `bringBack` is 700 ms, `reset` is 260 ms.
- `createRoundedImage(source, w, h, radius, scale = 3)` in
`createRoundedImage.ts` returns `SkImage | null` and center-crops the source
so a photo of any aspect ratio is never distorted.
- `Slider` props in `Slider.tsx`: `label` (string), `value`
(`SharedValue`), `min` (default `0`), `max` (default `1`),
`decimals` (default `2`).
## When to use it
- A media-to-notch transition: a photo, story or attachment that visibly gets
filed away into the Dynamic Island when the user sends or dismisses it.
- A Dynamic Island demo screen for a Live Activity feature, showing where the
content goes before the activity appears.
- Send or upload confirmation, where the item being sent flies into the island
instead of just fading out.
- A dismiss gesture for a full-screen photo viewer or card that should feel
physical rather than a plain scale down.
- An onboarding or landing moment in a portfolio or launch app that needs one
memorable, unmistakably iOS-native flourish.
- A reference implementation for any Skia mesh warp in React Native: the same
`Vertices` plus `useDerivedValue` pattern retargets to ripples, page curls or
squash and stretch.
## How to get it
- Buy Island Genie on its own at
https://motionary.dev/animations/island-genie, $11 USD, instant download,
secure Stripe checkout, no subscription and no account renewal.
- Or get Island Genie 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 genie effect in React Native?**
Island Genie from Motionary does it with a React Native Skia `Vertices` mesh:
lay a grid over a baked image texture, then recompute every vertex inside a
Reanimated `useDerivedValue` worklet, blending each row's width and center
toward a target slot with a sigmoid neck factor. That is a forward mesh warp,
which is why it bends like the macOS Dock genie instead of just scaling.
**Does Island Genie work with Expo Go?**
No. Island Genie needs an Expo development build because it depends on
`@shopify/react-native-skia` for the mesh and
`@lodev09/react-native-true-sheet` for the config sheet, both native modules.
Run `npx expo run:ios` or `npx expo run:android`. It is built on Expo SDK 56
with the New Architecture (Fabric), which Reanimated 4 requires.
**Does the Island Genie animation run on both iOS and Android?**
Yes. Island Genie from Motionary is tested on iOS and Android with Expo SDK 56.
The Dynamic Island in Island Genie is drawn by the component itself as a Skia
`RoundedRect` positioned from `useSafeAreaInsets()`, so it renders on Android
too and lines up with the real hardware island on notched iPhones.
**Is the Island Genie notch animation the real iOS Dynamic Island API?**
No, and that is deliberate. Island Genie renders its own island inside the Skia
canvas, so the warped content can pass behind it and the island can swell in
sync with the warp. For a real Live Activity, pair Island Genie with the
Animated Live Activity drop on Motionary.
**Is the Skia warp effect fast enough for 60 fps?**
Yes. In Island Genie the mesh is 735 vertices and 1,344 triangles, and the
whole rebuild happens in one Reanimated worklet on the UI thread, so nothing
crosses to JavaScript per frame. Texture coordinates and the triangle index
buffer are computed once with `useMemo` and never change.
**How much does Island Genie cost and what do I get?**
Island Genie is $11 USD on Motionary at
https://motionary.dev/animations/island-genie (price as of 2026-09-01, live
price on the product page), or it is included in the Motionary lifetime pass at
$79 USD. You get the editable TypeScript source (`SqueezeGenie.tsx`, the
texture baker, the custom slider and the config sheet), delivered as an instant
download after Stripe checkout.
**Can I use my own image with Island Genie?**
Yes. Island Genie loads its photo with Skia's `useImage` and bakes it through
`createRoundedImage`, which center-crops any aspect ratio and bakes the rounded
corners into the texture, so swapping `assets/rail.jpg` is the only change
needed.
## Related drops on Motionary
- Genie Form ($11): https://motionary.dev/animations/genie-form
The same Skia genie warp applied to a form and a button rather than a photo
and a notch, with haptics.
- Dynamic Island Member Card ($6):
https://motionary.dev/animations/dynamic-island-member-card
A card dispensed out of the Dynamic Island, the reverse direction of the
Island Genie tuck.
- Dynamic Island Boarding Pass ($6):
https://motionary.dev/animations/dynamic-island-boarding-pass
Another Dynamic Island interaction, driven by a pan gesture and a tear-off.
- Zipper Curtain ($12): https://motionary.dev/animations/zipper-curtain
A Skia mesh transition for splash and reveal, same family of vertex warping.
- Animated Live Activity ($10):
https://motionary.dev/animations/animated-live-activity
The Live Activity and lock screen widget that pairs with an Island Genie
hand-off.
## 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/island-genie/llms.txt
Describes: https://motionary.dev/animations/island-genie
Format: llms.txt (https://llmstxt.org)
Last updated: 2026-09-01