# Spectral Terrain — "Reuse Effect" case study

Pages (after `npm ci && npm run build && npm run preview` → http://localhost:4190/, or any static server on `dist/`):
- `index.html` — A/B. **A: independent implementation; no upstream source files incorporated** (WebGL2). **B: adapted Apache-2.0 FFT components** (WebGPU).
- `controlled.html` — **B-control** (Gerstner only) vs **B-FFT** (Gerstner + adapted WaveCascade), identical presentation, plus the "What this demonstrates / does not demonstrate" panel and recorded metrics.

Query params: `mode=fft|control` (pane.html), `capture=1` (offscreen render + readback, deterministic; needed headless), `t=<s>` (freeze sim time/camera), `frames=<n>`. The controlled live page sends one same-origin clock to both panes; Pause/Resume/Reset are available above the panes. No other/debug params exist.
WebGPU missing → visible red UNAVAILABLE/FAIL box; no fallback rendering.

The optional image-metrics script needs Python 3 with NumPy and Pillow. Install
them in a local environment with `python3 -m venv .venv` and
`.venv/bin/python -m pip install -r requirements-capture.txt`, then use
`.venv/bin/python metrics.py` in place of `python3 metrics.py` below.

Each capture uses a fresh directory (a timestamped directory by default).
Existing non-empty output directories are rejected to preserve evidence and
prevent a failed rerun from leaving stale PASS files. Set `METRICS_DIR` to the
capture directory when calculating its metrics.

Reproduce a new capture without touching the frozen supplied evidence: `npm run build && npm run preview &` then choose one output directory and use it for both commands, for example `OUT=capture-output/run-$(date +%Y%m%d-%H%M%S); CAPTURE_OUT="$OUT" node capture.mjs http://localhost:4190/ && METRICS_DIR="$OUT" python3 metrics.py`. Set `BROWSER_PATH` or `CHROME_PATH` when Chrome is not in a standard location. Linux defaults to SwiftShader; macOS and Windows default to the detected hardware WebGPU path. Set `CAPTURE_WEBGPU=hardware` or `CAPTURE_WEBGPU=swiftshader` explicitly when supported. Both commands return nonzero on failed captures, missing/stale repeat images, shape mismatches, or a failed negative control. `node capture.mjs --help` documents the options. `node screenshot.mjs` makes `screenshot-controlled.png` and accepts the same browser-path environment variables.
Run `npm run manifest` after source changes, then `npm run verify` for the production build, syntax checks, and manifest check. Build output copies `EVIDENCE.md`, `EVIDENCE-supplied.md`, `README.md`, the Apache license, upstream NOTICE, `THREE-MIT-LICENSE.txt`, `source-manifest.json`, and `THIRD_PARTY_LICENSES/three-LICENSE.txt` into `dist/`.
See **EVIDENCE.md** for upstream commit, hashes, diffs, environment, metrics and limits. `EVIDENCE-supplied.md` preserves the prior supplied note verbatim.

## Third-party code / Apache-2.0 notice
Derived from https://github.com/AdemVessell/arsenal-first-drop @ 36ec504782b2bda8ec16e678419f14de4da1da1f (Apache License 2.0; `LICENSE-APACHE-2.0.txt`, upstream `NOTICE-upstream.txt`):
- `src/b/vendor/WaveCascade.js` — MODIFIED (optional power-law spectrum `cfg.powerLaw`, `cfg.morphRate`).
- `src/b/vendor/gerstner.js` — MODIFIED excerpt of `gerstner()` from `the-cove/src/water/WaterMaterial.js`.
Not affiliated with or endorsed by the upstream project; no upstream names, logos, screenshots or media are used in the artwork. Three.js is MIT.
