Skip to content

feat(studio): automation lanes in the timeline, and automate/un-automate from the panel - #3018

Closed
vanceingalls wants to merge 2 commits into
wa-5-automation-runtime-fixesfrom
wa-6-automation-studio
Closed

feat(studio): automation lanes in the timeline, and automate/un-automate from the panel#3018
vanceingalls wants to merge 2 commits into
wa-5-automation-runtime-fixesfrom
wa-6-automation-studio

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Stacked on #3017. The UI.

Lanes. Each automated parameter draws its own lane under the audio clip, on
the same disclosure caret the keyframe lanes use — that caret is the DAW
automation triangle. One lane per parameter rather than a selector to swap
between them, so two envelopes can be read and edited without hiding either.
Double-click the line to add a point, drag to shape it, right-click a point to
remove it.

Everything the lane knows comes from the FX registry — range, unit, log scale —
so a log knob is drawn and dragged on a log axis, and the curve on screen is
the curve the audio thread plays.

Panel. An automated parameter's control is disabled, since a value set there
would be overwritten by the envelope on the next tick. Each automatable
parameter gets a toggle: adding a lane seeds it with a single point at the value
the control already holds, so switching to an envelope never changes the sound,
only where the value comes from. Parameters no envelope can drive have no toggle
at all.

Things that took more than one attempt

  • Dragging a point did not move it. The live write deliberately skips the
    refresh, so the stored value does not move under the pointer; the lane needed a
    local draft. Releasing it then snapped back until the draft was made to survive
    until the prop changed rather than until the drag ended — which in turn
    needed the parsed attribute cached, or a fresh object each playhead tick threw
    the drag away.
  • Deleting one effect made every later delete a no-op. The live path persists
    but does not resync the selection, so the panel kept computing from the
    snapshot it was built with. Added a commit that skips the reload and resyncs;
    both halves are needed, and they were fighting each other.
  • Automating a volume would have deleted every FX lane. The volume section
    does not parse the chain, and resolving against an absent chain drops every
    lane it cannot resolve. Now "no chain" means "do not resolve" rather than "drop".
  • Carve glitched the track by opening a second AudioContext just to decode;
    it decodes in an OfflineAudioContext, which touches no device. Turning carve
    off now also drops the filters it generated.

AudioFxGroup moves into its own module and AUTOMATION_LANE_H into its own —
PropertyPanelFlat and timelineLayout were both at the 600-line studio limit.

🤖 Generated with Claude Code

vanceingalls and others added 2 commits August 4, 2026 09:49
Draws each automated parameter as its own lane under the audio clip, on the
same disclosure caret the keyframe lanes use — that caret is the DAW automation
triangle. One lane per parameter rather than a selector to swap between them,
so two envelopes can be read and edited without hiding either.

Editing is the DAW gesture set: double-click the line to add a point, drag to
shape it, right-click a point to remove it. Dragging writes live so playback is
not interrupted, and a local draft carries the point under the pointer — the
live write deliberately skips the refresh, so the stored value does not move
until the edit comes back around, and without the draft the point would not
follow the cursor.

Everything the lane knows about a parameter comes from the FX registry: range,
unit, and whether it reads logarithmically. A log knob is drawn and dragged on
a log axis, so the curve on screen is the curve the audio thread plays. Lanes
are bound to the chain the way preview and the render bind them, so one whose
effect was deleted is dropped rather than drawn against the wrong axis.

Row height reserves each lane, the envelope is inset so a point at the clip's
first or last frame is drawn whole rather than half outside it, and pressing a
lane on an unselected clip selects it — lanes sit below the clip bar, where the
timeline's own selection handler never sees the press.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An automated parameter's control is disabled, since a value set there would be
overwritten by the envelope on the next tick — the lane is the value now. Each
automatable parameter gets a toggle beside it: adding a lane seeds it with a
single point at the value the control already holds, so switching to an
envelope never changes the sound, only where the value comes from. Toggling it
off deletes that lane and hands the value back.

Parameters no envelope can drive have no toggle at all — the worklet-backed
dynamics, a WaveShaper's curve, a convolution impulse — and neither does a
chain node with no id, since a lane addresses nodes by id. Adding an effect now
mints one.

Writes go through a new commit that persists without reloading the preview but
still re-reads the selection. Both halves are needed: the reload restarts every
playing track, and without the resync the panel keeps reading the snapshot it
was built with, so a second edit computes from a pre-edit value and appears to
do nothing — deleting one effect made every later delete a no-op.

Carve is on that path too, and decodes its source in an OfflineAudioContext:
opening a second output device mid-playback makes the running track glitch
while the hardware is reconfigured. Turning carve off now also drops the
filters it generated, which otherwise kept dipping the bed with nothing to
explain it.

AudioFxGroup moves into its own module — PropertyPanelFlat was already at its
size budget — and both panel sections read automation through one helper, which
is what surfaced that resolving against an absent chain would have deleted
every FX lane the moment someone automated a volume.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vanceingalls

Copy link
Copy Markdown
Collaborator Author

Superseded by stack #3027. This PR carried the studio UI in one diff (+2397); it is now #3023 lane geometry and plumbing, #3024 the lane component, #3025 the non-reloading write path + volume, #3026 the per-parameter surface — each under 1000 LOC, with the pure lane maths extracted into its own module along the way.

@vanceingalls
vanceingalls deleted the wa-6-automation-studio branch August 4, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant