All books

Loading samples

~5 min read · updated 2026-09-20 · markdown

You have an audio file (a kick drum, a vocal phrase, a synth bounce) and you want to use it in a Builder — in a soundfile node, in a sampler DSP. This section is the workflow. The sample-store + the round-trip from recording → sample also touches FaustWave — Recorder § 2.

What FaustWave accepts

FormatNotes
.wavFull header parsing — channels / sample rate / duration are populated. The default for FaustWave's own recordings.
.flacBytes import fine; header parsing for channels / rate / duration is a TODO so those fields land as 0.
.aiffSame as .flac — bytes-only for now.

Hard limit: 100 MB per sample. Bigger files are rejected at import; chop them externally first.

How samples are addressed

FaustWave doesn't reference samples by path. Every sample lives in the sample store keyed by its sha256 content hash. Two consequences:

  1. Idempotent imports — importing the same bytes twice is a no-op. The second import returns the existing sha and adds no new file. Drag the same WAV in 50 times; you have one sample.
  2. Portable documents — a .builder references its samples by sha. Share the DSP with a friend who already has the sample → it just works. Share with a friend who doesn't → they get a "browse for this sha" prompt at compile, with the original filename + channel/rate metadata as hints.

Three ways to import

A) Drag-and-drop onto a Builder canvas

Drop the file onto the Builder canvas. If there's a soundfile node selected, the sample binds to it. If not, FaustWave adds a soundfile node, binds the sample to it, and places it at the drop position.

B) The samples.import MCP action

samples.import { file_path: "<absolute OS path to your .wav/.flac/.aiff>" }

Returns the SampleEntry — { sha256, channels, sampleRate, duration, originalFilename, byteSize }. Idempotent (same bytes twice = same entry).

C) Hub install of a sample-pack

If you don't have the bytes locally but somebody published them to the Hub:

hub.install { item_id: "<sample-pack id from hub.search>" }

The install fetches the bytes + runs the same sha-keyed write path. The sample lands in your store ready to reference. See FaustWave — Hub § 3 for the publishing side.

Using the sample in a graph

1. samples.import   { file_path: "<abs path to kick.wav>" }              → { sha256, … }
2. documents.add      { type: "builder", title: "Sample player" }          → module_id
3. builder.graph.node.add     { module_id, kind: "soundfile" }                     → sf node id
4. builder.graph.node.ref.set { module_id, node_id: <sf>, param: "sha256", value: <sha256> }
5. builder.graph.node.add     { module_id, kind: "output" }                        → out node id
6. builder.graph.connect      { module_id, source_node_id: <sf>, source_port: "audio_l",
                      target_node_id: <out>, target_port: "in_l" }
7. dsp.run          { module_id }

(Not a click-chain — step 1 needs the absolute OS path of YOUR audio file, the one place paths are expected: the sample import gate. Ask the assistant to run the sequence with a real file.) The soundfile node's sha256 ref is the binding point: once builder.graph.node.ref.set lands, the Builder's compile includes the sample in its worklet bundle, and dsp.run plays it.

The soundfile node is a player

Since the sample-hybrid round (2026-08-29) the node is a one-shot player, not a tape that runs once at patch start:

  • trig_in restarts playback on every rising edge — wire midi_key.gate (one node per drum voice), midi_note.gate, or a clock.tick.
  • freq_in + root_hz make the sample follow the played note like a sampler zone: the read rate is freq / root_hz. Measure the root with analysis.sample (the strongestLowBinHz field of its spectrum block) — do not trust the pack's key label. root_hz 0 = no tracking, plain rate.
  • Two read heads. A retrigger starts the other head and crossfades to it in ~5 ms while the old tail runs out; the last 5 ms of the file fade to zero. Both were single-sample jumps before — measured as clicks on every retriggered open hat and at the hard end of a distorted 808 sample. See FaustWave — Analysis § The click hunt.
  • Shape a tail with adsr x mul when the sample is longer than the note; nothing else is built (no loop, reverse or start offset — nobody asked).

The kit recipe is midi_key → soundfile → mul(velocity) → gain → add … per voice; the bundled Around the World project's Sample Kit.builder is seven of those summed.

Browsing your sample store

actions:
  - action_id: samples.list
    input: {}
    save_as: store
    label: "▶ Enumerate every sample"

Returns an array of SampleEntry — sha, original filename, channels, sample rate, duration, byte size. samples.get { sha256 } returns one entry's metadata (not the bytes — too heavy for MCP). samples.remove { sha256 } deletes (destructive — confirm before calling; DSPs referencing the sha will prompt-to-locate on next compile).

Where the bytes live on disk

OSPath
Windows%APPDATA%\FaustWave IDE\samples\<sha>\<original-filename>.<ext>
macOS~/Library/Application Support/FaustWave IDE/samples/<sha>/<filename>.<ext>
Linux~/.config/FaustWave IDE/samples/<sha>/<filename>.<ext>

The folder structure (sha-prefixed) is what lets the import be idempotent + content-addressed.

A common end-to-end task: record → re-import

The round-trip from synth performance to playable sample is one chain (full version in FaustWave — Recorder § 2):

  1. Press Rec.
  2. Play the synth via the Keyboard or Sequencer.
  3. Press Rec to stop — recorder.stop returns the WAV path.
  4. samples.import { file_path: <path> } — the recording is now sha-keyed in the store.
  5. Drop a fresh soundfile node + bind by sha as in the chain above.

From "synth voice" to "playable sample slice" without leaving FaustWave.

Common moves

  • "Use the kick I dragged in last week" — samples.list { search: "kick" } narrows by filename; pick it by filename or durationMs, copy the sha, bind it to a fresh soundfile node via builder.graph.node.ref.set.
  • "Delete every old WAV I dragged in for testing" — samples.list, filter by sha or filename, iterate samples.remove. No trash.
  • "Re-bind a soundfile in an existing Builder" — builder.graph.node.ref.set with a new sha. The change is topology-relevant so the engine recompiles; the new sample plays immediately.

Where to go from here

  • FaustWave — Builder § 1 — wiring soundfile nodes into the rest of your graph (envelope, filters, FX).
  • FaustWave — Recorder § 2 — the round-trip from recording → sample-store.
  • FaustWave — Hub § 3 — publishing samples as hub.publish kind: "sample-pack".
  • FaustWave — Getting Started § Your first recording — the 5-minute starter flow.
Try it yourself — the IDE runs in your browser. Open the IDE → Get the desktop app

Text licensed under CC BY 4.0 — Mani Weber / FaustWave. For language models: llms.txt · llms-full.txt

AUDIO · 48k · 48.0ms FAUST · 3 KB · 1171 docs CPU · 8.4% BPM · 120.0 UTF-8 BETA· v0.90.0