@jayf0x/fluidity-js 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +84 -174
  2. package/dist/index.js +653 -629
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # fluidity-js — WebGPU Fluid Simulation for React
1
+ # fluidity-js — Upgrade your UX
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
@@ -7,105 +7,75 @@
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](./tsconfig.json)
8
8
  [![CI](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml/badge.svg)](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml)
9
9
 
10
- **Real-time Navier-Stokes fluid dynamics for React — interactive water, ink, glass, and aurora effects on text and images. WebGPU-first with automatic WebGL2/WebGL1 fallback. Runs in a Web Worker for zero jank.**
11
-
12
10
  <a href="https://jayf0x.github.io/fluidity">
13
11
  <p align="center">
14
- <img src="assets/preview.gif" alt="WebGPU fluid simulation — fluid text and image effects in React" height="300px"/>
12
+ <img src="assets/preview.gif" alt="Fluid text and image effects in React" height="300px"/>
15
13
  </p>
16
- <p align="center"><strong>Live demo →</strong></p>
14
+ <p align="center"><strong>Demo & Examples →</strong></p>
17
15
  </a>
18
16
 
17
+ ## Quickstart
18
+
19
19
  ```bash
20
- npm install @jayf0x/fluidity-js
21
- # bun add / yarn add / pnpm add
20
+ bun add @jayf0x/fluidity-js
21
+ # npm / pnpm / aube
22
22
  ```
23
23
 
24
- > **WebGPU-first** — falls back automatically to WebGL2 → WebGL1. No configuration required.
25
-
26
- ---
27
-
28
- ## Features
29
-
30
- - **Fluid text effects** — wrap fluid around text as obstacle + background
31
- - **Fluid image effects** — apply fluid over any bitmap with `cover`/`contain` sizing
32
- - **5 visual algorithms** — standard, glass, ink, aurora, ripple
33
- - **5 presets** — calm, sand, wave, neon, smoke
34
- - **Web Worker** — simulation runs off the main thread via OffscreenCanvas
35
- - **WebGPU + WebGL2 + WebGL1** — widest browser compatibility
36
- - **Fully typed** — ambient TypeScript types, no import needed
37
- - **React 17+** — forwardRef components, StrictMode safe
38
- - **Programmatic API** — `splat()`, `move()`, `updateConfig()`, `reset()`
39
- - **Reactive props** — change algorithm, preset, quality, config at runtime
40
-
41
- ---
42
-
43
- ## Quickstart
44
-
45
24
  ```tsx
46
- import { FluidText, FluidImage } from '@jayf0x/fluidity-js';
25
+ import { FluidImage, FluidText } from '@jayf0x/fluidity-js';
47
26
 
48
27
  // Fluid text — reacts to cursor movement
49
- export function Hero() {
28
+ export const Hero = () => {
50
29
  return (
51
30
  <div style={{ width: '100%', height: 400 }}>
52
31
  <FluidText text="Hello World" fontSize={140} color="#ffffff" />
53
32
  </div>
54
33
  );
55
- }
34
+ };
56
35
 
57
36
  // Full-bleed fluid image
58
- export function Cover() {
37
+ export const Cover = () => {
59
38
  return (
60
39
  <div style={{ width: '100%', height: '100vh' }}>
61
40
  <FluidImage src="/hero.jpg" algorithm="aurora" />
62
41
  </div>
63
42
  );
64
- }
65
- ```
66
-
67
- **Presets and algorithms:**
68
-
69
- ```tsx
70
- <FluidText text="Wicked" preset="neon" algorithm="glass" />
71
- <FluidImage src="/poster.jpg" algorithm="ripple" config={{ curl: 0.4, splatRadius: 0.008 }} />
72
- <FluidText text="Chill" preset="calm" quality={{ dpr: 1, sim: 0.75 }} />
43
+ };
73
44
  ```
74
45
 
75
46
  ---
76
47
 
77
48
  ## Programmatic control
78
49
 
79
- Use `ref` to trigger effects from code — attractors, scroll-driven splats, click bursts:
50
+ Use `ref` to trigger effects from code — scroll-driven splats, click bursts, attract particles:
80
51
 
