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.
| Parameter | Type | Description |
|---|---|---|
x | number | X position in normalized coords (0 = left, 1 = right) |
y | number | Y position in normalized coords (0 = bottom, 1 = top) |
dx | number | X velocity (raw value, not scaled by splatForce) |
dy | number | Y velocity (raw value, not scaled by splatForce) |
color | RGB | Color 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.
| Parameter | Type | Description |
|---|---|---|
count | number | Number 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:
| Context | Range | Notes |
|---|---|---|
backColor prop | 0-255 | CSS-style. Normalized internally before reaching the shader. |
FluidHandle.splat() color | 0-1 | Linear. Values above 1 are valid HDR and create bloom highlights. |
PresetSplat.color | 0-1 | Linear. Same convention as splat(). |
revealCoverColor, revealAccentColor, revealFringeColor | 0-1 | Linear. |
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.
| Field | Type | Description |
|---|---|---|
x | number | X position, 0-1 (left to right) |
y | number | Y position, 0-1 (bottom to top) |
dx | number | X velocity (raw, not scaled by splatForce) |
dy | number | Y velocity (raw, not scaled by splatForce) |
color | RGB | Color 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.
| Field | Type | Description |
|---|---|---|
d | string? | SVG path data (path mode). Takes precedence over text. |
text | string? | Text to rasterize (text mode). Centered in the mask. |
font | string? | 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 }. |
scale | number? | 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.