# FluidSim — Agent Instructions

## Project Overview

FluidSim is a zero-dependency, real-time fluid dynamics simulation that runs in the browser using WebGL 1.0. It solves a GPU-based approximation of the Navier-Stokes equations via GLSL fragment shaders and renders colourful dye driven by mouse/touch input.

## Architecture

- **No build step.** Plain HTML, CSS, and vanilla JS served statically.
- **No external libraries.** All WebGL and shader code is hand-written.
- **Single entry point:** `js/fluid.js` is an async IIFE that loads shaders via `fetch()`, compiles them, creates framebuffers, and runs the simulation loop.
- **Shaders** live in individual `.frag` / `.vert` files under `shaders/` and are fetched at runtime.
- **Double-buffered FBOs** (ping-pong pattern) keep read and write textures separate each frame.

## Key Conventions

- **WebGL 1.0 only.** Do not use WebGL 2 APIs. Rely on `OES_texture_half_float` / `OES_texture_float` extensions.
- **`mediump` precision** is the default in fragment shaders for mobile compatibility. Use `highp` only where numerically required.
- **No transpiler or bundler.** Code must run directly in modern evergreen browsers. Use ES5-compatible syntax in `fluid.js` (`var`, function expressions, no arrow functions, no `class`, no template literals).
- **CSS is minimal.** Full-screen canvas with `touch-action: none`, no scrollbars, black background.
- **Configuration** is done via constants at the top of `fluid.js` (e.g. `SIM_RESOLUTION`, `DYE_RESOLUTION`, `VISCOSITY`). Do not introduce a config file or UI panel unless explicitly requested.

## Simulation Pipeline (per frame)

1. Curl → 2. Vorticity confinement → 3. Self-advection of velocity → 4. Divergence → 5. Pressure solve (Jacobi, 30 iterations) → 6. Gradient subtraction → 7. Dye advection → 8. Display

Each step uses a dedicated fragment shader, a shared full-screen quad vertex shader, and the `blit()` helper to render into the appropriate framebuffer.

## File Responsibilities

| File | Role |
|---|---|
| `index.html` | Minimal markup: viewport meta, canvas, CSS link, script tag |
| `css/style.css` | Full-bleed canvas styling, reset, responsive |
| `js/fluid.js` | All simulation logic: WebGL init, shader compilation, FBO management, input handling, render loop |
| `shaders/baseVertex.vert` | Shared vertex shader for full-screen quad |
| `shaders/advection.frag` | Advects velocity and dye fields |
| `shaders/curl.frag` | Computes velocity curl |
| `shaders/vorticity.frag` | Applies vorticity confinement force |
| `shaders/splat.frag` | Gaussian splat of colour + velocity at interaction point |
| `shaders/divergence.frag` | Computes velocity divergence |
| `shaders/pressure.frag` | Jacobi pressure iteration |
| `shaders/gradient.frag` | Subtracts pressure gradient from velocity |
| `shaders/display.frag` | Maps dye texture to screen output |

## Guidelines for Changes

- **Keep it simple.** This project intentionally has no abstractions, no module system, and no framework. Don't introduce them.
- **Shader changes** should be made in the individual `.frag` / `.vert` files, not inlined in JS.
- **New simulation features** (e.g. obstacles, bloom, colour modes) should follow the existing pattern: add a new shader file, create a program in `fluid.js`, and wire it into the pipeline.
- **Input handling** uses the `PointerEvent` API with fallbacks for separate mouse/touch events. Maintain this progressive-enhancement approach.
- **Resize handling** is debounced. FBOs are destroyed and recreated on resize — keep this pattern to avoid stale texture references.
- **Performance matters.** The target is 60 fps on desktop and 30 fps on mid-range mobile. Avoid unnecessary draw calls, texture binds, or heavy JS per frame.
- **Test changes** by serving the project locally (e.g. `python -m http.server`) and interacting in a browser. There is no automated test suite.

## Common Tasks

- **Tuning visuals:** Adjust constants at the top of `fluid.js` (`SPLAT_RADIUS`, `CURL_STRENGTH`, `VISCOSITY`, etc.) or modify `display.frag` for colour mapping.
- **Adding a post-process pass:** Create a new shader, compile it, add an FBO if needed, and insert the draw call after `render()` in the main loop.
- **Improving mobile performance:** Lower `SIM_RESOLUTION` / `DYE_RESOLUTION` or reduce `PRESSURE_ITERATIONS`.
