# The three writes, and what an amount means

A parameter that is modulated has three values that look like one knob, and FaustWave gives each its own verb. Getting this wrong is the single most common way to "set a value that does not stick".

| Write | Action | What it changes | Survives |
|---|---|---|---|
| **live** | `dsp.param.set { instance_id, path, value }` | The number in the running worklet, right now — what `dsp.param.get` reads back, post-modulation | until the next modulation tick overwrites it, or the next compile |
| **base** | `modulation.base.set { sink_id, path, value }` | The centre the modulation swings around: `clamp(base + sum amount x source)` | the project (`paramBases` for .dsp, the `.builder` file for Builder nodes, the mixer slice for slots) |
| **slot** | `master.fx.slot.param.set { slot_id, param_name, value }` / `mixer.tracks.fx.set { params }` | What an FX slot stores and re-applies when it next compiles | the project |

The rule of thumb: **a modulated parameter is set through its base.** Writing it live is overwritten on the next tick; writing an unmodulated `.dsp` parameter live is fine and also lands in its base. For a slot, write the slot.

```yaml
actions:
  - action_id: modulation.base.set
    input: { sink_id: "<module_id>", path: "/MyDsp/cutoff", value: 2400 }
    label: "▶ Set the base a modulator swings around (edit the ids first)"
```

## Amount is a fraction of the range

An edge's `amount` is **not** in the parameter's units. It is a fraction of the sink parameter's declared range, multiplied by the source's output:

```
live = clamp( base + amount x source x (max - min), min, max )
```

So a macro at 0.6 on `weight_dB` (0..12) with amount 0.2 adds 0.6 x 0.2 x 12 = **1.4 dB**; an LFO at -1..1 on a 400..8000 Hz cutoff with amount 0.12 breathes +/-912 Hz. Two helpers for thinking in *min/max* instead: set `base = (min + max) / 2` and `amount = (max - min) / 2 / range`.

```yaml
actions:
  - action_id: routing.connect
    input: { from_source: "<source_id>", from_port: "/LFO/out", to_sink: "<module_id>", to_port: "/MyDsp/cutoff", kind: modulation, amount: 0.12 }
    label: "▶ Wire an LFO into a cutoff at 12 % of its range (edit the ids first)"
  - action_id: routing.list
    input: {}
    label: "▶ See the edge (sinks appear only while the instrument runs)"
```

Source ports are the `[modout]` addresses from `modulation.source.list` (`/LFO/out`); sink ports are the parameter paths from `dsp.params.list`. Re-connecting the same pair replaces the amount; `routing.disconnect` removes the edge and the base is what remains — modulation is non-destructive by construction.

## The demo's three edges

`Around the World` wires exactly three, and they show the three kinds of use:

- **Breath** — `Mod LFO Breath` (0.135 Hz ~ four bars) → Hook Lead `cutoff`, amount 0.12. A slow drift nobody notices until it is gone.
- **Step** — `Mod SH Bell` (sample-and-hold, sync 1/8) → Bell Hook `width`, amount 0.18. The bell is somewhere else on every eighth.
- **Macro** — `Mod Macro Erdbeben` → 808 `weight_dB` 0.2, `boom` 0.3, `dirt_level` 0.25. One knob, three targets, no motion of its own — the next chapter puts a controller on it.

> 🟦 **THE DIAGNOSTIC:** a value "does not stick" → `dsp.param.get` shows the live number; `modulation.source.list` shows whether an edge is on that path; if yes, you wanted `modulation.base.set`. A base "does not apply" after a restart → the sink registers only while the instrument runs; play first.
