@jayf0x/fluidity-js 0.2.2 → 0.2.3

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.
package/AGENTS.md ADDED
@@ -0,0 +1,148 @@
1
+ # AGENTS.md — fluidity-js
2
+
3
+ > Universal agent instructions for GitHub Copilot, Cursor, GPT-4, Gemini, and all AI coding assistants.
4
+ > Claude Code users: see [CLAUDE.md](./CLAUDE.md) for deeper architecture notes.
5
+
6
+ ---
7
+
8
+ ## What this repo is
9
+
10
+ `@jayf0x/fluidity-js` — a WebGPU-first fluid simulation library for React. Implements a real-time Navier-Stokes solver (advection, divergence, pressure, vorticity confinement) that renders onto a `<canvas>` element. Falls back automatically to WebGL2 → WebGL1. Runs the heavy simulation loop in a Web Worker via OffscreenCanvas by default.
11
+
12
+ Two components: `<FluidText>` (text as obstacle/background) and `<FluidImage>` (bitmap as obstacle/background).
13
+
14
+ **Stack:** TypeScript · React 17+ · Vite 4 · Vitest · Bun
15
+
16
+ ---
17
+
18
+ ## Repo layout (critical paths)
19
+
20
+ ```
21
+ src/
22
+ index.ts ← public exports
23
+ globals.d.ts ← ambient global types (FluidConfig, FluidHandle…)
24
+ core/
25
+ config.ts ← DEFAULT_CONFIG, presets, mergeConfig
26
+ gl-utils.ts ← WebGL context + FBO helpers
27
+ gpu-utils.ts ← WebGPU helpers
28
+ shaders.ts ← GLSL shader strings
29
+ wgsl-shaders.ts ← WGSL shader strings
30
+ simulation.ts ← FluidSimulation class (dual WebGPU/WebGL)
31
+ textures.ts ← texture creation for text + image modes
32
+ worker/index.ts ← Web Worker message handler
33
+ fluid-controller.ts ← worker vs main-thread abstraction
34
+ react/
35
+ FluidText.tsx ← React component
36
+ FluidImage.tsx ← React component
37
+ useFluid.ts ← core hook: canvas lifecycle + controller
38
+ tests/ ← Vitest + jsdom (83 tests)
39
+ demo/ ← standalone Vite 5 demo site (NOT the library)
40
+ dist/ ← built output (do not edit)
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Commands
46
+
47
+ ```bash
48
+ # Run tests (library root, Node 16 compat)
49
+ bun test:claude # preferred: runs vitest, tails last 8 lines
50
+
51
+ # Build library
52
+ bun build
53
+
54
+ # Demo (requires Node 20)
55
+ cd demo
56
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun dev
57
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun build
58
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun deploy # → gh-pages
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Public API
64
+
65
+ ### Components
66
+
67
+ ```tsx
68
+ <FluidText text="Hello" fontSize={120} color="#fff" algorithm="glass" preset="neon" />
69
+ <FluidImage src="/hero.jpg" algorithm="aurora" quality={{ dpr: 1, sim: 0.5 }} />
70
+ ```
71
+
72
+ ### FluidHandle ref
73
+
74
+ ```ts
75
+ interface FluidHandle {
76
+ reset(): void;
77
+ move(opts: { x: number; y: number; strength?: number }): void;
78
+ splat(x: number, y: number, vx: number, vy: number, strength?: number): void;
79
+ updateConfig(config: Partial<FluidConfig>): void;
80
+ }
81
+ ```
82
+
83
+ ### Algorithms
84
+ `'standard'` · `'glass'` · `'ink'` · `'aurora'` · `'ripple'`
85
+
86
+ ### Presets
87
+ `'calm'` · `'sand'` · `'wave'` · `'neon'` · `'smoke'`
88
+
89
+ ---
90
+
91
+ ## Code style / conventions
92
+
93
+ - **TypeScript strict** — no `any`, no non-null assertions without a comment
94
+ - **No file extensions** on imports within `src/` (e.g. `from './config'`)
95
+ Exception: worker import keeps `.js?worker&inline` for Vite query string
96
+ - **No default exports** from library modules; named exports only
97
+ - **No comments** unless the *why* is non-obvious (hidden constraint, invariant, workaround)
98
+ - **Formatting:** Prettier with `@trivago/prettier-plugin-sort-imports`
99
+ - **Tests:** All 83 must pass before any commit. Add tests for new behaviour.
100
+
101
+ ---
102
+
103
+ ## Architecture invariants (do not violate)
104
+
105
+ | Rule | Reason |
106
+ |------|--------|
107
+ | `transferControlToOffscreen` called at most once per canvas | Irreversible — double-mount in StrictMode would crash |
108
+ | `useFluid` creates a **fresh** `<canvas>` element each mount inside a container `<div>` | StrictMode safety |
109
+ | `createBlit` sets up vertex buffers **once** | Performance — never call `vertexAttribPointer` per draw |
110
+ | Worker `.terminate()` always deferred 50ms after `postMessage({type:'destroy'})` | Lets the worker flush its destroy sequence |
111
+ | Display shader outputs **premultiplied alpha** | Transparent canvas compositing correctness |
112
+ | Coverage texture ref: `text` mode → `coverageTex === obstacleTex`; guard against double-delete | Memory safety |
113
+
114
+ ---
115
+
116
+ ## What agents MUST NOT do
117
+
118
+ - **Do not** edit files under `dist/` — these are build artefacts
119
+ - **Do not** add `// eslint-disable` or `@ts-ignore` without explaining why in the PR
120
+ - **Do not** introduce new peer dependencies without updating `peerDependencies` in `package.json`
121
+ - **Do not** call `vertexAttribPointer` inside the render loop
122
+ - **Do not** call `transferControlToOffscreen` more than once per canvas element
123
+ - **Do not** use `window.*` APIs directly inside `worker/index.ts` (worker context)
124
+ - **Do not** commit if `bun test:claude` fails
125
+ - **Do not** change the module format from ESM to CJS — consumers depend on tree-shaking
126
+ - **Do not** modify `CLAUDE.md` unless explicitly asked — it is the authoritative agent guide
127
+
128
+ ---
129
+
130
+ ## Testing notes
131
+
132
+ - Tests run under jsdom with a WebGL mock (`tests/setup.js`). `navigator.gpu` is absent — all tests exercise the WebGL path.
133
+ - `FluidText`/`FluidImage` are `forwardRef` ExoticComponents — check `$$typeof`, not `typeof`.
134
+ - React component mocks require `.ts` extension: `vi.mock('../../src/fluid-controller.ts', ...)`
135
+ - Worker mock: `vi.mock('../src/worker/index.ts?worker&inline', ...)`
136
+
137
+ ---
138
+
139
+ ## Where to find more
140
+
141
+ | Resource | Path |
142
+ |----------|------|
143
+ | Deep architecture guide (Claude-specific) | [CLAUDE.md](./CLAUDE.md) |
144
+ | Prioritised bug/feature backlog | [backlog.md](./backlog.md) |
145
+ | Version history | [changelog.md](./changelog.md) |
146
+ | Live demo source | [demo/src/examples/](./demo/src/examples/) |
147
+ | npm package | https://www.npmjs.com/package/@jayf0x/fluidity-js |
148
+ | Live demo | https://jayf0x.github.io/fluidity |
@@ -0,0 +1,45 @@
1
+ # Contributing to fluidity-js
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/jayf0x/fluidity
7
+ cd fluidity
8
+ bun install
9
+ ```
10
+
11
+ Run tests:
12
+
13
+ ```bash
14
+ bun test:claude
15
+ ```
16
+
17
+ Run the demo (requires Node 20):
18
+
19
+ ```bash
20
+ cd demo
21
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun install
22
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun dev
23
+ ```
24
+
25
+ ## Before opening a PR
26
+
27
+ - All 83 tests pass (`bun test:claude`)
28
+ - No new peer dependencies without prior discussion
29
+ - Code formatted with `bun format`
30
+ - See [AGENTS.md](./AGENTS.md) for code style and architecture constraints
31
+
32
+ ## Where to look
33
+
34
+ | Goal | Files |
35
+ |------|-------|
36
+ | Add/change a visual effect | `src/core/shaders.ts`, `src/core/wgsl-shaders.ts`, `src/core/simulation.ts` |
37
+ | Add a new prop | `src/globals.d.ts`, `src/react/FluidText.tsx` or `FluidImage.tsx`, `src/core/config.ts` |
38
+ | Add a preset | `src/core/config.ts` |
39
+ | Fix a simulation bug | `src/core/simulation.ts`, `src/core/gl-utils.ts`, `src/core/gpu-utils.ts` |
40
+ | Fix React lifecycle | `src/react/useFluid.ts`, `src/fluid-controller.ts` |
41
+ | Fix worker communication | `src/worker/index.ts`, `src/fluid-controller.ts` |
42
+
43
+ ## Reporting issues
44
+
45
+ Use the GitHub issue templates — include a minimal repro snippet and your browser/renderer info.
package/README.md CHANGED
@@ -1,32 +1,51 @@
1
- # fluidity-js - 🔥Upgrade your UX🔥
1
+ # fluidity-js WebGPU Fluid Simulation for React
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
3
+ [![npm version](https://img.shields.io/npm/v/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@jayf0x/fluidity-js)](https://bundlephobia.com/package/@jayf0x/fluidity-js)
4
6
  [![license](https://img.shields.io/npm/l/@jayf0x/fluidity-js)](./LICENSE)
5
- [![size](https://img.shields.io/bundlephobia/minzip/@jayf0x/fluidity-js)](https://bundlephobia.com/package/@jayf0x/fluidity-js)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](./tsconfig.json)
8
+ [![CI](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml/badge.svg)](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml)
6
9
 
7
- <a href="https://raw.githubusercontent.com/jayf0x/fluidity/main/assets/preview.gif">
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
+ <a href="https://jayf0x.github.io/fluidity">
8
13
  <p align="center">
9
- <img src="assets/preview.gif" alt="preview" height="300px"/>
14
+ <img src="assets/preview.gif" alt="WebGPU fluid simulation — fluid text and image effects in React" height="300px"/>
10
15
  </p>
11
16
  <p align="center"><strong>Live demo →</strong></p>
12
17
  </a>
13
18
 
14
19
  ```bash