81
52
  ```tsx
82
53
  import { useRef } from 'react';
54
+
83
55
  import { FluidText } from '@jayf0x/fluidity-js';
84
56
 
85
- export function Interactive() {
57
+ export const Interactive = () => {
86
58
  const fluid = useRef<FluidHandle>(null);
87
59
 
88
60
  return (
89
61
  <>
90
62
  <div style={{ width: '100%', height: 400 }}>
91
- <FluidText ref={fluid} text="Touch me" fontSize={120} color="#fff" />
63
+ <FluidText ref={fluid} text="Splash!" fontSize={120} color="#fff" />
92
64
  </div>
93
65
  <button onClick={() => fluid.current?.splat(200, 200, 8, -4)}>Splat</button>
94
66
  <button onClick={() => fluid.current?.updateConfig({ curl: 0.5 })}>Swirl</button>
95
67
  <button onClick={() => fluid.current?.reset()}>Reset</button>
96
68
  </>
97
69
  );
98
- }
70
+ };
99
71
  ```
100
72
 
101
- ### FluidHandle methods
102
-
103
- | Method | Description |
104
- |--------|-------------|
105
- | `reset()` | Re-initialise simulation and reload source |
106
- | `updateConfig(patch)` | Merge a partial config update into the running sim |
107
- | `move({ x, y, strength? })` | Programmatic pointer input (canvas-relative px) |
108
- | `splat(x, y, vx, vy, strength?)` | Inject a fluid splat directly — safe to call N×/frame |
73
+ | Method | What it does |
74
+ | -------------------------------- | --------------------------------------------------------- |
75
+ | `reset()` | Restart the simulation |
76
+ | `updateConfig(patch)` | Change any config value live |
77
+ | `move({ x, y, strength? })` | Simulate a pointer move |
78
+ | `splat(x, y, vx, vy, strength?)` | Inject fluid directly safe to call many times per frame |
109
79
 
110
80
  ---
111
81
 
