Skip to content

The seed is a station now, and what it leaves out keeps rolling - #47

Merged
Gamah merged 3 commits into
masterfrom
seed/global-vibe-format
Aug 13, 2026
Merged

The seed is a station now, and what it leaves out keeps rolling#47
Gamah merged 3 commits into
masterfrom
seed/global-vibe-format

Conversation

@Gamah

@Gamah Gamah commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Closes PLAN row 99.

The format

tag:n[:genre][:vibe] — genre and vibe optional and order-free, told apart by length (1 hex char = genre, 36 = vibe).

Absent means rolled, present means pinned. An omitted part is derived deterministically from (tag, n) and so changes with every song; a written-down one is pinned and every song plays it. Genre and vibe roll from separate PRNG streams, so pinning one never moves the other.

seed what it is
gamah:0 a station — genre and vibe both roll
gamah:0:3 metal forever, vibes rolling
gamah:0:8a4c… one vibe through whatever genre each song rolls
gamah:0:3:8a4c… one song, exactly

n keeps its job: Prev/Next are n±1, the queue walks an ordered timeline, and n is what reaches a song fifty ago that nothing remembers.

Malformed is refused with a sentence and changes nothing — typed mid-session the music carries on, arriving from a link the widget stays silent. There is no "apply the part I understood".

The vibe is one global grid

This is the substantive change. The vibe was a per-genre grid whose first char was the genre and whose instrument rows meant different things per genre — a pinned vibe rolling across genres would have been nonsense.

It is now 9 voices × 4 columns = 36 hex chars, the same width and the same meaning in every genre, whether or not that genre plays the instrument. Identity is the voice, not the label: ska's LEAD is MELODY and pop's LEAD is LEAD GTR; pop's SYNTH is rock's KEYS.

Ranges are global too. The per-genre floor under each drive knob already lives in Guitar.cs / Lead.cs / Keys.cs, and two places to hold a genre's character is one too many. The wire carries a normalised level, so the same digit is the same fraction of travel everywhere.

Genre swapping mid-song keeps the vibe — the same 36 knobs through a different band, which is what reaching for the dropdown mid-song is asking for.

Parsing lives in Engine/SeedCodec.cs and the web asks the wasm for it rather than keeping a second parser.

shuffle

Stops meaning "a different vibe every song" — a seed with nothing pinned already does that, and it needs no attribute. It now decides what next means: the next song of this station (off, the new default) or a whole new station at song 0 (on). Those stations are derived from the root, so Prev still replays what was heard and a reload does not lose the line.

UI

Genre dropdown gains Random; knobs collapse behind a tinker button and have their own "random vibe" way back out (a knob drag pins the whole grid, so there has to be one); reroll is a fresh station rather than a fresh taste; two copy buttons — this song fully resolved, or the station as it stands; refusals appear inline under the seed box.

Verified here

make test-engine 556 checks, all six node suites, test/dist-single.mjs, and check-bundle against a fresh AOT re-stage (committed). Render digests re-blessed — audio changes, which the plan allows.

Not verified here: anything s&box. SkafinityPlayer.cs and the Razor panel are ported by review and grep only — they compile against the new API as far as that can tell, and the panel's UI (collapsed knobs, Random genre, two copy affordances, seed-only reroll) is deliberately row 98.

Also updates rows 98 and 12, which referenced 99.

Gamah added 3 commits August 13, 2026 01:26
`tag:n[:genre][:vibe]`. Absent means rolled — an omitted genre or vibe is
derived from (tag, n) and changes with every song — and present means pinned,
so you can pin a vibe and let genres roll, pin a genre and let vibes roll, or
pin both and move only n. The two roll from separate streams, so pinning one
never moves the other. Malformed is an error with a sentence attached, never a
coerced string: a seed that quietly becomes a different seed is worse than one
that is refused, because the person who sent it never finds out.

The significant part is the vibe. It was a per-genre grid whose first char was
the genre and whose instrument rows meant different things in different genres,
which made a pinned vibe nonsense the moment the genre changed under it. It is
now ONE global grid — nine voices x four columns, 36 hex chars, the same width
and the same meaning in every genre, whether or not that genre plays the
instrument. A genre chooses which cells it shows as sliders and what to call
them, and nothing else; the ranges are global too, because the per-genre floor
under each drive knob already lives in Guitar.cs, Lead.cs and Keys.cs, and two
places to put a genre's character is one too many.

Genre swapping mid-song therefore keeps the vibe: the same 36 knobs through a
different band, which is what reaching for the dropdown is asking for.

The parser is SeedCodec, in the engine, and the web asks the wasm for it rather
than keeping a second one — a seed is exactly the thing two people have to
agree about.

`shuffle` stops meaning "a different vibe every song" (a seed with nothing
pinned already does that, and it needs no attribute) and starts meaning what
NEXT is: the next song of this station, or a whole new station at song 0. Those
stations are derived from the root rather than drawn fresh, so Prev still
replays what was heard and a reload does not lose the line.

UI: the genre dropdown gains Random, the knobs collapse behind a tinker button
with their own way back to rolling, reroll is a fresh station rather than a
fresh taste, and there are two copy buttons — this song, or this station.

Old seeds break, as the plan says they may. The s&box driver compiles and
drives the new format; its panel's UI follows in row 98.
Three of the four buttons over the sliders were never about the sliders: genre,
reroll and shuffle change the seed, and tinker only opens the box. Sitting in
the panel header they read as part of the mixer and vanished for everyone who
never opened it. They move to a row of their own on the board, and the panel is
now only the matrix and the two buttons that act on it.

The die in there also did not do what a die on a mixer is expected to do. It
was an UNPIN — hand the vibe back to the station — which moves nothing at all
when nothing was pinned, so pressing it mostly looked broken. There are now
two: `🎲 randomize` draws a fresh vibe and pins it, so it moves every slider
every time; `↺ random each song` is the unpin, worded as what it actually
changes (the songs after this one) and disabled when there is no pin to drop.

`controls` had to learn that a section can be more than one node, since `vibe`
is now a strip of buttons and the panel they open. Its own hidden flag and
tinker's now settle in one place, because both were reaching for `hidden` on
the same element.
@Gamah
Gamah merged commit 41ecba1 into master Aug 13, 2026
1 check passed
@Gamah
Gamah deleted the seed/global-vibe-format branch August 13, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant