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:

PropTypeDefaultDescription
widthnumberFixed width in CSS px. Omit to fill parent.
heightnumberFixed height in CSS px. Omit to fill parent.
classstringClass on wrapper div.
stylestringInline style on wrapper div.
lazybooleanfalseDefer engine until the element enters the viewport. Frees the WebGL context slot. Recommended when you have 6+ instances on one page.
autoPausebooleantruePause 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.

Composition: Slot content defaults to 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>
PropTypeDefaultDescription
excludestringCSS selector for elements to exclude. Example: ".card, .nav"
excludeRadiusnumber16Border radius of exclusion zones in CSS px.
excludePadnumber4Padding around exclusion zones in CSS px.
classstringClass on wrapper div.
stylestringInline 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>
PropTypeDefaultDescription
sensitivitynumber0.1How easily areas reveal. Higher = less dye needed.
curvenumber0.5Power exponent for reveal alpha. Higher = crisper edge.
coverColorRGB{ r: 1, g: 1, b: 1 }Solid color of the cover layer (0–1 linear).
accentColorRGB{ r: 0.2, g: 0.35, b: 0.7 }Fringe accent color at reveal edges (0–1 linear).
fringeColorRGB{ r: 0.6, g: 0.7, b: 0.85 }Outer fringe color between cover and accent (0–1 linear).
fadeBackbooleantrueWhether revealed areas gradually fade back to covered.
fadeSpeednumberExplicit dissipation value. 1.0 = permanent, 0.99 = slow fade. Overrides fadeBack.
autoRevealbooleanfalseAuto-animate a Lissajous curve to reveal content before user interaction.
autoRevealSpeednumber1.0Speed of the auto-reveal animation.

Also accepts width, height, class, style, lazy, autoPause, and all FluidConfig props.

Note: The canvas sits on top of children for alpha compositing. Interactive elements (links, buttons) inside the slot will not receive pointer events.

<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} />
PropTypeDefaultDescription
srcstringURL of the image to distort. Required.
strengthnumber0.4How strongly velocity warps the image. 0–1.
intensitynumber24How much dye each interaction injects.
fit'cover' | 'contain''cover'How the image fits the canvas.
scalenumber1.0Image scale. >1 zooms out, <1 zooms in.
autoDistortbooleanfalseAuto-animate distortion via Lissajous curve before user interacts.
autoDistortSpeednumber1.0Speed of auto-distort animation.
initialSplatsnumber20Random splats at startup. Creates a chaotic distortion that settles. 0 to start clean.
bleednumber60Extra 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" />
PropTypeDefaultDescription
textstringText to render as the sticky mask.
fontstring'bold 72px sans-serif'CSS font string for text mode.
dstringSVG 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.
maskResolutionnumber512Mask rasterization resolution.
maskBlurnumber4Blur radius in mask pixels. Softens edges.
maskPaddingnumber0.9How much of the texture the text fills (text mode only).
strengthnumber0.95How strongly dissipation is reduced on the mask. 1 = dye never fades.
stickyPressureAmountnumber0.15Artificial pressure on the mask to push fluid around it.
amplifynumber2.0Splat intensity multiplier on the mask.
autoAnimatebooleantrueAuto-animate a Lissajous path to deposit dye before user interaction.
autoAnimateSpeednumber2.0Speed of auto-animation.
autoAnimateDurationnumber5.0Seconds 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
/>
PropTypeDefaultDescription
textstringThe text to render as fluid-filled letterforms. Required.
fontstring'bold 100px "Helvetica Neue", Arial, sans-serif'CSS font string for mask rasterization.
maskResolutionnumber512Mask rasterization resolution.
heightnumberFixed height in CSS px.

Also accepts class, style, lazy, autoPause, and all FluidConfig props. Defaults to transparent=true.