← Maru Sampler product page
AvailableInstrument

Maru Sampler

The compiled sampler: one kernel, every target.

Browse the sampler banks — every zone map, preset and sample in the format. Create and export your own bank

You are in the live demo. Included formats, release status, and downloads live on the product page.

About

Two samplers existed in this repository: a Web Audio prototype that could read a sample library's map, and a compiled reader that could ship as a plug-in. The repository's invariant settles which one is the product — Web Audio DSP is reference-only and never a production fallback — so the reader is the sampler, and the prototype's selection semantics were ported into it rather than the other way round.

What a sampler mostly does is choose. Which recording answers this key, which layer answers this strike, which take comes up next so a repeated note does not sound mechanical, what happens when the key is released. Those choices are the engine. Putting them in a compiled kernel means the choice a plug-in makes and the choice the browser makes are the same choice, provably, rather than two implementations that agree until they do not.

That proof is the point of the gate: twelve scenarios, five lanes — the Node oracle, the ScriptC LLVM archive, the ScriptC C-emission archive, a repeat run, and the wasm32-wasi reactor — compared byte for byte, with behavioural assertions alongside, because byte parity alone would also be satisfied by a kernel that ignored every feature it claims.

Round robin is deterministic here, which is a deliberate departure from the sampler engines it takes after. A clock- or Math.random-seeded selector cannot pass a byte-parity gate at all, so takes advance from a pure integer generator seeded per group. The randomness is real to a player and reproducible to a test.

Some things are refused rather than approximated. Legato is rejected when a bank declares it, with its own return code, because there is no legato axis in the program model; an out-of-range round-robin take is refused rather than wrapped, because a bank whose 33rd take quietly became take 1 would be a plug-in playing the wrong sample. A missing algorithm keeps its content unsupported; it never authorizes a different implementation.

Presets

Start from a named sound, then adjust the instrument to make it yours.

InitDefaultSoft PadLong TailOctave Down

Signal graph.

Experimental

A sketch of the signal path, in the order of the parameter groups.

How it works.

A bank is a `codex-sound-pack/v1` manifest plus its instrument document and its PCM. The build interns the samples into one contiguous arena and emits a flat zone table; nothing about the map is re-derived from the audio at runtime.

Loading a bank is a non-realtime operation. The host writes the arena, then declares each zone with one `set_zone` call carrying the row's fields verbatim, then plays. The render callback that follows allocates nothing, takes no lock, performs no I/O and calls back into no JavaScript.

Every new zone field is validated with a distinct return code rather than clamped, so a build or ABI mismatch says which fact was wrong instead of quietly playing something else. Loop points are rebased onto the trimmed zone, and a loop that does not survive the rebase is forwarded as no loop — the bank plays as a one-shot rather than being refused outright.

The loop wrap is one integer division rather than a subtract-until-in-range loop. The natural `while` form was correctly rejected by ScriptC's realtime prover, which could not bound its iterations from the types — nothing stopped a tiny loop length and a huge index — so the construct was rewritten rather than the compiler patched.

The same profile builds the native archive and the wasm32-wasi reactor. `verify/browser-wasm-lane.mjs` loads that exact `.lib.wasm` in headless Chromium, drives the `scsrd_` ABI and byte-compares against Node: it proves the artifact executes correctly in a browser engine, and deliberately does not claim AudioWorklet scheduling, which needs a reader-ABI bank-install path in the persistent worklet host.

Part of these racks.

A rack is a fixed rig: several modules already wired together, playable as one instrument. This module is one of the pieces.

  • Drumbox

    Drumstep sequences the Maru Sampler's Detroit Techno kit through a ping-pong delay.

    Opens on a Drumstep pattern over the sampler's 16-pad Detroit Techno kit.

    3 modules · drumstep → sampler → ping-pong-delay

  • Linesynth

    Linestep writes one melodic line and plays it on a compiled synth voice and the Maru Sampler at once, summed through a ping-pong delay.

    Opens on a Linestep melody played at once by the synth voice and the sampler's Aurora Keys bank.

    4 modules · linestep → oscillator → sampler → ping-pong-delay

Goes well with.

Maru Sampler

A free instrument for playing layered sample libraries.

FreeNo build recorded
Preview

Tetra Box

A sixteen-pad drum machine whose whole kit morphs across three faces at once.

No build recorded
Preview

Granular

A recording split into a playable grain cloud.

Preview

Pluck

A physical string model, rendered per note.