---
title: "Getting Started"
description: "A compact integration path for the solver, pointer splats, outputs, resize handling, and the parameters you actually tune."
section: "Start"
order: 1
badge: "Core API"
---
import Callout from '../../components/tutorials/Callout.astro'
import ExampleEmbed from '../../components/tutorials/ExampleEmbed.astro'
import FlowDiagram from '../../components/tutorials/FlowDiagram.astro'
import ParameterTable from '../../components/tutorials/ParameterTable.astro'
import TextureChannels from '../../components/tutorials/TextureChannels.astro'
`three-fluid-fx` is not a visual preset engine. It gives you a small fluid
field that can be sampled from three.js. The field is usually driven by pointer
movement, then consumed by an overlay, a distortion pass, or your own particle
system.
## The smallest useful integration
Create a renderer, create the fluid simulation, attach pointer splats, and step
the solver in the render loop.
```ts
import { WebGLRenderer, Timer } from 'three'
import { attachPointerSplats, FluidSimulation } from 'three-fluid-fx'
const renderer = new WebGLRenderer({ antialias: true })
const fluid = new FluidSimulation(renderer, {
profile: 'balanced',
splatRadius: 0.001,
splatForce: 6,
})
const detachPointer = attachPointerSplats(renderer.domElement, fluid)
const clock = new Timer()
renderer.setAnimationLoop(() => {
clock.update()
fluid.step(clock.getDelta())
// Render your scene, effect composer, custom shader, or particles here.
})
```
The solver does not render anything by itself. `fluid.step(dt)` updates GPU
textures. Your render path decides how those textures become visible.
## Keep resize explicit
The simulation uses internal render targets. Resize them with the canvas so the
field keeps the same aspect ratio as the viewport.
```ts
function resize() {
const width = stage.clientWidth
const height = stage.clientHeight
const dpr = Math.min(window.devicePixelRatio || 1, 2)
renderer.setPixelRatio(dpr)
renderer.setSize(width, height, false)
fluid.resize(width, height)
}
resize()
window.addEventListener('resize', resize)
```
The profile controls the baseline render-target sizes. `resize()` reshapes
those targets to match the viewport aspect. It does not change the selected
profile.
## What the library gives you
The main output is not a mesh. It is a set of textures in the GLSL pipeline and
matching `TextureNode`s in the TSL pipeline.
For WebGPU/TSL, the names mirror the texture names:
```ts
fluid.velocityNode
fluid.densityNode
fluid.dyeNode
```
Use textures with WebGL materials and post-processing passes. Use nodes with
`WebGPURenderer` and `RenderPipeline`.
## Pointer splats
Pointer splats are small impulses written into the velocity and density fields.
The helper is intentionally thin: it reads `fluid.splatRadius` and
`fluid.splatForce` every pointer event.
```ts
fluid.splatRadius = 0.0012
fluid.splatForce = 8
```
You can also add splats manually. Coordinates are normalized from 0 to 1.
```ts
fluid.addSplat(0.5, 0.5, 120, 40, {
radius: 0.001,
color: [120, 40, 1],
})
```
For colored ink effects, turn on the dye field and write `dyeColor` splats.
```ts
fluid.enableDye = true
attachPointerSplats(renderer.domElement, fluid, {
coloredStrokes: true,
})
```
Use `colorize` when the stroke color should be deterministic instead of random
HSV cycling.
```ts
attachPointerSplats(renderer.domElement, fluid, {
colorize(dx, dy) {
const mag = Math.min(Math.hypot(dx, dy) / 80, 1)
return [Math.abs(dx) * 0.003 * mag, 0.08 * mag, Math.abs(dy) * 0.003 * mag]
},
})
```
## Parameters that change the look
You do not need to teach users the Stable Fluids algorithm. You do need to tell
them which controls make a visual difference.
## Profiles
Profiles pick the texture sizes and the default pressure iteration count at
construction time.
```ts
new FluidSimulation(renderer, { profile: 'performance' }) // weak GPUs, small embeds
new FluidSimulation(renderer, { profile: 'balanced' }) // default
new FluidSimulation(renderer, { profile: 'quality' }) // hero visuals, capture
```
Individual options still override the selected profile.
```ts
const fluid = new FluidSimulation(renderer, {
profile: 'balanced',
pressureIterations: 8,
})
```
## Pick the render path
Use the same solver for every visual family.
```ts
// Overlay: add density, dye, or velocity color over the scene.
scene.rgb + color * density * intensity
// Distortion: use fluid flow to shift scene UVs.
texture2D(tDiffuse, uv - fluid.rg * intensity)
// Particles: sample velocity at each particle screen position.
particleAcceleration += velocityField.xy * flowStrength
```