← Inside the current approximation

SIREN / design lab / parameter reactivity

Few instructions.
Fluid motion.

Tell the chip where to go — and how to get there. Spend your updates on the strike, then let the note breathe between instructions.

Chipset proposalInteractive curvesExisting piano data

The CPU supplies landmarks. The chip draws the journey. Gain, pan, send, and other continuous controls can move smoothly even when their next instruction arrives only ten times a second.

New: tune bits, point count, and CPU playback rate independently →
The compression lab packs real descriptors and explores a 600 Hz CPU interpreter without changing the chip.

Loading the current piano curves…

01 / Separate instructions from movement

The control rate is not the motion rate.

1. Immediate

A new value becomes active at the next control latch. No journey — just the destination.

y ← target

Useful for deliberate steps, switches, and resetting a value. Sparse gain updates sound stepped unless an envelope supplies the motion elsewhere.

2. Exponential follower

Each audio sample closes a fraction of the remaining distance. New targets can arrive whenever they like.

y ← y + α · (target − y)
α = 1 − 10−1/(48,000 · T₉₀)

T₉₀ is the time to cover 90% of the gap. Calculate α only when the response time changes; the sample loop needs one multiply and two adds/subtracts. It glides naturally, but follows behind a moving target.

3. Timed linear ramp

“Reach this value in 25 ms.” The player sends the next landmark at the start of the segment.

N = round(48,000 · duration)
step = (target − current) / N
y ← y + step; snap to target at N

One addition per sample, plus a countdown. Exact arrival time. Perfect for planned envelope landmarks; also useful for a pan sweep or send fade.

4. Rounded ramp

The same scheduled arrival, with a gentle departure and landing. No overshoot between two points.

u = elapsed / duration
y = start + (target − start) · (3u² − 2u³)

Smoothstep has zero slope at both ends. In gain space, its cubic can run with forward differences — three additions per sample after setup. Log-space motion also needs conversion back to gain. Rounding every landmark can sound too cushioned; the linear option stays useful.

Two different instructions: “follow this target” reacts to the present; “arrive there in this time” plans the next segment. For a piano envelope we already know the next point, so timed ramps can avoid the follower's lag.

02 / Put the idea on the actual curves

Turn down the update rate.
Watch what survives.

The white trace is the current 440-reduced piano's gain curve. The dots are the landmarks kept by the selected schedule. All motion modes share those landmarks: the follower receives each value when its dot arrives; timed ramps receive the next value one segment earlier.

ReferenceImmediateExponential followerTimed linearRounded ramp
—vector updates / 8 s
—versus 1,920 rows
—updates in first 20 ms

The count is the number of intervals after initialization at t = 0. Each interval corresponds to one set of eight target values: ramp commands go out at its start, follower/step commands at its end. A schedule determines when to send targets, not how many bytes describe the instrument.

Each tick is an update. The upper lane magnifies the first 100 ms; the lower lane shows two seconds. A longer gap means less CPU control traffic, while the chip keeps moving.
All eight partials, with the same settings
The next twenty commands for the selected partial
Landmark times and gain targets
Time Gap from previous Gain / peak dB Ramp command sent at

For a follower or immediate mode, send at the landmark time. For a ramp, send at the preceding landmark with the gap as its duration. The same coefficient may be reused while response settings stay unchanged.

The reference comes from the existing 240 Hz gain table, joined linearly for this drawing. A 600 Hz view explores scheduling and interpolation between those values; new attack measurements can later supply finer detail. The follower always operates in linear gain; “ramp geometry” changes only the two planned ramps. No audio plays on this page.

03 / A clock that stretches with the note

Fine near zero.
Roomier as time passes.

Pure doubling — 1, 2, 4, 8, 16… — quickly leaves very large gaps. A gentler geometric progression keeps more of the broad dips and recoveries.

q = 1 / 600 second = 1.667 ms
k₀ = 0
Δk = clamp(round(k / 8), 1, 60)
knext = k + Δk; t = k · q

At first, move one chip tick at a time. Then the gap grows with note age, roughly multiplying the time by 9/8 per update. Eventually cap the gap at 60 ticks: 100 ms, or 10 Hz. The integer version lands on the chip's existing 600 Hz latch grid.