15
- bun add @jayf0x/fluidity-js
16
- # or: npm install / yarn add / pnpm add
20
+ npm install @jayf0x/fluidity-js
21
+ # bun add / yarn add / pnpm add
17
22
  ```
18
23
 
19
- > **WebGPU-first** 🔥 — falls back automatically to WebGL2 / WebGL1.
24
+ > **WebGPU-first** — falls back automatically to WebGL2 WebGL1. No configuration required.
20
25
 
21
26
  ---
22
27
 
23
- ## React examples
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
+ ---
24
42
 
25
- **Text that moves with your cursor:**
43
+ ## Quickstart
26
44
 
27
45
  ```tsx
28
- import { FluidText } from '@jayf0x/fluidity-js';
46
+ import { FluidText, FluidImage } from '@jayf0x/fluidity-js';
29
47
 
48
+ // Fluid text — reacts to cursor movement
30
49
  export function Hero() {
31
50
  return (
32
51
  <div style={{ width: '100%', height: 400 }}>
@@ -34,13 +53,8 @@ export function Hero() {
34
53
  </div>
35
54
  );
36
55
  }
37
- ```
38
-
39
- **Full-bleed image cover — one line to make it alive:**
40
-
41
- ```tsx
42
- import { FluidImage } from '@jayf0x/fluidity-js';
43
56
 
57
+ // Full-bleed fluid image
44
58
  export function Cover() {
45
59
  return (
46
60
  <div style={{ width: '100%', height: '100vh' }}>
@@ -50,7 +64,7 @@ export function Cover() {
50
64
  }
51
65
  ```
52
66
 
53
- **Go further — presets, algorithms, live config:**
67
+ **Presets and algorithms:**
54
68
 
55
69
  ```tsx
56
70
  <FluidText text="Wicked" preset="neon" algorithm="glass" />
@@ -58,11 +72,14 @@ export function Cover() {
58
72
  <FluidText text="Chill" preset="calm" quality={{ dpr: 1, sim: 0.75 }} />
59
73
  ```
60
74
 
61
- **Trigger effects programmatically:**
75
+ ---
76
+
77
+ ## Programmatic control
78
+
79
+ Use `ref` to trigger effects from code — attractors, scroll-driven splats, click bursts:
62
80
 
63
81
  ```tsx
64
82
  import { useRef } from 'react';
65
-
66
83
  import { FluidText } from '@jayf0x/fluidity-js';
67
84
 
68
85
  export function Interactive() {
@@ -81,7 +98,14 @@ export function Interactive() {
81
98
  }
82
99
  ```
83
100
 
84
- Official examples → [`demo/src/examples/`](./demo/src/examples/)
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 |
85
109
 
86
110
  ---
87
111
 
@@ -89,50 +113,50 @@ Official examples → [`demo/src/examples/`](./demo/src/examples/)
89
113
 
90
114
  ### FluidText
91
115
 
92
- | Prop | Type | Default |
93
- | ------------ | ------------------ | -------------- |
94
- | `text` | `string` | — |
95
- | `fontSize` | `number` | `100` |
96
- | `color` | `string` | `'#ffffff'` |
97
- | `fontFamily` | `string` | `'sans-serif'` |
98
- | `fontWeight` | `string \| number` | `900` |
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` |
99
123
 
100
124
  ### FluidImage
101
125
 
102
- | Prop | Type | Default |
103
- | ----------- | ------------------ | --------- |
104
- | `src` | `string` | — |
126
+ | Prop | Type | Default |
127
+ |------|------|---------|
128
+ | `src` | `string` | — |
105
129
  | `imageSize` | `string \| number` | `'cover'` |
106
- | `effect` | `number` | `0` |
130
+ | `effect` | `number` | `0` |
107
131
 
108
132
  ### Shared props
109
133
 
110
- | Prop | Type | Default |
111
- | ----------------- | ---------------------- | ---------------------- |
112
- | `config` | `Partial<FluidConfig>` | — |
113
- | `preset` | `PresetKey` | — |
114
- | `algorithm` | `FluidAlgorithm` | `'standard'` |
115
- | `quality` | `FluidQuality` | `{ dpr: 1, sim: 0.5 }` |
116
- | `backgroundColor` | `string` | `'#0a0a0a'` |
117
- | `backgroundSrc` | `string` | — |
118
- | `backgroundSize` | `string \| number` | `'cover'` |
119
- | `isMouseEnabled` | `boolean` | `true` |
120
- | `isWorkerEnabled` | `boolean` | `true` |
121
- | `useWebGPU` | `boolean` | `true` |
122
- | `className` | `string` | — |
123
- | `style` | `CSSProperties` | — |
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` | — |
124
148
 
125
149
  ---
126
150
 
127
151
  ## Algorithms
128
152
 
129
- | Value | Character |
130
- | ------------ | ------------------------------------------------- |
131
- | `'standard'` | Colour overlay + gentle refraction (default) |
132
- | `'glass'` | Strong UV distortion only — bent-glass, no colour |
133
- | `'ink'` | Dense opaque pigment that accumulates and stains |
134
- | `'aurora'` | Velocity-field UV warp — liquid metal / lava-lamp |
135
- | `'ripple'` | Exaggerated normals + Fresnel rim — still water |
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 |
136
160
 
137
161
  ```tsx
138
162
  <FluidImage src="/photo.jpg" algorithm="aurora" />
@@ -141,74 +165,125 @@ Official examples → [`demo/src/examples/`](./demo/src/examples/)
141
165
 
142
166
  ---
143
167
 
144
- ## Quality
145
-
146
- `quality` controls rendering resolution on two independent axes. Both props are reactive — you can adjust them at runtime.
147
-
148
- | Field | Range | Default | Description |
149
- | ----- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
150
- | `dpr` | 0.1 – 1 | `1` | Canvas backing resolution as a fraction of `devicePixelRatio`. `0.5` on a Retina screen renders at 1× instead of 2×, saving ~75% fill rate. |
151
- | `sim` | 0.1 – 1 | `0.5` | Simulation FBO size as a fraction of canvas size. Lower = cheaper GPU, less fluid detail. |
168
+ ## Presets
152
169
 
153
170
  ```tsx
154
- <FluidText text="hello" quality={{ dpr: 0.75, sim: 0.25 }} />
171
+ <FluidText text="Wicked" preset="neon" />
172
+ <FluidText text="Calm vibes" preset="calm" />
155
173
  ```
156
174
 
157
- `dpr` and `sim` are independent you can run a sharp canvas at a coarser simulation:
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
+ ## Quality
182
+
183
+ `quality` controls rendering resolution on two independent axes. Both are reactive at runtime.
184
+
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. |
158
189
 
159
190
  ```tsx
160
- // Sharp display, cheap simulation
191
+ // Sharp canvas, cheap simulation
161
192
  <FluidImage src="/hero.jpg" quality={{ dpr: 1, sim: 0.2 }} />
162
193
 
163
- // Lower display res, full simulation quality
194
+ // Lower canvas res, full simulation quality
164
195
  <FluidImage src="/hero.jpg" quality={{ dpr: 0.5, sim: 1 }} />
165
196
  ```
166
197
 
167
198
  ---
168
199
 
169
- ## FluidConfig
170
-
171
- | Key | Default | Description |
172
- | --------------------- | ------------------ | ----------------------------------------- |
173
- | `densityDissipation` | `0.992` | How long ink lingers (0–1) |
174
- | `velocityDissipation` | `0.93` | How fast velocity decays (0–1) |
175
- | `pressureIterations` | `1` | Jacobi iterations — accuracy vs. cost |
176
- | `curl` | `0.0001` | Vorticity / swirl. `0.2`–`0.5` for eddies |
177
- | `splatRadius` | `0.004` | Brush radius |
178
- | `splatForce` | `0.91` | Force applied by brush |
179
- | `refraction` | `0.25` | Background warp strength |
180
- | `specularExp` | `1.01` | Specular highlight sharpness |
181
- | `shine` | `0.01` | Highlight intensity |
182
- | `waterColor` | `[0, 0, 0]` | Base fluid colour `[R, G, B]` (0–1) |
183
- | `glowColor` | `[0.7, 0.85, 1.0]` | Glow / specular colour `[R, G, B]` (0–1) |
184
- | `warpStrength` | `0.015` | UV warp intensity (`aurora` algorithm) |
200
+ ## FluidConfig reference
201
+
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) |
185
216
 
186
217
  ---
187
218
 
188
- ## FluidHandle (ref)
219
+ ## TypeScript
189
220
 
190
- | Method | Description |
191
- | -------------------------------- | ----------------------------------------------------- |
192
- | `reset()` | Re-initialise simulation and reload source |
193
- | `updateConfig(patch)` | Merge a partial config update into running sim |
194
- | `move({ x, y, strength? })` | Programmatic pointer input (canvas-relative px) |
195
- | `splat(x, y, vx, vy, strength?)` | Inject a fluid splat directly — safe to call N×/frame |
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
+ ```
196
227
 
197
228
  ---
198
229
 
199
- ## Presets
230
+ ## How it works
200
231
 
201
- ```tsx
202
- <FluidText text="Wicked" preset="neon" />
203
- <FluidText text="Wicked" preset="calm" />
204
- ```
232
+ fluidity-js runs a real-time **Navier-Stokes fluid simulation** entirely on the GPU:
205
233
 
206
- Available: `calm` · `sand` · `wave` · `neon` · `smoke`
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.
245
+
246
+ ---
247
+
248
+ ## Browser support
249
+
250
+ | Browser | WebGPU | WebGL2 | WebGL1 |
251
+ |---------|--------|--------|--------|
252
+ | Chrome 113+ | ✅ | ✅ | ✅ |
253
+ | Edge 113+ | ✅ | ✅ | ✅ |
254
+ | Firefox | — | ✅ | ✅ |
255
+ | Safari 17+ | ✅ (partial) | ✅ | ✅ |
256
+ | Safari < 17 | — | ✅ | ✅ |
257
+ | Mobile Chrome | — | ✅ | ✅ |
258
+
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)
274
+
275
+ ---
276
+
277
+ ## Contributing
278
+
279
+ Issues and PRs welcome. See [AGENTS.md](./AGENTS.md) for agent-specific instructions and code conventions.
207
280
 
208
- `preset` is reactive — changing it re-applies the preset config. Any `config` values you pass override the preset. `algorithm` prop also overrides the preset's algorithm.
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
209
284
 
210
285
  ---
211
286
 
212
287
  ## License
213
288
 
214
- [MIT](./LICENSE)
289
+ [MIT](./LICENSE) © [jayf0x](https://github.com/jayf0x)
package/llms.txt ADDED
@@ -0,0 +1,156 @@
1
+ # fluidity-js
2
+
3
+ > WebGPU-first fluid simulation library for React. Real-time Navier-Stokes solver — text and image effects with hardware-accelerated fluid dynamics.
4
+
5
+ - npm: https://www.npmjs.com/package/@jayf0x/fluidity-js
6
+ - GitHub: https://github.com/jayf0x/fluidity
7
+ - Live demo: https://jayf0x.github.io/fluidity
8
+ - License: MIT
9
+ - Version: 0.2.2
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @jayf0x/fluidity-js
15
+ # bun add / yarn add / pnpm add also work
16
+ ```
17
+
18
+ Peer deps: react >=17, react-dom >=17
19
+
20
+ ## Core concepts
21
+
22
+ - **WebGPU-first** with automatic fallback to WebGL2 → WebGL1
23
+ - Runs simulation in a **Web Worker** via OffscreenCanvas (configurable)
24
+ - Two render modes: `FluidText` (text as obstacle) and `FluidImage` (bitmap as obstacle)
25
+ - Navier-Stokes pipeline: advect velocity → advect density → curl/vorticity → splat → divergence → pressure solve → gradient subtract → display
26
+
27
+ ## Quick start
28
+
29
+ ```tsx
30
+ import { FluidText, FluidImage } from '@jayf0x/fluidity-js';
31
+
32
+ // Fluid text that reacts to cursor
33
+ <FluidText text="Hello World" fontSize={140} color="#ffffff" />
34
+
35
+ // Fluid image with aurora algorithm
36
+ <FluidImage src="/hero.jpg" algorithm="aurora" />
37
+
38
+ // With preset and custom config
39
+ <FluidText text="Wicked" preset="neon" algorithm="glass" config={{ curl: 0.4 }} />
40
+ ```
41
+
42
+ ## FluidText props
43
+
44
+ | Prop | Type | Default |
45
+ |------|------|---------|
46
+ | text | string | required |
47
+ | fontSize | number | 100 |
48
+ | color | string | '#ffffff' |
49
+ | fontFamily | string | 'sans-serif' |
50
+ | fontWeight | string\|number | 900 |
51
+
52
+ ## FluidImage props
53
+
54
+ | Prop | Type | Default |
55
+ |------|------|---------|
56
+ | src | string | required |
57
+ | imageSize | string\|number | 'cover' |
58
+ | effect | number | 0 |
59
+
60
+ ## Shared props (both components)
61
+
62
+ | Prop | Type | Default |
63
+ |------|------|---------|
64
+ | config | Partial<FluidConfig> | — |
65
+ | preset | PresetKey | — |
66
+ | algorithm | FluidAlgorithm | 'standard' |
67
+ | quality | FluidQuality | { dpr: 1, sim: 0.5 } |
68
+ | backgroundColor | string | '#0a0a0a' |
69
+ | backgroundSrc | string | — |
70
+ | backgroundSize | string\|number | 'cover' |
71
+ | isMouseEnabled | boolean | true |
72
+ | isWorkerEnabled | boolean | true |
73
+ | useWebGPU | boolean | true |
74
+ | className | string | — |
75
+ | style | CSSProperties | — |
76
+
77
+ ## FluidHandle ref API
78
+
79
+ Access via `useRef<FluidHandle>()` and `ref` prop:
80
+
81
+ ```tsx
82
+ const fluid = useRef<FluidHandle>(null);
83
+ <FluidText ref={fluid} text="Touch me" />
84
+ fluid.current?.splat(200, 200, 8, -4);
85
+ fluid.current?.updateConfig({ curl: 0.5 });
86
+ fluid.current?.reset();
87
+ fluid.current?.move({ x: 100, y: 100, strength: 2 });
88
+ ```
89
+
90
+ ## Algorithms
91
+
92
+ | Value | Effect |
93
+ |-------|--------|
94
+ | standard | Colour overlay + gentle refraction |
95
+ | glass | UV distortion only — bent glass |
96
+ | ink | Dense opaque pigment that accumulates |
97
+ | aurora | Velocity-field UV warp — liquid metal |
98
+ | ripple | Exaggerated normals + Fresnel rim |
99
+
100
+ ## Presets
101
+
102
+ `calm` · `sand` · `wave` · `neon` · `smoke`
103
+
104
+ ## FluidConfig options
105
+
106
+ | Key | Default | Description |
107
+ |-----|---------|-------------|
108
+ | densityDissipation | 0.992 | Ink persistence (0–1) |
109
+ | velocityDissipation | 0.93 | Velocity decay (0–1) |
110
+ | pressureIterations | 1 | Jacobi iterations |
111
+ | curl | 0.0001 | Vorticity / swirl strength |
112
+ | splatRadius | 0.004 | Brush radius |
113
+ | splatForce | 0.91 | Brush force |
114
+ | refraction | 0.25 | Background warp strength |
115
+ | specularExp | 1.01 | Specular sharpness |
116
+ | shine | 0.01 | Highlight intensity |
117
+ | waterColor | [0,0,0] | Base fluid RGB (0–1 each) |
118
+ | glowColor | [0.7,0.85,1.0] | Specular/glow RGB (0–1 each) |
119
+ | warpStrength | 0.015 | UV warp (aurora algorithm) |
120
+
121
+ ## Quality control
122
+
123
+ ```tsx
124
+ // dpr: canvas backing resolution fraction (0.1–1)
125
+ // sim: simulation FBO size fraction (0.1–1)
126
+ <FluidText text="hello" quality={{ dpr: 0.75, sim: 0.25 }} />
127
+ ```
128
+
129
+ Both axes are independent and reactive at runtime.
130
+
131
+ ## TypeScript types
132
+
133
+ All types are globally ambient — no import needed:
134
+
135
+ ```ts
136
+ // Available globally after installing the package:
137
+ FluidConfig
138
+ FluidHandle
139
+ FluidAlgorithm // 'standard'|'glass'|'ink'|'aurora'|'ripple'
140
+ FluidQuality // { dpr: number; sim: number }
141
+ PresetKey // 'calm'|'sand'|'wave'|'neon'|'smoke'
142
+ ```
143
+
144
+ ## Architecture summary
145
+
146
+ ```
147
+ React component (FluidText / FluidImage)
148
+ └── useFluid hook
149
+ └── FluidController
150
+ ├── [worker mode] Web Worker → FluidSimulation (OffscreenCanvas)
151
+ └── [main mode] FluidSimulation directly
152
+ └── WebGPU path (FluidSimulation.create())
153
+ └── WebGL2 / WebGL1 fallback
154
+ ```
155
+
156
+ Worker transfer: zero-copy OffscreenCanvas transfer. Background images transferred as ImageBitmap (zero-copy). Safe to destroy/recreate under React StrictMode.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jayf0x/fluidity-js",
3
- "version": "0.2.2",
4
- "description": "High-performance WebGPU fluid simulation for React — text and image effects",
3
+ "version": "0.2.3",
4
+ "description": "WebGPU-first real-time Navier-Stokes fluid simulation for React — interactive water, ink, glass, aurora, and ripple effects on text and images. Falls back to WebGL2/WebGL1. Runs in a Web Worker.",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
7
7
  "exports": {
@@ -13,7 +13,10 @@
13
13
  "types": "./src/index.d.ts",
14
14
  "files": [
15
15
  "dist",
16
- "README.md"
16
+ "README.md",
17
+ "AGENTS.md",
18
+ "llms.txt",
19
+ "CONTRIBUTING.md"
17
20
  ],
18
21
  "sideEffects": false,
19
22
  "scripts": {
@@ -30,12 +33,37 @@
30
33
  "publish:npm": "bash ./scripts/publish-npm.sh"
31
34
  },
32
35
  "keywords": [
36
+ "webgpu",
33
37
  "webgl",
38
+ "webgl2",
34
39
  "fluid",
35
- "simulation",
40
+ "fluid-simulation",
41
+ "fluid-dynamics",
42
+ "navier-stokes",
36
43
  "react",
37
44
  "animation",
38
- "canvas"
45
+ "canvas",
46
+ "water-effect",
47
+ "ink-effect",
48
+ "glass-effect",
49
+ "aurora-effect",
50
+ "ripple-effect",
51
+ "interactive-animation",
52
+ "canvas-animation",
53
+ "hover-effect",
54
+ "text-animation",
55
+ "image-effect",
56
+ "offscreencanvas",
57
+ "web-worker",
58
+ "typescript",
59
+ "react-component",
60
+ "vorticity",
61
+ "gpu",
62
+ "shader",
63
+ "glsl",
64
+ "wgsl",
65
+ "visual-effect",
66
+ "creative-coding"
39
67
  ],
40
68
  "author": "https://github.com/jayf0x",
41
69
  "license": "MIT",
@@ -46,7 +74,7 @@
46
74
  "bugs": {
47
75
  "url": "https://github.com/jayf0x/fluidity/issues"
48
76
  },
49
- "homepage": "https://github.com/jayf0x/fluidity#readme",
77
+ "homepage": "https://jayf0x.github.io/fluidity",
50
78
  "peerDependencies": {
51
79
  "react": ">=17.0.0",
52
80
  "react-dom": ">=17.0.0"