# Lantern Jam — an actual trained music partner

Lantern Jam is a four-phrase XOR instrument. It has no timer and no losing finish. Any legal sequence of four choices becomes a song you can hear, name, and keep. The optional partner suggests pairings according to three explicit rhythm preferences: Softly drifting, A little bounce, and Call & answer.

## What was trained

The installed `pytoolz.wsrf.WSRFRegressor` (8.0.0) was fitted on **24,576 action examples**, with **294,912 complete-song rollouts** used to label them. The model has 60 trees over three mood zones and 19 features. All trees are exported unpruned and evaluated in the browser by the existing full-forest adapter.

Actual `pytoolz.indexrng.Index` streams in `lantern-jam-v1` create separate training, validation, and test seeds. Training visits a mixture of reference and random choices across 288 ordinary songs and 96 songs with changing moods. Each legal pairing is labeled by 12 continuations: later moves use a reference choice 70% of the time and a random choice 30% of the time. The target is the resulting mean preference score.

The preference score is an explicit **game-design rubric**: beat density near the chosen mood, an opening beat, a final beat, repetition or variety appropriate to the mood, and a return to the opening rhythm at the end. It is not a listener rating or a claim of objectively good music. The precise formula is in `model.js`.

The lantern combination uses the four associations already learned with actual `pytoolz.wsrf_hlib.ParityDetector` for Glow Pair Post. Those associations passed all 64 held-out card pairs on every output light. This game reuses those learned results; it does not claim a second hlib fit. Library and source receipts are retained in both games’ reports.

## Independent results

| Player | Mean preference score / 100 | Fresh songs |
| --- | ---: | ---: |
| WSRF partner | 90.50 | 192 |
| Explicit reference | 90.91 | 192 |
| Random legal choices | 63.03 | 192 |
| WSRF with changing moods | 90.09 | 96 |

The first three use the same 192 fresh song seeds. The changing-mood set is separate. Model architecture was fixed before these tests; it was not selected on test results. All legal songs finish, so completion rate alone is not evidence of learning.

A 48-song validation collection uses 24 rollouts per candidate; value MAE is 2.3522 preference-score points. The browser matches Python on **168,699 prediction fixtures**, including float32 neighbors around every split threshold, with maximum absolute error 0.

## What teaching means here

The two teaching ideas are distinct rhythm patterns from actual WSRF-ranked candidates. Hear either without changing the song, then choose one. That phrase becomes part of the next prediction’s history. You can also change the mood, undo, or play manually.

The deployed trees stay fixed. Teaching directs the performance and changes the context; it does not secretly retrain the forest in your browser. The UI distinguishes model suggestions, your manual phrases, and your teaching choices. Saved songs preserve those choices and reconstruct their actual notes.

## Playback and persistence

The four XOR windows become four beats. Notes follow a fixed C/F/G/C-style root movement, transposed by the song’s seed. Bell or plucked synthesis, three tempos, volume, and an explicit Stop control are provided. Sound starts only after a listening action and stops on blur, page hiding, or leaving the page. All notes remain visible without audio.

A completed song can be saved to the discoveries shelf, played again, exported in a validated JSON archive, or saved as a PNG score card. A game night finishes with such a song. Saved records are legal replays, not screenshots passed off as state.

## Reproduce

```sh
/home/wiliamrocha/.spenv/bin/python -B tools/jam/train.py
node tools/test-jam.cjs
node tools/test-discoveries.cjs
ARCADE_URL=http://127.0.0.1:8016/ CDP_PORT=9236 node tools/test-jam-browser.cjs
```

See [training/report.json](training/report.json) for model parameters, versions, exact seeds, every evaluation, source hashes, and the installed WSRF source path. No upstream library was changed. The static deployment needs only the exported data and browser code.
