ska + infinity — an endless, deterministic procedural ska / reggae-rock track that's
generated entirely in your browser from a short shareable seed. No server, no audio
assets: the music is synthesised from scratch in C# (compiled to WebAssembly) and
scheduled through the Web Audio API. The whole song is a URL — …/web/#vibe:tag:n.
The engine is the same MusicGen.cs + VibeCodec.cs the Rotaliate s&box music
library ships (sbox-library/). The web toy compiles that shared source to WebAssembly
with the .NET wasm-tools workload — no port, so the game and the web run identical
composition code. reference/ keeps the original C# for context (see CLAUDE.md).
One-time toolchain (machine, not vendored):
sudo apt-get install -y dotnet-sdk-10.0
dotnet workload install wasm-toolsThen:
make # publish the engine → web/_framework (AOT; ~2 min)
make dev # same but skip AOT — much faster to build, identical composition
make test # node smoke test of the JS↔wasm boundary (needs web/_framework/)
make serve # static server rooted at web/; open http://localhost:8000/The page must be served (the .NET runtime is a bundle fetched over http) — opening
web/index.htmlviafile://won't work.web/is self-contained (it includesweb/_framework), so deploy it by pointing any static server's docroot straight atweb/.web/_frameworkis committed so a fresh clone is testable with justmake serve; rebuild it withmake. A single self-contained.html(inlining the runtime) is a deferred follow-up —make dist.
| Path | Role |
|---|---|
sbox-library/Skafinity/Code/MusicGen.cs |
Composition (fixed RNG draw order) + subtractive synth — the algorithmic source of truth, shared with the s&box library. |
sbox-library/Skafinity/Code/VibeCodec.cs |
Base-36 vibe encode/apply + field metadata (append-only wire format). |
wasm/Exports.cs |
The [JSExport] boundary (generate, vibe codec, WAV, config) — the only web-specific code. |
wasm/Skafinity.Wasm.csproj |
browser-wasm project that <Compile Include>s the shared .cs and builds the runtime. |
web/engine.js |
Boots the .NET runtime and adapts the exports to the small mod API the app uses. |
web/index.html · app.js · worker.js · style.css |
The page: Web Audio crossfade scheduler, rolling playlist, vibe editor, WAV export, shuffle. |
sbox-library/Skafinity/skafinity.config.json · web/config.json |
The shared house-mix config (peak balances / kit presence / stereo-width knobs). Canonical in the library; make copies it to web/. Overlaid at runtime — retune the baseline mix or the width without a rebuild. |
test/smoke.mjs |
Node smoke test that boots the published runtime and exercises every export. |
- Rolling playlist —
nauto-advances on every crossfade and is persisted; a full playlist panel shows played / now-playing / up-next, with click-to-jump and per-song export. - Export to disk — generate the loop as an interleaved-stereo WAV in-browser and download it (no server).
- Share via URL — the seed lives in
location.hash, so a reload or a shared link reproduces the exact same song. - Random every song — a 🎲 toggle that re-rolls the vibe and the genre for each new song (keeping your per-voice volumes), so the stream keeps reinventing itself.
- Stereo image — the mix is laid out across the field, not summed to the centre: hats
left / ride right, toms spread by pitch (rack → left, floor → right), the kit's two crashes
split L/R, and every non-drum voice is double-tracked — two slightly-detuned,
independently-phased takes panned apart for genuine width (not a mono copy on both
channels). Bass stays centred for a tight low end. The
STEREO WIDTHknob scales the whole image from full down to mono, and the double-tracking parameters are config-tunable below. - House-mix config, no rebuild — the baseline peak balances and the stereo-width knobs
live in
web/config.json(copied from the sharedsbox-library/Skafinity/skafinity.config.json); edit + reload to retune the mix or the width without recompiling the wasm. These shape the baseline mix and are not vibe knobs, so they never travel in the seed.
The vibe editor is a matrix: a block of global knobs that shape the whole track, then one
row per instrument, four columns each (volume / tone / character / extra). Every knob is
quantised to one base-36 digit (16 levels) in the seed. The instrument roster — and the two
genre-specific knobs in each row — changes per genre; the field list is read straight from
VibeCodec.cs, so this table is just a readable mirror of it.
Global (all genres): TEMPO MIN / TEMPO MAX (BPM band, 60–200) · TEMPO BIAS (how
often a song lands fast) · SWING (off-beat delay, 0–0.4) · RESONANCE (filter Q, 0.2–2) ·
STEREO WIDTH (master stereo amount — see Stereo image below; 100% = full, 0% = mono).
Every instrument row shares the first two columns: VOLUME (0–150%) and TONE (low-pass
cutoff). DRUMS carries the same four knobs in every genre — its TONE sweeps toms ↔
cymbals, and its character/extra are BUSY (fill/hit density) and DRIVE (timing feel,
pull ↔ push) — but the underlying beat changes per genre, so it's listed in each table below.
The other rows' character/extra columns differ by genre:
Ska — BASS · SKANK · ORGAN · LEAD · HORNS · DRUMS
| Instrument | Character | Extra |
|---|---|---|
| BASS | OCTAVE POP (octave jumps) |
TRIPLETS |
| SKANK | BITE (high-pass) |
CHOP (note length) |
| ORGAN | BUBBLE (chance) |
VIBRATO (depth) |
| LEAD | JUMPINESS (melodic leaps) |
TRIPLETS |
| HORNS | SECTION (full-section chance) |
DENSITY |
| DRUMS | BUSY |
DRIVE — one-drop or stepper groove (backbeat when fast) |
Rock — DRUMS · BASS · KEYS · LEAD GTR · RHYTHM GTR
| Instrument | Character | Extra |
|---|---|---|
| DRUMS | BUSY |
DRIVE — straight backbeat, per-song kick accents |
| BASS | DRIVE (overdrive) |
OCTAVE POP |
| KEYS | DISTORTION |
CHUG |
| LEAD GTR | DISTORTION |
BENDINESS |
| RHYTHM GTR | DISTORTION |
CHUG |
Country — DRUMS · BASS · RHYTHM GTR · KEYS · LEAD GTR
| Instrument | Character | Extra |
|---|---|---|
| DRUMS | BUSY |
DRIVE — train-beat backbeat, per-song kick accents |
| BASS | DRIVE (overdrive) |
OCTAVE POP |
| RHYTHM GTR | DISTORTION |
CHUG |
| KEYS | DISTORTION |
CHUG |
| LEAD GTR | DISTORTION |
BENDINESS |
Same per-instrument columns as Rock, but over a much cleaner base distortion (honky-tonk
piano, strummed open chords, twangy telecaster leads with heavy BENDINESS).
Metal — DRUMS · BASS · RHYTHM GTR · LEAD GTR
| Instrument | Character | Extra |
|---|---|---|
| DRUMS | BUSY |
DRIVE — double-kick gallop |
| BASS | DRIVE (overdrive) |
OCTAVE POP |
| RHYTHM GTR | DISTORTION |
CHUG |
| LEAD GTR | DISTORTION |
BENDINESS |
Same columns again, but a heavy base distortion: palm-muted gallop rhythm (CHUG) and fast
shredding leads.
The straight backbeat (Rock, Country, and fast Ska) picks a per-song kick accent personality — which off-beats the kick leans into beyond beats 1 & 3 — and rolls each accent per bar, so the groove breathes instead of stamping one mechanical pattern every bar.
Same seed ⇒ same song — and because the web toy compiles the same MusicGen.cs as the
s&box library, a seed shared from one plays identically in the other (no port to drift).
make test boots the published runtime under node and checks generation, vibe round-trip,
determinism, and WAV output.