--- title: "GLSL vs TSL" description: "The same fluid concepts in two render pipelines: WebGL EffectComposer passes and WebGPU RenderPipeline nodes." section: "Pipeline choice" order: 4 badge: "WebGL and WebGPU" --- import Callout from '../../components/tutorials/Callout.astro' import FlowDiagram from '../../components/tutorials/FlowDiagram.astro' import ParameterTable from '../../components/tutorials/ParameterTable.astro' The library has two public entries because three.js now has two practical rendering paths. ```ts import { FluidSimulation } from 'three-fluid-fx' // WebGL / GLSL import { FluidSimulation } from 'three-fluid-fx/tsl' // WebGPU / TSL ``` The solver surface is intentionally similar. The composition layer is what changes. ## WebGL / GLSL Use the default `three-fluid-fx` entry when your project uses `WebGLRenderer`, `EffectComposer`, and post-processing passes. ```ts import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js' import { RenderPass } from 'three/addons/postprocessing/RenderPass.js' import { OutputPass } from 'three/addons/postprocessing/OutputPass.js' import { FluidSimulation, OilOverlayPass } from 'three-fluid-fx' const fluid = new FluidSimulation(renderer) const overlay = new OilOverlayPass(fluid) const composer = new EffectComposer(renderer) composer.addPass(new RenderPass(scene, camera)) composer.addPass(overlay) composer.addPass(new OutputPass()) renderer.setAnimationLoop(() => { fluid.step(1 / 60) overlay.time = clock.getElapsed() composer.render() }) ``` Use this path when: - You already have a WebGL post-processing stack. - You want drop-in passes. - You need the widest browser compatibility. ## WebGPU / TSL Use `three-fluid-fx/tsl` when your project uses `WebGPURenderer`, `RenderPipeline`, and TSL node graphs. ```ts import { RenderPipeline, WebGPURenderer } from 'three/webgpu' import { pass, uniform } from 'three/tsl' import { FluidSimulation, oilOverlay } from 'three-fluid-fx/tsl' const renderer = new WebGPURenderer({ antialias: true, forceWebGL: false }) await renderer.init() const fluid = new FluidSimulation(renderer) const sceneNode = pass(scene, camera) const time = uniform(0) const pipeline = new RenderPipeline(renderer) pipeline.outputNode = oilOverlay( sceneNode, fluid.densityNode, fluid.dyeNode, fluid.velocityNode, { time }, ) renderer.setAnimationLoop(() => { time.value = clock.getElapsed() fluid.step(1 / 60) pipeline.render() }) ``` Use this path when: - You are already building with WebGPU and TSL. - You want composition as a node graph. - Your target browser/runtime has WebGPU. ## Concept mapping ## Style switching In WebGL, create pass instances and toggle `enabled`. ```ts const passes = { oil: new OilOverlayPass(fluid), smoke: new SmokeOverlayPass(fluid), } for (const pass of Object.values(passes)) composer.addPass(pass) function setStyle(style: keyof typeof passes) { for (const pass of Object.values(passes)) pass.enabled = false passes[style].enabled = true } ``` In TSL, rebuild the output node when the selected style changes. ```ts function setStyle(style: FluidOverlayStyle) { setPipelineOutput( pipeline, fluidOverlay(style, sceneNode, fluid.densityNode, fluid.dyeNode, fluid.velocityNode, options), ) } ``` ## Parameter semantics stay the same The visual tuning words do not change between GLSL and TSL: - `splatRadius` is still brush size. - `splatForce` is still pointer force. - `curlStrength` still adds vortex energy when `enableVorticity` is on. - `densityDissipation` still controls the mask lifetime. - `dyeDissipation` still controls colored stroke lifetime. - `intensity`, `vibrance`, and `time` still belong to the visual effect. Explain each effect once by visual task and parameter semantics. Then show the small GLSL/TSL API shape difference in this mapping page.