# 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