# Scale and root selection 2026-10-11. Status: draft for review. ## Goal Let Gee lock the line (bass, melody, pad) to a chosen root and scale, so Seedbox can play along with an existing musical idea. The seed still makes everything else. ## Decisions | Question | Answer | |---|---| | Scope | Global and sticky. SEED, ROLL and loading a slot don't change it. Slots don't store it. | | Pages | Two: ROOT and SCALE, each with AUTO. | | AUTO | The seed picks from all 21 scales and 12 roots. Every existing seed changes scale (an intended music change). | | Old data | None kept. It's a prototype, so the flash record starts fresh. | ## Scales The menu order is also the table index. | # | Screen name | Steps | |---|---|---| | 0 | MAJOR | 0 2 4 5 7 9 11 | | 1 | MINOR | 0 2 3 5 7 8 10 | | 2 | DORIAN | 0 2 3 5 7 9 10 | | 3 | MIXOLYDIAN | 0 2 4 5 7 9 10 | | 4 | LYDIAN | 0 2 4 6 7 9 11 | | 5 | PHRYGIAN | 0 1 3 5 7 8 10 | | 6 | HARM MINOR | 0 2 3 5 7 8 11 | | 7 | MINOR PENT | 0 3 5 7 10 | | 8 | MAJOR PENT | 0 2 4 7 9 | | 9 | PHRYG DOM | 0 1 4 5 7 8 10 | | 10 | DBL HARM | 0 1 4 5 7 8 11 | | 11 | HUNG MINOR | 0 2 3 6 7 8 11 | | 12 | UKR DORIAN | 0 2 3 6 7 9 10 | | 13 | LYD DOM | 0 2 4 6 7 9 10 | | 14 | HIRAJOSHI | 0 2 3 7 8 | | 15 | IN SEN | 0 1 5 7 10 | | 16 | IWATO | 0 1 5 6 10 | | 17 | PROMETHEUS | 0 2 4 6 9 10 | | 18 | WHOLE TONE | 0 2 4 6 8 10 | | 19 | OCTATONIC | 0 1 3 4 6 7 9 10 | | 20 | LOCRIAN | 0 1 3 5 6 8 10 | Scales have 5 to 8 notes. Chords stay "every other scale note" (0, 2, 4 from the chord degree), so: - 5-note scales give open chords; - the 6-note whole tone gives augmented chords; - the 8-note octatonic gives diminished chords. ## Engine (`sim/engine.js`, then `firmware/engine/engine.c`) - `SCALES` and `SCALE_NAMES` become the 21-entry table above. In C, `SCALES[21][8]` and `SCALE_LEN[21]`. - `genome()`: `g.scale = r.below(21)`. It's still one draw, so every later draw in the genome is unchanged. - The params get `scale` (-1 = AUTO, 0..20) and `root` (-1 = AUTO, 0..11). `renderBar` overrides `g.scale` and `g.root` when they're ≥ 0, right after `genome()`, the same way AMENIZER overrides `steps`. In C this is `sb_params.scale` and `sb_params.root`. - `describe()` and the simulator's "Key" fact show the effective scale and root. - Integer-only and deterministic, as before. ## Vectors - Run `npm run vectors` (an intended music change; the commit message says so). - Add cases to `test/cases.js` with a fixed scale and root, so the C port is tested on overrides. Cover at least: - a 5-note scale; - the 8-note OCTATONIC; - a root that isn't C; - AUTO. ## Panel (firmware `app/main.c`, simulator `app.js`) - Pages: SEED · GEN · ROOT · SCALE · BPM · SLOT · ROLL · LEARN. - ROOT turns through AUTO, C, C#, …, B. SCALE turns through AUTO, MAJOR, …, LOCRIAN. Edit them like GEN: push, turn, push. - Screen when set: label `ROOT` / `SCALE`, value `D` / `HIRAJOSHI`. - Screen on AUTO: label `ROOT AUTO` / `SCALE AUTO`, value is what the seed picked. - A change is heard from the next rendered bar, the same as GEN. ## Storage - `live_t` gains `scale` and `root`, saved in flash. The record version goes to 2. Old records are ignored and defaults load. - The leftovers from BANK go: the `unused` record field and `pad_t.spare`. - Defaults: both AUTO. - The simulator keeps them in localStorage next to `gen`. ## Docs - `docs/controls.md`: add ROOT and SCALE to the pages table. - `docs/concept.md`: the scale list. - `docs/decisions.md`: add one line on global sticky key, AUTO over all 21. ## Testing 1. `npm test` passes with the new vectors. 2. `cd firmware/test && make test` passes. 3. Bench checks: - set ROOT D and SCALE HIRAJOSHI, and check the MiniFreak notes stay in D Hirajoshi across several SEEDs; - check that AUTO shows the seed's own pick.