# Resonant Garden — MVP

A still painterly pond that **awakens through live piano**. Notes make ripples,
loudness brings light, harmony tints the sky, and sustained playing warms the
scene toward dawn.

## Run
```sh
./run.sh            # live mic — the real piece (play to wake the garden)
./run.sh keys       # live MIDI keyboard — play a digital piano in real time
./run.sh keys=1     # ...selecting MIDI input port 1 (default 0; see chuck --probe)
./run.sh midi       # play the bundled MIDI piece (data/demo.mid), no mic needed
./run.sh midi=song.mid   # play your own .mid (project-relative or absolute path)
./run.sh preview    # auto-ripples + forced warm scene, no mic needed
./run.sh full       # fullscreen, for the demo recording
./run.sh capture    # render one frame to /tmp/garden_frame.png, then quit (dev)
./run.sh midi full  # flags combine
```
Needs an **unlocked screen** (ChuGL opens a window). For the live experience also a
**mic / audio input**; `midi` mode needs neither mic nor playing. Raw chuck (the alias
already loads ChuGL): `chuck main.ck:midi`.

## First run
Easiest way to see the whole piece with no setup: **`./run.sh midi`** — it plays a
bundled solo-piano piece and you watch the garden wake from night to dawn on its own.

`./run.sh` (no args) ships **live**: a dark, still, pre-dawn pond that reacts to what
you play into the mic. With no sound it stays night — that's correct. `./run.sh preview`
forces a lit scene with auto-ripples to confirm the graphics. Flags map to
`USE_MIDI` / `DEBUG_VIEW` / `FULLSCREEN` / `CAPTURE` at the top of `main.ck`.

## MIDI input
`midi` mode reads a Standard MIDI File and feeds its note-ons into the **same feature
bus** the mic drives (so the visuals are identical): each note → a ripple (pitch →
position), velocity → brightness/sparkle, the recent notes' pitch-classes → harmony
hue, and cumulative activity → dawn. You also *hear* the piece as the garden reacts: if a
matching **grand-piano render** (`data/<piece>.wav`, made with fluidsynth + the Salamander
Grand Piano soundfont) sits next to the `.mid`, it plays that real piano in sync (and the
Rhodes synth is suppressed); otherwise it falls back to the warm Rhodes voice per note
(`SYNTH_GAIN` tunes it). It loops the file for a continuous demo. Bundled files:
`data/demo.mid`+`data/demo.wav` (Mompou, *Secreto*) and `data/bwv772.mid`+`.wav` (Bach).
Pass any `.mid` with `midi=<path>`; render its piano with:
`fluidsynth -ni -F out.wav -r 44100 SalamanderGrand.sf2 piece.mid && ffmpeg -i out.wav -ac 1 data/piece.wav`.

## Live MIDI keyboard
`keys` mode opens a live MIDI input port and plays the garden in real time from a
digital piano / MIDI keyboard — each note-on drives the **same feature bus** as the
mic and the MIDI-file path (ripple + brightness + harmony + an audible Rhodes voice).
Plug in your keyboard, run `chuck --probe` to find its MIDI-input port number, then
`./run.sh keys` (port 0) or `./run.sh keys=N`. The startup line prints the device that
opened. If no MIDI input is found it falls back to the mic automatically. (Note-offs are
ignored — each note flashes and decays on its own, like the file path.)

## Scene
A painterly dawn pond: a swaying forest treeline, drifting clouds, twinkling stars that
fade at dawn, a **flock of birds** that wheels across the sky in a loose V, a low-poly
**canoe** that drifts and bobs (now **reflected in the water**), and **reeds/cattails**
framing the near shore. The sun rises and warms as the garden wakes.

## What it does (the core features)
- **Ripples** from note onsets; **pitch → position** (low = left/big/slow,
  high = right/small/fast) + size/speed. Pitch is a log-frequency mapping of the
  spectral centroid (see `pitchHeight`).
- **Loudness → brightness** (noise-gated).
- **Silence → desaturated**, playing → color returns.
- **Cumulative energy → dawn** (palette warms over time).
- **Harmony → sky hue** (circle-of-fifths, heavily smoothed).

## Tuning (watch the console monitor: `rms / pitch / harm / dawn / onsets`)
- **`NOISE_FLOOR`** (TUNE #1) — set just *above* your silent-room `rms`. It gates
  **both** brightness **and** onset detection (no ripples without audible signal),
  so if playing produces no ripples/light, lower it; if silence still lights up,
  raise it.
- **`RMS_GAIN`** — raise/lower so playing reads `rms` ~0.3–0.8 on the monitor.
- **`FLUX_FLOOR`** — small floor (default `0.08`) above the adaptive onset
  baseline. Raise if onsets machine-gun on noise; lower if soft notes are missed.
  (The old `0.55` suppressed ~80% of real onsets — flux is bounded ~[0, 1.4].)

## Troubleshooting
- **No window appears:** the GLFW window can open on another macOS *Space* — swipe
  to your other Spaces (Mission Control). Launch from a normal Terminal.
- **Nothing happens when I play:** check the monitor — if `rms` stays ~0, your mic
  isn't reaching ChucK (input/permissions); if `rms` rises but `onsets` doesn't,
  lower `NOISE_FLOOR` / `FLUX_FLOOR`.
- **WGSL/validation error in console:** every declared shader uniform must be used;
  if you add one, reference it (or add `let _ = uniformName;`).

## Files
- `main.ck` — the whole MVP (config + flags, mic + MIDI engines, shaders, scene, loop)
- `audio_test.ck` — headless audio-pipeline check: `chuck --silent audio_test.ck`
  (synthetic SinOsc → onsets fire, pitch rises with frequency, harmony settles)
- `data/demo.mid`, `data/bwv772.mid` — bundled MIDI pieces for `./run.sh midi`
- `data/dot.png` — soft round sprite for the drifting motes
- `run.sh` — launcher (forwards `keys` / `midi` / `preview` / `full` / `capture` as ChucK args)

## MVP verification checklist
- [x] Window opens, no shader errors in console.
- [x] (`preview`) pond ripples + warm scene visible; treeline on the horizon; the
      sun's glitter column sits directly under the sun; motes are round specks.
- [x] (`capture`) renders `/tmp/garden_frame.png` and self-quits.
- [ ] Monitor `rms` ~0 in silence, rises when you play; `onsets` 0 in silence.
- [ ] One ripple per note; **high notes ripple right/small, low notes left/big**.
- [ ] Loud playing brightens the water; silence dims + desaturates it.
- [ ] Sustained playing warms the palette (dawn); sky hue drifts with harmony.
- [ ] Runs minutes with no audio glitch / frame collapse.
