diff --git a/README.md b/README.md index 5267525..893c897 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,38 @@ # react-native-effects -https://github.com/user-attachments/assets/5141208a-655a-4de8-94fb-3e66351bf36f +WebGPU shader effects for React Native. Drop in a component, or write your own effect in WGSL. Rendering runs on a separate JS runtime, so a busy JS thread does not stall the animation. -> **Experimental** — APIs may change without notice. Relies on `react-native-worklets` bundle mode, which is not enabled by default yet. +

+ The example app: the effects catalog, Lava Lamp, Liquid Chrome, Thanos, Bubble Wrap, Ferrofluid, Polaroid and CRT Off +

-WebGPU-powered effects running on **background thread** in React Native. +> **Experimental.** APIs may change without notice. The library relies on `react-native-worklets` bundle mode, which is off by default for now. -## Features - -- **WebGPU rendering** via `react-native-webgpu` -- **Off-thread rendering** using `react-native-worklets` bundle mode — the GPU render loop runs on a separate JS runtime, keeping the main thread free -- **Drop-in components** — use like any React Native `View` -- **Customizable** — control colors, speed, intensity, and effect-specific parameters -- **Animated & static** modes for gradients -- **Build your own** — create custom effects with `ShaderView` and WGSL shaders - -## Components - -| Component | Description | -| ------------------ | ---------------------------------------------- | -| `Iridescence` | Mesmerizing iridescent animated effect | -| `LiquidChrome` | Fluid metallic surface | -| `Silk` | Smooth flowing silk fabric | -| `Campfire` | Fire with drifting sparks and smoke | -| `CalicoSwirl` | Warped noise pattern with flowing colors | -| `Aurora` | Northern lights with flowing curtains of light | -| `LinearGradient` | Smooth linear gradients (static & animated) | -| `CircularGradient` | Customizable circular/radial gradients | - -## ShaderView - -`ShaderView` is the core building block that powers every effect in this library. It takes a WGSL fragment shader and renders it on a WebGPU canvas, handling the render loop, uniform buffer, and React Native view integration for you. +## Quick look ```tsx -import { ShaderView } from 'react-native-effects'; - -; -``` - -| Prop | Type | Default | Description | -| ---------------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------- | -| `fragmentShader` | `string` | — | WGSL fragment shader source | -| `colors` | `ColorInput[]` | `[]` | Up to 2 colors mapped to `u.color0` and `u.color1` | -| `params` | `number[]` | `[]` | Up to 8 floats mapped to `u.params0.xyzw` and `u.params1.xyzw` | -| `speed` | `number` | `1.0` | Animation speed multiplier | -| `isStatic` | `boolean` | `false` | Render once then stop the animation loop | -| `transparent` | `boolean` | `false` | Clear the canvas to alpha `0` for an overlay-friendly transparent background | -| `paramsSynchronizable` | `ParamsSynchronizable` | — | Live 4-float input written into the dedicated `u.live` slot every frame (touch/scroll/audio). See below | - -All built-in effects (Silk, Aurora, Campfire, etc.) are thin wrappers around `ShaderView`. You can use it directly to create your own custom effects — see the [Custom Effects Guide](CUSTOM_EFFECTS.md) for a full walkthrough and a ready-to-use AI prompt. - -### Live input with `paramsSynchronizable` - -Static `params` are great for values that change on the JS thread occasionally, but the render loop runs off-thread — so feeding it fast, per-frame input (a finger drag, scroll progress, audio level) through React props would be laggy. `paramsSynchronizable` is the bridge: a 4-float [Synchronizable](https://docs.swmansion.com/react-native-worklets/docs/synchronization/synchronizable) that the off-thread render loop reads every frame and writes into its own dedicated `u.live` slot — so it never collides with the static `params` (you keep all 8 _and_ get a live channel). +import { StyleSheet } from 'react-native'; +import { Aurora } from 'react-native-effects'; -Create one with the `useParamsSynchronizable` hook and update it from your gesture or scroll handlers: - -```tsx -import { ShaderView, useParamsSynchronizable } from 'react-native-effects'; - -function TouchReactive() { - // initial resting value, read once: (x, y, active, extra) - const { paramsSynchronizable, setParamsSynchronizable } = - useParamsSynchronizable([0.5, 0.5, 0, 0]); - - return ( - { - const { locationX, locationY } = e.nativeEvent; - setParamsSynchronizable(locationX, locationY, 1, 0); - }} - /> - ); +export function Background() { + return ; } ``` -Inside the shader, declare the `live` field on the `Uniforms` struct (right after `params1`) and read the live values from `u.live`: - -```wgsl -let pointer = u.live.xy; // (x, y) you wrote -let active = u.live.z; // 1 while touching, 0 otherwise -``` - -`setParamsSynchronizable(x, y, active, extra)` runs on the JS thread; the four floats are by convention `(x, y, active, extra)` for pointer input or `(progress, …)` for scroll-driven effects, but the meaning is entirely up to your shader. For a ready-made pan-gesture variant, use [`ShaderViewWithPanGesture`](src/components/ShaderViewWithPanGesture/index.tsx). +Effects accept standard `View` props, so they size and layer like any other view. ## Installation ```sh npm install react-native-effects +npm install react-native-webgpu react-native-worklets react-native-gesture-handler ``` -### Peer dependencies +Then complete the two steps below. The example app runs on React Native 0.88, Expo 58, `react-native-webgpu` 0.17 and `react-native-worklets` 0.13. -```sh -npm install react-native-webgpu react-native-worklets react-native-reanimated react-native-gesture-handler -``` +### Android API level -### Android requirements - -`react-native-webgpu` uses `AHardwareBuffer` APIs that require **Android API 26+**, so your app must set `minSdkVersion` to at least `26` (the default in many templates is `24`). In an Expo project, use [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/): +`react-native-webgpu` uses `AHardwareBuffer` APIs that require **Android API 26+**, and many templates default to 24. In a bare project, set `minSdkVersion = 26` in `android/build.gradle`. In Expo, use [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/): ```json { @@ -119,24 +44,9 @@ npm install react-native-webgpu react-native-worklets react-native-reanimated re } ``` -In a bare React Native project, set `minSdkVersion = 26` in `android/build.gradle`. - -### Bundle mode setup +### Bundle mode -This library relies on `react-native-worklets` [Bundle Mode](https://docs.swmansion.com/react-native-worklets/docs/bundleMode). You need to configure Metro, Babel, and package.json in your app. - -**package.json** (add at root level) - -```json -{ - "worklets": { - "staticFeatureFlags": { - "BUNDLE_MODE_ENABLED": true, - "FETCH_PREVIEW_ENABLED": true - } - } -} -``` +The library needs `react-native-worklets` [Bundle Mode](https://docs.swmansion.com/react-native-worklets/docs/bundleMode). Enable it in Babel and Metro:
Bare React Native @@ -155,7 +65,7 @@ module.exports = { **metro.config.js** ```js -const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); +const { getDefaultConfig } = require('@react-native/metro-config'); const { getBundleModeMetroConfig, } = require('react-native-worklets/bundleMode'); @@ -181,7 +91,7 @@ module.exports = config;
Expo -Install additional dev dependencies: +Install the additional dev dependencies: ```sh npm install -D babel-preset-expo @react-native/metro-config @@ -229,78 +139,107 @@ config.transformer = { module.exports = config; ``` -
- -## Usage +Release builds on Expo also need the `require.getModules` polyfill: copy [`metro-get-modules-polyfill.js`](example/metro-get-modules-polyfill.js) and register it the way [`example/metro.config.js`](example/metro.config.js) does. -```tsx -import { Iridescence, LiquidChrome, Aurora } from 'react-native-effects'; - -// Full-screen animated background - - -// Metallic effect with custom speed - - -// Aurora borealis with custom parameters - -``` +
-### Common Props +## Effects -All shader components accept standard `View` props plus: +The package exports eight ready-made components. Colors are `ColorInput` values: color strings or numbers. -| Prop | Type | Default | Description | -| ------- | ------------------ | ------- | -------------------------- | -| `color` | `string \| number` | varies | Base tint color | -| `speed` | `number` | `1.0` | Animation speed multiplier | +### Animated backgrounds -### Aurora +All of these take `color` and `speed` (a multiplier, default `1.0`), plus: -| Prop | Type | Default | Description | -| ----------- | -------- | ------- | ------------------------------ | -| `intensity` | `number` | `1.0` | Brightness of the aurora bands | -| `layers` | `number` | `3` | Number of curtain layers (1-5) | -| `waviness` | `number` | `1.0` | Turbulence of the curtains | +| Component | Looks like | Extra props | +| -------------- | ----------------------------------- | ---------------------------------------------- | +| `Aurora` | Northern lights | `intensity`, `layers` (1-5), `waviness` | +| `CalicoSwirl` | Warped noise with flowing colors | `intensity` | +| `Campfire` | Fire with drifting sparks and smoke | `sparkSize`, `fireIntensity`, `smokeIntensity` | +| `Iridescence` | Soap-film color | none | +| `LiquidChrome` | Fluid metallic surface | `amplitude`, `frequencyX`, `frequencyY` | +| `Silk` | Smooth flowing fabric | `scale`, `rotation`, `noiseIntensity` | ### Gradients ```tsx -import { LinearGradient, CircularGradient } from 'react-native-effects'; +import { CircularGradient, LinearGradient } from 'react-native-effects'; ``` -## Create Your Own Effect +| Component | Required props | Optional props | +| ------------------ | --------------------------------- | --------------------------------------------------- | +| `LinearGradient` | `startColor`, `endColor`, `angle` | `speed` in degrees per second, `0` is static | +| `CircularGradient` | `centerColor` | `edgeColor`, `centerX`, `centerY`, `sizeX`, `sizeY` | + +## Build your own + +Every effect above is a thin wrapper around `ShaderView`, which takes a WGSL fragment shader and handles the WebGPU canvas, render loop and uniform buffer. + +```tsx +import { ShaderView } from 'react-native-effects'; + +; +``` + +| Prop | Type | Default | Description | +| ---------------------- | ---------------------- | ------- | -------------------------------------------------------------------- | +| `fragmentShader` | `string` | none | WGSL fragment shader source | +| `colors` | `ColorInput[]` | `[]` | Up to 2 colors, mapped to `u.color0` and `u.color1` | +| `params` | `number[]` | `[]` | Up to 8 floats, mapped to `u.params0.xyzw` and `u.params1.xyzw` | +| `speed` | `number` | `1.0` | Animation speed multiplier | +| `isStatic` | `boolean` | `false` | Render once, then stop the animation loop | +| `transparent` | `boolean` | `false` | Clear to alpha `0`, for overlays | +| `texture` | `ImageSourcePropType` | none | Image the shader can sample, for effects on top of real photos | +| `paramsSynchronizable` | `ParamsSynchronizable` | none | Live floats written to `u.live` every frame, independent of `params` | + +### Live input + +React props are too slow for per-frame input such as a finger drag or an audio level. `ShaderViewWithPanGesture` feeds touch straight to the render loop: `u.live.xy` is the pointer in UV space and `u.live.z` is `1` while touching. + +```tsx +import { ShaderViewWithPanGesture } from 'react-native-effects'; + +; +``` + +For scroll, audio or any other source, create your own channel with `useParamsSynchronizable` and pass it as `paramsSynchronizable`. + +The [Custom Effects Guide](CUSTOM_EFFECTS.md) covers the uniform layout, the shader contract and live input in full, and includes a prompt you can paste into an AI assistant to generate an effect. -Want to build a custom shader effect? Check out the [Custom Effects Guide](CUSTOM_EFFECTS.md) — it includes everything you need to know about the `ShaderView` API, the uniform layout, and the WGSL shader contract. It also has a ready-to-use AI prompt you can paste into ChatGPT or Claude to generate a complete custom effect. +## Example app -## Running the example app +The clip above is the [example app](example), a catalog of 45 effects built with `ShaderView`. Most of them, including everything in the clip except Liquid Chrome, are demos in `example/src`. The package does not export them, so treat them as source to copy from. Many react to tilt, touch, sound or the magnetometer. ```sh yarn install -yarn prebuild # generates native iOS/Android projects -yarn ios # run on iOS simulator -yarn android # run on Android emulator +yarn example prebuild +yarn example ios # or: yarn example android ``` -To start fresh after config changes, use `yarn prebuild:clean` instead of `yarn prebuild`. +Use `yarn example prebuild:clean` to start fresh after config changes. The app's photo and software credits are in [docs/credits.md](docs/credits.md). ## Contributing diff --git a/docs/media/demo.webp b/docs/media/demo.webp new file mode 100644 index 0000000..905bf16 Binary files /dev/null and b/docs/media/demo.webp differ