Local spacing from the rule at representative note ages
Note age Interval Equivalent rate
0–10 ms 1.67 ms 600 Hz
20 ms 3.33 ms 300 Hz
60 ms 8.33 ms 120 Hz
200 ms 25 ms 40 Hz
800 ms and later 100 ms 10 Hz

Check “keep a 600 Hz strike” to give the first 20 ms a uniform fine grid. After that, the expanding rule or chosen slower schedule takes over. Knots are a budget, not a prison: a known sharp change can request an extra landmark wherever it is needed.

Keep the chip latch fast

Leave the global latch at 600 Hz. A quiet tail doesn't need to write every tick, but a new note can still start promptly. Each parameter's current value continues evolving at audio rate between latches.

Let the CPU wake less often

For actual CPU savings, eventually schedule the next event rather than run a high-frequency interrupt that mostly does nothing. Multiple notes can share one queue of upcoming landmarks. Their attacks and tails naturally use different cadences.

04 / A small language for moving parameters

Target. Response. Time.

My suggested first interface is per-parameter policy, initially for the eight slots' gain, pan, and send: 24 independent continuous lanes. Keep source selection and routing as discrete choices.

configure(slot.gain, LINEAR, 25 ms)
write(slot.gain, nextLevel)

configure(slot.pan, FOLLOW, T₉₀ = 80 ms)
write(slot.pan, +0.3)

configure(slot.send, ROUNDED, 150 ms)
write(slot.send, nextSend)

This is the proposed command vocabulary; register addresses come after choosing the behavior. Configuration persists, so repeated targets need not repeat their mode and time.

Retarget without jumping

A new ramp starts from the value the chip is currently using, not from the previous command's destination. An exponential follower keeps its current value and changes its destination. Immediate mode jumps deliberately.

Rounded ramps restart with zero slope; value is continuous, velocity can change. A future slope-preserving cubic is an option if that corner matters.

Keep the rules predictable

Defaults remain immediate, so existing programs keep their behavior. Mode/time and target changes latch together. Rewriting an unchanged target does not restart a ramp; a separate retrigger command can request that.

CPU reads continue to return programmed targets. Capture/debug tools can expose the evolving current values. These are two different things, and both are useful.

Silence is an exact state

Timed ramps snap to their endpoint. Exponential followers settle to an exact target below a small internal threshold instead of leaving endless floating-point tails. A zero time means immediate.

Clamp gain/send to their valid range and pan to its signed range; reject invalid commands. All low-level motion uses fractional internal precision, rather than rounding to a register byte every sample.

Use the right coordinates

Gain-space ramps can start and end at zero naturally. A straight line in dB produces an exponential amplitude ramp. Musical pitch glides should move in semitones; pan moves in its signed coordinate.

Log-gain plots use an −80 dB floor between zero endpoints. The engine can implement positive log ramps as repeated multiplication, with explicit start/stop handling for zero. Filters, pitch, and delay feedback deserve their own domain and stability rules before joining the first 24 lanes.

Recommended first implementation: immediate, exponential following, and timed linear ramps for gain/pan/send. Keep the rounded mode in the playground while listening decides whether its extra curve state is useful. Keep native generator envelopes for attacks they already express well.

05 / Small descriptions, small moving state

Fewer instructions.
Not a hidden giant table.

A four-byte partial description can generate a destination only when the next landmark is due. The chip keeps the current value, destination, and a little motion state — it does not need an eight-second expansion of all future values.

Memory

A simple fixed allocation could reserve eight 32-bit state words per lane: 24 × 32 = 768 bytes, plus mode/configuration storage. Linear and follower modes can use less. This is internal chip state, separate from the compact instrument bytes.

The schedule can be generated from the rule; it need not be stored per partial. Shared attack/tail settings and the small decoder still count in the instrument/player budget.

Arithmetic

Only moving lanes need updates. A follower uses a multiply and two adds/subtracts per sample; a linear ramp needs an add and countdown. Setup performs divisions or exponentials once per new motion, not 48,000 times a second.

At most 24 first-stage lanes × 48,000 samples = 1,152,000 lane steps per second. Guest control traffic falls; host interpolation work rises. We'll measure both when implementing it.

There are two independent compression wins: compact shapes reduce instrument data, and sparse destinations with chip-side motion reduce control traffic. Together they let us keep organic changes without paying for thousands of nearly repeated gain words.