@@ -113,50 +83,50 @@ export function Interactive() {
113
83
 
114
84
  ### FluidText
115
85
 
116
- | Prop | Type | Default |
117
- |------|------|---------|
118
- | `text` | `string` | — |
119
- | `fontSize` | `number` | `100` |
120
- | `color` | `string` | `'#ffffff'` |
121
- | `fontFamily` | `string` | `'sans-serif'` |
122
- | `fontWeight` | `string \| number` | `900` |
86
+ | Prop | Type | Default |
87
+ | ------------ | ------------------ | -------------- |
88
+ | `text` | `string` | — |
89
+ | `fontSize` | `number` | `100` |
90
+ | `color` | `string` | `'#ffffff'` |
91
+ | `fontFamily` | `string` | `'sans-serif'` |
92
+ | `fontWeight` | `string \| number` | `900` |
123
93
 
124
94
  ### FluidImage
125
95
 
126
- | Prop | Type | Default |
127
- |------|------|---------|
128
- | `src` | `string` | — |
96
+ | Prop | Type | Default |
97
+ | ----------- | ------------------ | --------- |
98
+ | `src` | `string` | — |
129
99
  | `imageSize` | `string \| number` | `'cover'` |
130
- | `effect` | `number` | `0` |
100
+ | `effect` | `number` | `0` |
131
101
 
132
102
  ### Shared props
133
103
 
134
- | Prop | Type | Default |
135
- |------|------|---------|
136
- | `config` | `Partial<FluidConfig>` | — |
137
- | `preset` | `PresetKey` | — |
138
- | `algorithm` | `FluidAlgorithm` | `'standard'` |
139
- | `quality` | `FluidQuality` | `{ dpr: 1, sim: 0.5 }` |
140
- | `backgroundColor` | `string` | `'#0a0a0a'` |
141
- | `backgroundSrc` | `string` | — |
142
- | `backgroundSize` | `string \| number` | `'cover'` |
143
- | `isMouseEnabled` | `boolean` | `true` |
144
- | `isWorkerEnabled` | `boolean` | `true` |
145
- | `useWebGPU` | `boolean` | `true` |
146
- | `className` | `string` | — |
147
- | `style` | `CSSProperties` | — |
104
+ | Prop | Type | Default |
105
+ | ----------------- | ---------------------- | ---------------------- |
106
+ | `config` | `Partial<FluidConfig>` | — |
107
+ | `preset` | `PresetKey` | — |
108
+ | `algorithm` | `FluidAlgorithm` | `'standard'` |
109
+ | `quality` | `FluidQuality` | `{ dpr: 1, sim: 0.5 }` |
110
+ | `backgroundColor` | `string` | `'#0a0a0a'` |
111
+ | `backgroundSrc` | `string` | — |
112
+ | `backgroundSize` | `string \| number` | `'cover'` |
113
+ | `isMouseEnabled` | `boolean` | `true` |
114
+ | `isWorkerEnabled` | `boolean` | `true` |
115
+ | `useWebGPU` | `boolean` | `true` |
116
+ | `className` | `string` | — |
117
+ | `style` | `CSSProperties` | — |
148
118
 
149
119
  ---
150
120
 
151
121
  ## Algorithms
152
122
 
153
- | Value | Character |
154
- |-------|-----------|
155
- | `'standard'` | Colour overlay + gentle refraction (default) |
156
- | `'glass'` | UV distortion only — bent-glass, no colour |
157
- | `'ink'` | Dense opaque pigment that accumulates and stains |
158
- | `'aurora'` | Velocity-field UV warp — liquid metal / lava-lamp |
159
- | `'ripple'` | Exaggerated normals + Fresnel rim — still water |
123
+ | Value | Vibe |
124
+ | ------------ | ------------------------------------------------ |
125
+ | `'standard'` | Colour overlay + gentle refraction (default) |
126
+ | `'glass'` | Bent-glass distortion, no colour |
127
+ | `'ink'` | Dense opaque pigment that accumulates and stains |
128
+ | `'aurora'` | Liquid metal / lava-lamp |
129
+ | `'ripple'` | Still water surface with Fresnel rim |
160
130
 
161
131
  ```tsx
162
132
  <FluidImage src="/photo.jpg" algorithm="aurora" />
@@ -165,27 +135,14 @@ export function Interactive() {
165
135
 
166
136
  ---
167
137
 
168
- ## Presets
169
-
170
- ```tsx
171
- <FluidText text="Wicked" preset="neon" />
172
- <FluidText text="Calm vibes" preset="calm" />
173
- ```
174
-
175
- Available: `calm` · `sand` · `wave` · `neon` · `smoke`
176
-
177
- Presets are reactive — changing the prop at runtime re-applies the config. Any `config` values you pass override the preset. The `algorithm` prop also overrides the preset's algorithm.
178
-
179
- ---
180
-
181
138
  ## Quality
182
139
 
183
- `quality` controls rendering resolution on two independent axes. Both are reactive at runtime.
140
+ Control rendering resolution on two independent axes both reactive at runtime.
184
141
 
185
- | Field | Range | Default | Description |
186
- |-------|-------|---------|-------------|
187
- | `dpr` | 0.1–1 | `1` | Canvas backing resolution as fraction of `devicePixelRatio`. `0.5` on Retina saves ~75% fill rate. |
188
- | `sim` | 0.1–1 | `0.5` | Simulation FBO size as fraction of canvas size. Lower = cheaper GPU, less fluid detail. |
142
+ | Field | Range | Default | What it does |
143
+ | ----- | ----- | ------- | ----------------------------------------------------------------------------------------- |
144
+ | `dpr` | 0.1–1 | `1` | Canvas resolution as fraction of screen pixel ratio. `0.5` on Retina saves ~75% GPU fill. |
145
+ | `sim` | 0.1–1 | `0.5` | Simulation resolution. Lower = cheaper, less detail. |
189
146
 
190
147
  ```tsx
191
148
  // Sharp canvas, cheap simulation
@@ -199,88 +156,41 @@ Presets are reactive — changing the prop at runtime re-applies the config. Any
199
156
 
200
157
  ## FluidConfig reference
201
158
 
202
- | Key | Default | Description |
203
- |-----|---------|-------------|
204
- | `densityDissipation` | `0.992` | How long ink lingers (0–1) |
205
- | `velocityDissipation` | `0.93` | How fast velocity decays (0–1) |
206
- | `pressureIterations` | `1` | Jacobi iterations — accuracy vs. cost |
207
- | `curl` | `0.0001` | Vorticity / swirl. `0.2`–`0.5` for visible eddies |
208
- | `splatRadius` | `0.004` | Brush radius |
209
- | `splatForce` | `0.91` | Force applied by brush |
210
- | `refraction` | `0.25` | Background warp strength |
211
- | `specularExp` | `1.01` | Specular highlight sharpness |
212
- | `shine` | `0.01` | Highlight intensity |
213
- | `waterColor` | `[0, 0, 0]` | Base fluid colour `[R, G, B]` (0–1) |
214
- | `glowColor` | `[0.7, 0.85, 1.0]` | Glow / specular colour `[R, G, B]` (0–1) |
215
- | `warpStrength` | `0.015` | UV warp intensity (`aurora` algorithm) |
216
-
217
- ---
218
-
219
- ## TypeScript
220
-
221
- All types are globally ambient — no import required:
222
-
223
- ```ts
224
- // Available globally after installing the package:
225
- // FluidConfig, FluidHandle, FluidAlgorithm, FluidQuality, PresetKey
226
- ```
227
-
228
- ---
229
-
230
- ## How it works
231
-
232
- fluidity-js runs a real-time **Navier-Stokes fluid simulation** entirely on the GPU:
233
-
234
- 1. **Advect velocity** — move velocity field along itself; obstacle mask zeroes inside text/image
235
- 2. **Advect density** — move ink/colour field with velocity
236
- 3. **Curl → vorticity confinement** — preserve swirling detail
237
- 4. **Splat** — inject velocity + density at cursor or programmatic positions
238
- 5. **Divergence → pressure solve** — N Jacobi iterations for incompressibility
239
- 6. **Gradient subtract** — enforce divergence-free velocity
240
- 7. **Display pass** — 5 texture units: density, obstacle, background, coverage mask, velocity; 5 algorithms selectable by uniform
241
-
242
- The simulation runs in a **Web Worker** by default, communicating with the React component via `postMessage`. The canvas is transferred to the worker via `OffscreenCanvas.transferControlToOffscreen()` for true off-thread rendering.
243
-
244
- **WebGPU path** uses render pipelines via `@webgpu/types`. **WebGL path** uses double-framebuffer objects (ping-pong FBOs) with GLSL shaders.
159
+ | Key | Default | Description |
160
+ | --------------------- | ------------------ | ----------------------------------------------- |
161
+ | `densityDissipation` | `0.992` | How long ink lingers (0–1) |
162
+ | `velocityDissipation` | `0.93` | How fast fluid slows down (0–1) |
163
+ | `pressureIterations` | `1` | Quality vs. cost trade-off |
164
+ | `curl` | `0.0001` | Swirl intensity. `0.2`–`0.5` for visible eddies |
165
+ | `splatRadius` | `0.004` | Brush radius |
166
+ | `splatForce` | `0.91` | Force applied by brush |
167
+ | `refraction` | `0.25` | Background warp strength |
168
+ | `specularExp` | `1.01` | Specular highlight sharpness |
169
+ | `shine` | `0.01` | Highlight intensity |
170
+ | `waterColor` | `[0, 0, 0]` | Base fluid colour `[R, G, B]` (0–1) |
171
+ | `glowColor` | `[0.7, 0.85, 1.0]` | Glow / specular colour `[R, G, B]` (0–1) |
172
+ | `warpStrength` | `0.015` | UV warp intensity (`aurora` algorithm) |
245
173
 
246
174
  ---
247
175
 
248
176
  ## Browser support
249
177
 
250
- | Browser | WebGPU | WebGL2 | WebGL1 |
251
- |---------|--------|--------|--------|
252
- | Chrome 113+ | ✅ | ✅ | ✅ |
253
- | Edge 113+ | ✅ | ✅ | ✅ |
254
- | Firefox | — | ✅ | ✅ |
255
- | Safari 17+ | ✅ (partial) | ✅ | ✅ |
256
- | Safari < 17 | — | ✅ | ✅ |
257
- | Mobile Chrome | — | ✅ | ✅ |
178
+ Works in all modern browsers. Automatically picks the best renderer available — no configuration needed.
258
179
 
259
- Fallback is automatic — no configuration needed.
260
-
261
- ---
262
-
263
- ## Examples
264
-
265
- Full working examples live in [`demo/src/examples/`](./demo/src/examples/):
266
-
267
- - `TextExample.tsx` — FluidText with Leva controls
268
- - `ImageExample.tsx` — FluidImage with backgroundSrc
269
- - `SplashExample.tsx` — full-page fluid hero
270
- - `SplitExample.tsx` — split-screen comparison
271
- - `PresetsExample.tsx` — all presets side by side
272
-
273
- [**Open live demo →**](https://jayf0x.github.io/fluidity)
180
+ | Browser | Support |
181
+ | ------------- | ------- |
182
+ | Chrome 113+ | ✅ |
183
+ | Edge 113+ | ✅ |
184
+ | Firefox | ✅ |
185
+ | Safari 17+ | ✅ |
186
+ | Safari < 17 | ✅ |
187
+ | Mobile Chrome | ✅ |
274
188
 
275
189
  ---
276
190
 
277
191
  ## Contributing
278
192
 
279
- Issues and PRs welcome. See [AGENTS.md](./AGENTS.md) for agent-specific instructions and code conventions.
280
-
281
- 1. Fork → branch → PR against `main`
282
- 2. Run `bun test:claude` before pushing — all 83 tests must pass
283
- 3. No new dependencies without discussion
193
+ Issues and PRs welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup and [AGENTS.md](./AGENTS.md) for code conventions.
284
194
 
285
195
  ---
286
196