Components
Six components for different use cases. Each one wraps the same WebGL engine with a different interface.
<Fluid>
The core component. Renders a WebGL fluid simulation on a canvas that fills its parent container.
<script lang="ts">
import { Fluid } from 'svelte-fluid';
import type { FluidHandle } from 'svelte-fluid';
let fluidRef = $state<{ handle: FluidHandle } | undefined>();
</script>
<button onclick={() => fluidRef?.handle.randomSplats(8)}>Splat</button>
<div style="height: 400px">
<Fluid bind:this={fluidRef} curl={30} bloom shading colorful />
</div> Accepts all FluidConfig props plus:
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | — | Fixed width in CSS px. Omit to fill parent. |
height | number | — | Fixed height in CSS px. Omit to fill parent. |
class | string | — | Class on wrapper div. |
style | string | — | Inline style on wrapper div. |
lazy | boolean | false | Defer engine until the element enters the viewport. Frees the WebGL context slot. Recommended when you have 6+ instances on one page. |
autoPause | boolean | true | Pause the RAF loop when not visible or tab is hidden. |
Exposes a FluidHandle via bind:this for programmatic control (splats, pause, resume).
<FluidBackground>
Full-viewport fluid behind page content. Use this as a page or screen wrapper; the canvas is fixed to the viewport and the slotted content layer is stacked above it.
<script lang="ts">
import { FluidBackground } from 'svelte-fluid';
</script>
<FluidBackground exclude=".card, .sidebar" splatOnHover colorful shading bloom>
<!-- your page content -->
</FluidBackground> DOM elements matching the exclude selector become physical "holes" — the fluid pools around them. The component observes scroll, resize, and DOM mutations to keep exclusion zones accurate.
pointer-events: none so window-level pointer input keeps feeding the fluid. Add pointer-events: auto back to interactive descendants, and keep nav/cards inside the slot when they should also be included in exclude.<script lang="ts">
import { FluidBackground } from 'svelte-fluid';
</script>
<FluidBackground class="fluid-screen" exclude=".site-nav, .panel" splatOnHover>
<nav class="site-nav">...</nav>
<main class="hero-copy">...</main>
<section class="panel">...</section>
</FluidBackground>
<style>
:global(.fluid-screen) {
min-height: 100vh;
isolation: isolate;
}
.site-nav,
.panel {
position: relative;
z-index: 2;
pointer-events: auto;
}
.hero-copy {
position: relative;
z-index: 1;
}
</style> | Prop | Type | Default | Description |
|---|---|---|---|
exclude | string | — | CSS selector for elements to exclude. Example: ".card, .nav" |
excludeRadius | number | 16 | Border radius of exclusion zones in CSS px. |
excludePad | number | 4 | Padding around exclusion zones in CSS px. |
class | string | — | Class on wrapper div. |
style | string | — | Inline style on wrapper div. |
Defaults to pointerTarget='window' and splatOnHover=true. All FluidConfig props are accepted.
<FluidReveal>
The fluid acts as an opacity mask — cursor movement reveals content behind a solid cover. Great for scratch-to-reveal effects and interactive hero sections.
<script lang="ts">
import { FluidReveal } from 'svelte-fluid';
</script>
<FluidReveal
sensitivity={0.24}
coverColor={{ r: 0.15, g: 0.15, b: 0.18 }}
fringeColor={{ r: 0.6, g: 0.7, b: 0.85 }}
accentColor={{ r: 0.2, g: 0.35, b: 0.7 }}
>
<div>Your hidden content here</div>
</FluidReveal> | Prop | Type | Default | Description |
|---|---|---|---|
sensitivity | number | 0.1 | How easily areas reveal. Higher = less dye needed. |
curve | number | 0.5 | Power exponent for reveal alpha. Higher = crisper edge. |
coverColor | RGB | { r: 1, g: 1, b: 1 } | Solid color of the cover layer (0–1 linear). |
accentColor | RGB | { r: 0.2, g: 0.35, b: 0.7 } | Fringe accent color at reveal edges (0–1 linear). |
fringeColor | RGB | { r: 0.6, g: 0.7, b: 0.85 } | Outer fringe color between cover and accent (0–1 linear). |
fadeBack | boolean | true | Whether revealed areas gradually fade back to covered. |
fadeSpeed | number | — | Explicit dissipation value. 1.0 = permanent, 0.99 = slow fade. Overrides fadeBack. |
autoReveal | boolean | false | Auto-animate a Lissajous curve to reveal content before user interaction. |
autoRevealSpeed | number | 1.0 | Speed of the auto-reveal animation. |
Also accepts width, height, class, style, lazy, autoPause, and all FluidConfig props.
<FluidDistortion>
The fluid velocity field warps an underlying image like liquid glass. Move your cursor to create ripples.
<script lang="ts">
import { FluidDistortion } from 'svelte-fluid';
</script>
<FluidDistortion src="/hero.jpg" strength={0.4} intensity={24} /> | Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | URL of the image to distort. Required. |
strength | number | 0.4 | How strongly velocity warps the image. 0–1. |
intensity | number | 24 | How much dye each interaction injects. |
fit | 'cover' | 'contain' | 'cover' | How the image fits the canvas. |
scale | number | 1.0 | Image scale. >1 zooms out, <1 zooms in. |
autoDistort | boolean | false | Auto-animate distortion via Lissajous curve before user interacts. |
autoDistortSpeed | number | 1.0 | Speed of auto-distort animation. |
initialSplats | number | 20 | Random splats at startup. Creates a chaotic distortion that settles. 0 to start clean. |
bleed | number | 60 | Extra canvas pixels beyond visible edges. Prevents velocity bounce at boundaries. |
Also accepts width, height, class, style, lazy, autoPause, and all FluidConfig props.
<FluidStick>
Dye clings to text or SVG paths via physics-level modulation. Reduced dissipation on the mask makes dye persist, while artificial pressure pushes fluid around the shape.
<script lang="ts">
import { FluidStick } from 'svelte-fluid';
</script>
<!-- Text mode -->
<FluidStick text="FLUID" font="900 120px sans-serif" />
<!-- SVG path mode -->
<FluidStick d="M55 5 L25 45 L45 45 L20 95 L75 50 L55 50 L80 5 Z" /> | Prop | Type | Default | Description |
|---|---|---|---|
text | string | — | Text to render as the sticky mask. |
font | string | 'bold 72px sans-serif' | CSS font string for text mode. |
d | string | — | SVG path data. Takes precedence over text if both are set. |
maskViewBox | [n, n, n, n] | [0,0,100,100] | viewBox for path mode. |
maskFillRule | 'nonzero' | 'evenodd' | 'nonzero' | Fill rule for path mode. |
maskResolution | number | 512 | Mask rasterization resolution. |
maskBlur | number | 4 | Blur radius in mask pixels. Softens edges. |
maskPadding | number | 0.9 | How much of the texture the text fills (text mode only). |
strength | number | 0.95 | How strongly dissipation is reduced on the mask. 1 = dye never fades. |
stickyPressureAmount | number | 0.15 | Artificial pressure on the mask to push fluid around it. |
amplify | number | 2.0 | Splat intensity multiplier on the mask. |
autoAnimate | boolean | true | Auto-animate a Lissajous path to deposit dye before user interaction. |
autoAnimateSpeed | number | 2.0 | Speed of auto-animation. |
autoAnimateDuration | number | 5.0 | Seconds before auto-animation stops. 0 = indefinite. |
Also accepts width, height, class, style, lazy, autoPause, and all FluidConfig props.
<FluidText>
Fluid confined inside text letterforms. Wraps <Fluid> with an svgPath container shape in text mode. Automatically computes the correct aspect ratio via measureText() so the font appears at a consistent visual height regardless of text length.
<script lang="ts">
import { FluidText } from 'svelte-fluid';
</script>
<FluidText
text="SVELTE"
height={100}
seed={42}
splatOnHover
shading
colorful
/> | Prop | Type | Default | Description |
|---|---|---|---|
text | string | — | The text to render as fluid-filled letterforms. Required. |
font | string | 'bold 100px "Helvetica Neue", Arial, sans-serif' | CSS font string for mask rasterization. |
maskResolution | number | 512 | Mask rasterization resolution. |
height | number | — | Fixed height in CSS px. |
Also accepts class, style, lazy, autoPause, and all FluidConfig props. Defaults to transparent=true.