# Grain field implementation

Built from `prompt.txt`, the supplied manifest and the permitted Forest/Water audio files. No other student apps, source projects, slides or history were inspected.

## What is here

- `index.html`, `styles.css` and `app.js` run without a build step or external dependencies.
- Four supplied 20-second loops are available. Forest after rain is the default. A local audio-file picker provides a fallback when a browser blocks fetching audio from a directly opened HTML file.
- Play creates/resumes Web Audio after the user's click. Stop fades the master output and stops scheduled sources. Audio also stops when the page is hidden.
- Grain length, density, playback rate, recording position, position spread and output level have labelled sliders with numeric values.
- Every grain uses a Hann amplitude envelope. Overlap-dependent gain, a 35% default master level and a final compressor restrain the output. This is not a calibrated loudness system.
- Gentle cloud and Scattered set contrasting grain parameters. A slow optional position drift works inside the chosen spread. Each grain also receives a random stereo position.
- Grain cloud and Original loop are exclusive comparison modes with short crossfades. The original plays at its recorded speed; the grain controls affect the cloud. Both use the same level control.
- The canvas shows the decoded recording waveform, selected position/spread, grain events and original playback position. It is a visual guide, not an audio meter.

## Checks performed

- `node --check app.js` passed.
- Checked unique HTML IDs, slider label targets, the script reference and existence of all four audio files.
- Read through scheduler timing, envelope endpoints, stop behavior and node cleanup.
- Browser testing, actual audio playback and visual QA are assigned to the parent agent. I did not launch a server or browser.

## Limits

- Native AudioBufferSourceNode grains use a short look-ahead timer. Browsers can suspend background processing; this page stops on hiding to avoid an accumulated burst.
- Position spread wraps around the recording ends. A grain that crosses the source loop boundary can reveal a discontinuity in that recording even though its outer edges fade.
- Rate changes pitch and speed together. There is no pitch-preserving time stretching, export, recording or saved session.
- Subjective loudness and perceived grain quality still need listening checks on the target browser and speakers/headphones.

## First complete attempt

`attempts/001/` preserves the first complete HTML/CSS/JavaScript copy. The current version adds a keyboard-accessible file-picker button, ignores stale loading responses, prevents duplicate Play clicks during loading and fixes the original playback cursor after comparison-mode switches.
