API Reference

Imperative control via FluidHandle.

While the <Fluid> component is primarily declarative (props in, simulation out), sometimes you need imperative control: injecting splats from application logic, pausing the simulation in response to an event, or querying the engine state. The FluidHandle interface provides this.

Getting the Handle

Use Svelte's bind:this to capture a reference to the component instance. The handle property exposes the imperative API.

<script lang="ts">
  import { Fluid } from 'svelte-fluid';
  import type { FluidHandle } from 'svelte-fluid';

  let fluidRef: { handle: FluidHandle };
</script>

<Fluid bind:this={fluidRef} />

<button onclick={() => fluidRef.handle.randomSplats(5)}>
  Add splats
</button>

The same pattern works with presets — every preset component exposes the same handle property:

<script lang="ts">
  import { LavaLamp } from 'svelte-fluid';
  import type { FluidHandle } from 'svelte-fluid';

  let lampRef: { handle: FluidHandle };
</script>

<LavaLamp bind:this={lampRef} />

FluidHandle Methods

splat(x, y, dx, dy, color)

Inject a single splat at the given position with velocity and color.

ParameterTypeDescription
xnumberX position in normalized coords (0 = left, 1 = right)
ynumberY position in normalized coords (0 = bottom, 1 = top)
dxnumberX velocity (raw value, not scaled by splatForce)
dynumberY velocity (raw value, not scaled by splatForce)
colorRGBColor in 0-1 linear range. Values above 1 are valid HDR and appear as bloom highlights.
// Inject a bright red splat at the center moving rightward
fluidRef.handle.splat(0.5, 0.5, 500, 0, { r: 1.5, g: 0.1, b: 0.1 });

Velocity values are in pixels per frame and written directly to the splat shader uniform. Unlike pointer-driven splats, these are not multiplied by splatForce. Pick the absolute magnitude you want. Typical values range from 100 (gentle) to 1000 (forceful).

randomSplats(count)

Push N additional random splats onto the splat queue. Colors are generated by the engine's internal generateColor function.

ParameterTypeDescription
countnumberNumber of random splats to inject
// Add 10 random splats
fluidRef.handle.randomSplats(10);

pause()

Stop the animation loop. The WebGL context stays alive but no frames are requested. Call resume() to restart. Idempotent — calling pause() on an already-paused engine is a no-op.

fluidRef.handle.pause();

resume()

Restart the animation loop after a pause(). Idempotent — calling resume() on an already-running engine is a no-op.

fluidRef.handle.resume();

isPaused

Readonly boolean property indicating whether the engine's animation loop is currently paused.

if (fluidRef.handle.isPaused) {
  fluidRef.handle.resume();
}

Types

RGB

A simple color triple with r, g, b number fields. The unit convention depends on context:

ContextRangeNotes
backColor prop0-255CSS-style. Normalized internally before reaching the shader.
FluidHandle.splat() color0-1Linear. Values above 1 are valid HDR and create bloom highlights.
PresetSplat.color0-1Linear. Same convention as splat().
revealCoverColor, revealAccentColor, revealFringeColor0-1Linear.
interface RGB {
  r: number;
  g: number;
  b: number;
}

PresetSplat

Declarative initial splat consumed once at engine construction. Used by preset wrappers to paint a deterministic opening scene.

FieldTypeDescription
xnumberX position, 0-1 (left to right)
ynumberY position, 0-1 (bottom to top)
dxnumberX velocity (raw, not scaled by splatForce)
dynumberY velocity (raw, not scaled by splatForce)
colorRGBColor in 0-1 linear range (HDR values above 1 are valid)
interface PresetSplat {
  x: number;
  y: number;
  dx: number;
  dy: number;
  color: RGB;
}

Obstruction

An interior obstacle the fluid flows around, passed via the obstructions prop. Reuses the same path/text descriptor as the svgPath container variant — the filled region marks where fluid is blocked. All obstructions union into one combined mask; the allowed region is container × (1 − obstruction). Orthogonal to containerShape. See Container Shapes and ADR-0034.

