# Pool Water Shader: React Native animated pool water background > Pool Water Shader is a React Native and Expo component that fills the > screen with live swimming-pool water: an SkSL fragment shader running in > @shopify/react-native-skia refracts a pool photo with 16 summed sine waves, > adds drifting caustic highlights, and floats a rubber duck whose shadow > ripples with the tiles, all driven on the UI thread by > react-native-reanimated. Sold by Motionary (https://motionary.dev). Product page: https://motionary.dev/animations/pool-water-shader Price: $12 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, shaders, react native Rarity: legendary Sold by: Motionary (https://motionary.dev) ## What it is Pool Water Shader is a full-screen animated swimming-pool background for React Native and Expo apps. You see sunlit water: bright caustic light patterns drifting across the surface, the tiled pool floor bending and wobbling under refraction, and a rubber duck bobbing and slowly wandering across the frame with a soft elliptical shadow on the floor beneath it. It is ambient, not gestural: nothing needs to be tapped or dragged, the scene runs on a clock, so it works as a living background behind a splash screen, an onboarding step or an empty state. The water is not a video or a Lottie file, it is a real Skia runtime shader compiled at startup, so it fills any screen size and any orientation without resampling artifacts. ## What it does - Renders the whole scene as one Skia `RuntimeEffect` (SkSL) painted with a `Fill` across a full-screen `Canvas`. - Sums 16 sine waves with pseudo-random directions and frequencies into two crossed wave fields, then blends them into a single caustics mask. - Uses a scaled-down copy of the same wave fields as a refraction offset that displaces the sampling coordinate, so the pool tiles bend under the water. - Adds the caustic highlights back additively, so the shimmer reads as light on the surface rather than a texture overlay. - Floats a rubber duck sprite whose wander path is a sum of sines at unrelated frequencies, so the path never visibly repeats. - Adds a swell, a small surface chop and a tilt that rides the swell, and clamps the duck to a 12 px inset so it never leaves the screen. - Paints the duck's shadow inside the shader, sampled at the same displaced coordinate as the floor, so the shadow ripples in lockstep with the tiles instead of sitting flat on top of them. - Exposes plain top-level switches, including `SHOW_CAUSTICS`, `SHOW_WAVES` and `SHOW_DUCK`, so you can run waves without shimmer, shimmer without waves, or the water with no duck at all. - Renders an empty tinted `View` (background `#0a3a4a`) while the pool photo is still decoding, so there is no flash of white. - Runs identically on iOS and Android, because all the per-pixel work stays on the GPU. ## How it works Pool Water Shader lives in `components/Container.tsx` in the Motionary drop, and everything visual happens in one SkSL fragment shader compiled once with `Skia.RuntimeEffect.Make(SKSL)` inside a `useMemo`. The shader is a port of a Shadertoy GLSL caustics shader: `texture(iChannel0, uv)` becomes `image.eval(coord)`, and because the child `ImageShader` is fitted to the canvas (`fit="cover"`, `tx="repeat"`, `ty="repeat"`), that sampling happens in canvas pixel space, so the normalised distortion is converted back to pixels with `iResolution.x`. Inside the shader, `makeWaves` loops 16 times, hashing each index through `randomVal` and `randomVec2` into a stable direction vector, and sums `smoothstep`-folded sine waves along those directions; two of those fields (one drifting with time, one against it at 0.8 speed) are folded with `smoothstep` into the caustics mask, and a small copy of the same result becomes `displace`, added to `fragCoord` before the pool image is sampled. The duck's shadow is a soft ellipse computed from `(sampleCoord - shadowCenter) / shadowRadius` and mixed into the floor colour at that same displaced coordinate, which is why the shadow ripples instead of floating; `shadowSoftness` feeds a clamped `smoothstep` edge so the feather can never go degenerate. On the JavaScript side, a Skia `useClock()` SharedValue drives two `useDerivedValue` worklets from `react-native-reanimated`: `duckPos` computes the duck's x, y and rotation from a sum of sines, and `uniforms` rebuilds the uniform object each frame (`iTime`, `iResolution`, `causticsStrength`, `waveStrength`, `waveScale`, `shadowCenter`, `shadowRadius`, `shadowSoftness`, `shadowColor`, `shadowOpacity`). Both worklets run on the UI thread, so no frame of the animation crosses the React bridge or re-renders a component. The duck sprite itself is a Skia `` inside a `` whose `transform` is a third `useDerivedValue` reading the same `duckPos`, so sprite and shadow can never drift out of sync, and the duck is drawn centred on the origin so the tilt rotates about its own centre. Screen size comes from `useWindowDimensions()` and is a dependency of the worklets, so rotation and split-view resizes are handled without remounting the canvas. ## What you get ``` components/ Container.tsx assets/ pool.png duck2.png icon.png adaptive-icon.png splash-icon.png App.tsx index.ts app.json babel.config.js package.json package-lock.json tsconfig.json README.md ``` - TypeScript source with the inline SkSL shader - Pool floor and rubber duck image assets - Tunable controls (waves, caustics, speed, duck, shadow) - Demo screen - README with install and usage ## Requirements Pool Water Shader is built and tested against these exact versions, pinned in the drop's `package.json`: - expo ~56.0.11 (Expo SDK 56) - react-native 0.85.3 - react 19.2.3 - @shopify/react-native-skia 2.6.2 - react-native-reanimated 4.3.1 - react-native-worklets 0.8.3 - react-native-gesture-handler ~2.31.1 - react-native-safe-area-context ~5.7.0 - expo-status-bar ~56.0.4 - TypeScript ~6.0.3 (dev dependency) Pool Water Shader runs in Expo Go on Expo SDK 56. The module people assume blocks it is `@shopify/react-native-skia`, but Skia is bundled in Expo Go as of SDK 56, so `npx expo start` and the QR code are enough to see the water running on a device. A development build (`npx expo run:ios` or `npx expo run:android`) 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 of your own. Reanimated 4 requires `react-native-worklets` and the new architecture; the Babel config uses `babel-preset-expo`, which adds the worklets plugin automatically when the package is installed. Both iOS and Android are supported. ## Install ```bash npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets ``` Those are the packages `components/Container.tsx` itself imports (Reanimated 4 needs `react-native-worklets` alongside it). The demo `App.tsx` in the drop also wraps the scene in `react-native-gesture-handler`, `react-native-safe-area-context` and `expo-status-bar`, all pinned in the drop's `package.json`; install those too if you keep that wrapper. ## Usage ```tsx // App.tsx // Container takes no props: the look is tuned with the constants at the // top of components/Container.tsx (SHOW_CAUSTICS, WAVE_STRENGTH, SPEED, ...). import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StatusBar } from 'expo-status-bar'; import { Container } from './components/Container'; export default function App() { return ( ); } ``` ## Customization The `Container` component of Motionary's Pool Water Shader takes no props. It is tuned with typed constants at the top of `components/Container.tsx`, each of which feeds a shader uniform: - `SHOW_CAUSTICS` (boolean, default `true`): the bright shimmer drifting on the surface; `false` sets `causticsStrength` to 0. - `SHOW_WAVES` (boolean, default `true`): the refraction that bends the tiles; `false` sets `waveStrength` to 0 for a flat floor. - `CAUSTICS_STRENGTH` (number, default `0.15`): shimmer brightness, the source suggests 0.3 to 1.2 for a stronger look. - `WAVE_STRENGTH` (number, default `1.5`): ripple distortion amount, suggested range 0 to 2.5. - `WAVE_SIZE` (number, default `2.0`): wave scale, above 1 is bigger swells, below 1 is tighter chop (it becomes the `waveScale` uniform as `150 / WAVE_SIZE`). - `SPEED` (number, default `0.8`): overall animation speed, suggested 0.3 to 3. - `SHOW_DUCK` (boolean, default `true`): draw the rubber duck sprite. - `DUCK_SCALE` (number, default `0.2`): duck width as a fraction of screen width, suggested 0.15 to 0.4. - `DUCK_SPEED` (number, default `1.5`): how fast the duck drifts around, suggested 0.3 to 2. - `SHOW_SHADOW` (boolean, default `true`): draw the duck's shadow on the pool floor. - `SHADOW_OPACITY` (number, default `0.5`): 0 is none, 1 is black. - `SHADOW_SCALE` (number, default `0.9`): shadow size relative to the duck, suggested 0.5 to 1.6. - `SHADOW_SOFTNESS` (number, default `0.6`): edge feather, 0 is crisp, 1 is very soft, keep it above 0. - `SHADOW_COLOR` (number triple, default `[0.03, 0.11, 0.16]`): rgb from 0 to 1, the colour the floor is tinted toward. - `SHADOW_OFFSET` (number pair, default `[-40, 40]`): pixel offset from the duck, which sets the fake light angle. Swapping `assets/pool.png` for another photo changes the floor without touching the shader, since the image is passed in as an `ImageShader` child. ## When to use it - A splash or launch screen that has to look alive while assets load. - An onboarding or welcome screen that needs a calm animated backdrop behind a headline and a call to action. - An empty state (no messages, no saved items) that would otherwise be a blank screen. - A login or paywall background for a travel, holiday, pool, spa or summer product. - A playful 404 or offline screen where a floating rubber duck fits the tone. - A live wallpaper style background in a relaxation, sleep or meditation app. ## How to get it - Buy Pool Water Shader on its own at https://motionary.dev/animations/pool-water-shader, $12 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 water shader in React Native?** Motionary's Pool Water Shader does it with a single SkSL fragment shader run through `Skia.RuntimeEffect.Make` from `@shopify/react-native-skia`: the shader sums 16 sine waves into a caustics mask, reuses a scaled copy of them as a refraction offset on the sampling coordinate, and reads the pool photo through an `ImageShader` child. Time uniforms come from Skia's `useClock` through a `react-native-reanimated` `useDerivedValue` worklet, so every frame is computed on the UI thread and rendered on the GPU. **Does Pool Water Shader work with Expo Go?** Yes. The only native dependencies of Motionary's Pool Water Shader are `@shopify/react-native-skia` and `react-native-reanimated`, and both are bundled in Expo Go on Expo SDK 56, so `npx expo start` runs the drop with no custom build. A development build (`npx expo run:ios` or `npx expo run:android`) is still what you would ship to production, and what you need the moment you add a native module Expo Go does not bundle or a config plugin of your own. **What is a good animated background for a React Native app?** Pool Water Shader from Motionary is built exactly for that: it is a full-screen ambient scene with no gestures and no state to manage, so you drop `` behind your content on a splash screen, onboarding step or empty state. Because it is a GPU shader rather than a video loop, it fills any screen size and never shows a seam when the loop restarts. **Is there a Skia SkSL shader example for React Native I can read?** Pool Water Shader ships a commented, production SkSL shader inside `components/Container.tsx`, including the notes on porting a Shadertoy GLSL shader to Skia (`texture(iChannel0, uv)` becomes `image.eval(coord)`, and coordinates are in canvas pixel space). It is one of the clearest end to end SkSL plus Reanimated uniform examples Motionary sells. **Does it run on Android as well as iOS?** Yes. Pool Water Shader was tested on Expo SDK 56 with the new architecture (react-native-skia 2.6, react-native-reanimated 4 and react-native-worklets) on both iOS and Android, and all the per-pixel work stays on the GPU, so performance is comparable on both platforms. **How much does Pool Water Shader cost and what do I actually download?** Pool Water Shader is $12 USD on Motionary at https://motionary.dev/animations/pool-water-shader (price as of 2026-09-01), or it is included in the $79 USD Motionary lifetime pass. You download the editable TypeScript source (`components/Container.tsx`, `App.tsx`, the pool and duck image assets and a README), not a video or a design file. **Can I use it in a commercial app?** Yes. The Motionary license for Pool Water Shader covers a single developer building unlimited personal and commercial apps; you may not resell or redistribute the source itself. Full terms: https://motionary.dev/license ## Related drops on Motionary - Glass Magnifier ($10): https://motionary.dev/animations/glass-magnifier another SkSL refraction shader, it distorts what is underneath it with a lens instead of with waves. - Zipper Curtain ($12): https://motionary.dev/animations/zipper-curtain a full-screen Skia shader transition, the natural next step if you use Pool Water Shader on a splash screen. - Apple weather rain ($12): https://motionary.dev/animations/apple-weather-rain the other full-screen Skia weather background in the catalog, built with a particle atlas rather than a fragment shader. - Freeze Card ($9): https://motionary.dev/animations/freeze-card a Skia shader applied to a surface (frost spreading over a bank card), a smaller scale use of the same SkSL technique. - Voice Orb (Free): https://motionary.dev/animations/voice-orb a free Skia plus Reanimated drop, a no-cost way to check that Skia renders on your device before buying a paid shader. ## 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/pool-water-shader/llms.txt Describes: https://motionary.dev/animations/pool-water-shader Format: llms.txt (https://llmstxt.org) Last updated: 2026-09-01 ```