# RCWeb Fluid Simulation

The **RCWeb Fluid Simulation** app (`app/fluid-sim`) is a shared display for collaborative, relaxing color swirls. It pairs with `app/fluid-sim-c`, where many users can touch and drag to inject their own color into a WebGL fluid canvas.

![icon](pwa-512x512.png "Fluid Simulation App Icon")

## Screenshot
![screenshot](screenshot.png "Fluid Simulation App")

## What it does

- **WebGL Fluid Solver**: A GPU velocity and dye simulation creates curls, pressure-driven motion, and soft paint-on-water blooms.
- **Momentum**: Finger velocity is transferred into the display, so fast strokes keep drifting and slow strokes bloom gently.
- **Multi-User Color Trails**: Every controller has its own color, letting several people create distinct swirls on the same screen.
- **Shared Display Flow**: The display shows a QR code to open the controller for the current room.
- **Ambient Mode**: When the display starts, and again after 30 seconds without controller input, it adds slow automatic swirls so the screen never feels empty.
- **OLED-Friendly Presentation**: The base display is true black, with saturated dye colors rendered on top.

## How to use it

1. Open `/fluid-sim/` on the main display.
2. Scan the QR code from one or more phones to open `/fluid-sim-c/` in the same room.
3. Pick a color on each controller and drag on the touch pad.
4. Use slow gestures for calm ink-like blooms, or faster gestures for energetic streaks and curls.

The centered information box appears on startup and returns after 30 seconds of inactivity. It fades when a real controller stroke is received. The QR code remains in the bottom-right corner and fades to 33% opacity after the first user stroke.

## How it works

The controller sends normalized touch segments to `fluidSim.addStroke` using `rc.sendFunctionCall`. The display converts those segments into WebGL splats, disturbs a GPU velocity field, solves pressure to keep the motion fluid-like, and advects dye through the field over time. The graphics pipeline is inspired by Pavel Dobryakov's MIT-licensed WebGL Fluid Simulation.

## Rendering pipeline

- **Velocity Buffer**: Stores two-channel motion in floating-point WebGL textures.
- **Dye Buffer**: Stores the visible color field separately from velocity.
- **Splat Pass**: Adds controller input into both the velocity and dye buffers.
- **Curl and Vorticity Passes**: Reintroduce swirling detail so motion feels fluid rather than linear.
- **Divergence and Pressure Solve**: Projects velocity toward incompressible fluid-like motion.
- **Velocity Stabilization**: Soft-compresses runaway velocity so the simulation does not accelerate into instability over time.
- **Advection**: Moves both dye and velocity through the current flow field.
- **Display Shader**: Renders the dye on a black background while preserving the selected controller hue.

## Network API

The display exposes these functions globally under `fluidSim`:

- `fluidSim.addStroke(player, startX, startY, endX, endY, color, strength)`: Adds a normalized stroke segment from a controller.
- `fluidSim.setAimCursor(client, x, y, color, active)`: Shows or hides a transient remote cursor.
- `fluidSim.broadcastSize()`: Sends the display aspect ratio to connected controllers.
- `fluidSim.clearCanvas()`: Clears simulation buffers. This is retained as an internal API, although the controller no longer exposes a clear button.

## Notes for maintainers

- This app intentionally has no external JavaScript dependency.
- The display requires WebGL with floating-point or half-float render targets. If unsupported, it shows a fallback message.
- Brush size is controlled primarily by `splatRadius` in `webgl-fluid.js`.
- Stability is controlled by velocity dissipation, curl strength, pressure iterations, and the `stabilizeVelocity` shader pass.
- Ambient motion is generated locally by the display and is not broadcast to controllers.