FieldTypeDescription
dstring?SVG path data (path mode). Takes precedence over text.
textstring?Text to rasterize (text mode). Centered in the mask.
fontstring?CSS font for text mode. Default 'bold 72px sans-serif'.
viewBox[number, number, number, number]?viewBox for path mode. Default [0, 0, 100, 100].
fillRule'nonzero' | 'evenodd'?Fill rule for path mode. Default 'nonzero'.
offset{ x: number; y: number }?UV-space translation after the fit transform. Default { x: 0, y: 0 }.
scalenumber?Multiplier on the fit scale. Default 1.
fit'contain' | 'fill'?viewBox→canvas mapping. 'contain' (default) uniform-fits + centers (letterboxes); 'fill' stretches to fill at any aspect (for maze/nozzle channels). Path mode only.
interface Obstruction {
  d?: string;
  text?: string;
  font?: string;
  viewBox?: [number, number, number, number];
  fillRule?: 'nonzero' | 'evenodd';
  offset?: { x: number; y: number };
  scale?: number;
  fit?: 'contain' | 'fill';
}

FlowConfig

Higher-level scene description for believable presets. It is additive: omitting flow keeps legacy splat behavior unchanged. FluidReveal and FluidDistortion ignore flow visualization because their display shaders are authoritative. When set, flow.boundary overrides individual canvas edges; legacy openBoundary is only the fallback for unspecified edges.

interface FlowConfig {
  mode?: 'live' | 'prescribed' | 'hybrid';
  boundary?: { left?: 'wall'|'open'; right?: 'wall'|'open'; top?: 'wall'|'open'; bottom?: 'wall'|'open' };
  sources?: FlowSource[];
  outlets?: FlowOutlet[];
  scalarFields?: FlowScalarField[];
  forces?: FlowForce[];
  prescribed?: PrescribedFlowField;
  visualization?: FlowVisualization;
}

Use FlowSource for persistent point/line/rect emitters, FlowOutlet for edge sponge/drain zones, FlowScalarField for temperature or ink-like quantities, FlowForce for gravity, pressure-gradient body force, or buoyancy, and PrescribedFlowField for custom uploaded flow grids. In mode: 'prescribed', source velocity and forces are ignored so the velocity remains exactly prescribed; use 'hybrid' when you want to add live velocity before projection. Outlets drain fields near an edge; use flow.boundary to decide which edges are actual pressure-open boundaries.

interface FlowScalarField {
  name: string;
  dissipation?: number;
  advection?: 'standard' | 'low-dissipation';
  color?: RGB;
	range?: [number, number];
}

type Vec2 = { x: number; y: number };

type FlowForce =
  | { kind: 'gravity'; vector: Vec2 }
  | { kind: 'pressureGradient'; vector: Vec2 }
  | { kind: 'buoyancy'; scalar: string; direction?: Vec2; strength: number; ambient?: number };

interface FlowVisualization {
  colorBy?: 'dye' | 'speed' | 'pressure' | 'temperature' | 'scalar';
  scalar?: string;
  glowBy?: 'speed' | 'scalar' | 'none';
  transfer?: 'fire' | 'water' | 'ink' | 'viridis' | 'cfd';
  range?: [number, number];
  scale?: number;
}

interface FlowGridField {
  width: number;
  height: number;
  data: ArrayLike<number>;
  scale?: number;
  version?: number | string;
}

low-dissipation reduces explicit scalar fading; it is not a MacCormack/BFECC advection correction. For uploaded grids, replace immutable data objects or bump version when values change so WebGL textures are re-uploaded. For velocity and pressure plots, FlowVisualization.range maps raw field values onto the selected transfer function; transfer: 'cfd' provides a blue→cyan→green→yellow→red CFD-style magnitude ramp.


FluidEngine (Advanced)

For advanced users who need framework-agnostic fluid simulation, the FluidEngine class is available as a direct import. It is the WebGL engine that powers the <Fluid> component, but it has no Svelte dependency.

import { FluidEngine } from 'svelte-fluid/engine/FluidEngine';

The engine is constructed with a canvas element and a config object matching the FluidConfig interface. It exposes the same splat(), randomSplats(), pause(), resume(), and isPaused API as FluidHandle, plus additional methods:

  • setConfig(config) — update configuration at runtime (respects the 4-bucket system)
  • dispose() — free all GPU resources (textures, framebuffers, programs). The WebGL context is left intact for potential reuse.

Architecture invariant: the engine never imports Svelte. It is fully framework-agnostic and could be used with React, vanilla JS, or any other framework. The Svelte component is a thin wrapper that handles lifecycle, sizing, and prop reactivity.