--- 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 ```