# FluidCursor

> Full Navier-Stokes fluid simulation that swirls color in response to cursor and touch movement, running on WebGL2 with a WebGL1 fallback and an optional WebGPU HDR path for wide-gamut, brighter-than-white glow.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Effects
- Tags: cursor, webgl, fluid, simulation, interactive
- Docs: https://fancy-ui.rama.app/docs/components/fluid-cursor
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/fluid-cursor

## Import

```svelte
<script lang="ts">
  import { FluidCursor } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { FluidCursor } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `simResolution` | `number` | `128` | Simulation grid resolution |
| `dyeResolution` | `number` | `1440` | Dye/color buffer resolution |
| `captureResolution` | `number` | `512` | Capture buffer resolution |
| `densityDissipation` | `number` | `3.5` | Rate at which density fades |
| `velocityDissipation` | `number` | `2` | Rate at which velocity fades |
| `pressure` | `number` | `0.1` | Pressure solver strength |
| `pressureIterations` | `number` | `20` | Number of pressure solver iterations |
| `curl` | `number` | `3` | Amount of curl/vorticity |
| `splatRadius` | `number` | `0.2` | Radius of fluid splats |
| `splatForce` | `number` | `6000` | Force of fluid splats |
| `shading` | `boolean` | `true` | Enable fluid shading |
| `colorUpdateSpeed` | `number` | `10` | Speed of color changes |
| `backColor` | `ColorRGB \| string` | `{ r: 0.5, g: 0, b: 0 }` | Background color |
| `transparent` | `boolean` | `true` | Transparent background |
| `fluidColor` | `string` | — | Override all fluid with a single color |
| `fluidColors` | `string[]` | — | Array of colors to cycle through for fluid splats |
| `colorIntensity` | `number` | `0.15` | Intensity of color mixing |
| `autoSplat` | `boolean` | `false` | Automatically create random splats |
| `autoSplatInterval` | `number` | `1500` | Interval between auto-splats in ms |
| `interactive` | `boolean` | `true` | React to mouse/touch input |
| `pauseWhenHidden` | `boolean` | `true` | Pause simulation when tab is hidden |
| `splatOnMount` | `boolean` | `false` | Create a splat on mount |
| `allowMultiple` | `boolean` | `false` | Allow multiple instances simultaneously |
| `contained` | `boolean` | `true` | Confine simulation to parent container |
| `hdr` | `boolean` | `false` | Render via the WebGPU HDR engine — wide gamut + brighter-than-white glow on HDR displays. Needs Chrome/Edge 129+ or Safari 26+; falls back to wide-gamut WebGL (Chrome 104+, Safari 16.4+, Firefox 132+), then to standard rendering |
| `hdrBoost` | `number` | `1.5` | Display exposure multiplier in HDR mode, clamped to [1, 4] |
| `dither` | `boolean` | `false` | Experimental retro bitmap dithering — pixelates the fluid to a chunky grid and quantizes color with an ordered 4x4 Bayer threshold. Forces the WebGL renderer; hdr is ignored while set |
| `ditherPixelSize` | `number` | `3` | Size of one dithered pixel in CSS pixels, minimum 1 |
| `ditherLevels` | `number` | `4` | Color levels per channel in dither mode, clamped to [2, 16] |

## Examples

### Basic Usage

```svelte
<script lang="ts">
	import { FluidCursor } from "fancy-ui-svelte";
</script>

<div class="relative h-64 w-full overflow-hidden rounded-lg border">
	<FluidCursor />
	<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
		<p class="text-muted-foreground text-sm select-none">Move your cursor here</p>
	</div>
</div>
```

### Custom Colors

Fixed teal fluid color.

```svelte
<script lang="ts">
	import { FluidCursor } from "fancy-ui-svelte";
</script>

<div class="relative h-64 w-full overflow-hidden rounded-lg border">
	<FluidCursor fluidColor="#00ffcc" colorIntensity={0.4} />
	<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
		<p class="text-muted-foreground text-sm select-none">Teal fluid color</p>
	</div>
</div>
```

### HDR Mode

WebGPU engine with extended tone mapping — glows brighter than white on HDR displays. Requires a WebGPU browser (Chrome/Edge 129+, Safari 26+) plus an HDR screen for the full glow; otherwise falls back to wide-gamut WebGL (Chrome 104+, Safari 16.4+, Firefox 132+), then to standard rendering.

```svelte
<script lang="ts">
	import { FluidCursor } from "fancy-ui-svelte";
</script>

<div class="relative h-64 w-full overflow-hidden rounded-lg border bg-black">
	<FluidCursor
		hdr
		hdrBoost={2}
		fluidColors={["#a142ff", "#42cfff", "#ff2fd6"]}
		colorIntensity={0.5}
		splatOnMount
		allowMultiple
	/>
	<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
		<p class="text-sm text-white/60 select-none">
			HDR glow — brightest on an HDR display in a WebGPU browser
		</p>
	</div>
</div>
```

### Bitmap Dithering

Retro ordered-dither rendering — the fluid is snapped to a chunky pixel grid and each color channel is quantized with a 4x4 Bayer threshold, so dot density encodes brightness. Forces the WebGL renderer (hdr is ignored).

```svelte
<script lang="ts">
	import { FluidCursor } from "fancy-ui-svelte";
</script>

<div class="relative h-64 w-full overflow-hidden rounded-lg border bg-black">
	<FluidCursor
		dither
		ditherPixelSize={3}
		ditherLevels={4}
		fluidColors={["#a142ff", "#42cfff", "#2fe6a8", "#ffb02f"]}
		colorIntensity={0.5}
		autoSplat
		splatOnMount
		allowMultiple
	/>
	<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
		<p class="text-sm text-white/60 select-none">Bitmap dithering — move your cursor here</p>
	</div>
</div>
```
