docs(skills): teach /hyperframes-audio the submix bus

`<hf-audio-group>` appeared in no skill, doc or registry file. The audio
skill did cover groups, but only as membership tags to carve against — and
its example groups three clips with no group ELEMENT at all. So an agent
following the skill could never emit a group chain, fader or automation:
it was never told the element exists.

Adds "One bus for many tracks": the element, the five attributes it
carries, and when to reach for it — the same compressor wanted on four
narration clips is four chains that drift, and a compressor on the bus
hears the whole voice instead of a third of it.

Two things the skill has to say because they are the ways to get this
wrong:

- **Group automation is composition time.** A bus has no `data-start`, so
  `t: 0` in a group lane is the composition's start, not a clip's. Moving
  an envelope from a clip up onto its bus changes what its numbers mean.
- **A carve stays on the clip.** `data-fx-carve` is not a group attribute.
  The one thing the skill already said about groups was about carve, which
  invites exactly that wrong inference; the bed carries the carve, pointed
  AT a group.

Also corrects two framings that the bus makes false: the SKILL and
attributes.md both said all three attributes go "on the audio/video
element itself", when two of them also go on a bus.

No flag needed, and the skill says so: the `audio-groups` canary gates the
Studio UI for building groups, while a hand-authored bus parses, plays and
renders for everyone.

Verified rather than asserted: the documented example was run through
`resolveAudioGroups` and `parseAudioFxChain` (group resolves, volume 0.9,
both members found, compressor+peaking validate), its parameters checked
against fx-registry.md ranges, and the whole pattern linted — the only
findings were the throwaway fixture's own missing `data-start` and
timeline registration, nothing about the bus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-08-20 02:21:40 -07:00
co-authored by Claude Opus 5
parent f8b6f35171
commit 9b1cc64422
5 changed files with 74 additions and 16 deletions
+57 -3
View File
@@ -5,8 +5,9 @@ description: >
fade-in/fade-out, crossfade, track gain or volume, volume automation, ducking,
a music bed that fights a voiceover (voiceover carve), effects on a track
(EQ, compressor, limiter, gate, saturation, delay, reverb, chorus, phaser,
bitcrush), or automation envelopes drawn on a track's volume or any effect
parameter.
bitcrush), automation envelopes drawn on a track's volume or any effect
parameter, or one submix bus carrying a chain, a fader and an automation clock
for several tracks at once (`<hf-audio-group>`).
Don't use for sourcing or generating audio — finding BGM, SFX, or making a
voiceover is `/media-use`. Don't use for clip timing or track layout, which is
`/hyperframes-core`.
@@ -38,7 +39,8 @@ no rate envelope; preprocess a derived synchronized asset. HyperFrames does not
provide automatic waveform sync or drift correction.
For copyable cut/crossfade/retime recipes, use `/hyperframes-core``references/creator-editing-recipes.md`.
Three attributes carry everything, all on the audio/video element itself:
Three attributes carry everything, on the audio/video element itself — or, for
the first two, on an `<hf-audio-group>` bus (see "One bus for many tracks"):
| Attribute | Holds |
| ----------------- | --------------------------------------------------------- |
@@ -261,6 +263,58 @@ A `sources` list naming two or more plain clip ids instead of a group is caught
by the `audio_carve_ungrouped_sources` lint rule — it still works, but it is the
version that silently rots when a clip is added.
### One bus for many tracks
Membership alone is enough to carve against, as above — but add an
`<hf-audio-group>` element with that id and the group becomes a real submix bus:
one chain, one fader, one automation clock for every member.
```html
<hf-audio-group
id="voiceover"
data-label="Voiceover"
data-volume="0.9"
data-fx-chain='{"version":1,"nodes":[
{"type":"compressor","id":"g1","params":{"threshold":-18,"ratio":3}},
{"type":"peaking","id":"g2","params":{"frequency":3000,"gain":2,"q":1}}]}'
></hf-audio-group>
<audio id="vo-intro" data-audio-group="voiceover" ></audio>
<audio id="vo-middle" data-audio-group="voiceover" ></audio>
```
**Reach for the bus when the same treatment belongs on several tracks.** Four
narration clips that each want the same compressor is four chains to keep in
step, and they drift the moment one is edited; on the bus it is one chain, and
the compressor sees the whole voice rather than each clip in isolation — which is
the point, since a compressor cannot ride a sequence it only hears a third of.
Per-clip chains remain right for what is genuinely per-clip: one noisy take that
needs its own de-esser.
| On the bus | Does |
| ----------------- | ----------------------------------------- |
| `data-fx-chain` | one chain over the summed members |
| `data-automation` | envelopes on the bus, in COMPOSITION time |
| `data-volume` | one fader for every member (default 1) |
| `data-label` | the display name; falls back to the id |
| `data-hidden` | drops every member from the mix |
**Group automation is composition time, not clip time.** A bus has no
`data-start` — members are already at their composition positions when they
reach it — so `t: 0` in a group lane is the start of the composition, not of any
clip. A lane on a clip is clip-local; the same numbers mean different instants on
the two, which is the one thing to get right when moving an envelope from a clip
up onto its bus.
**A carve stays on the clip.** `data-fx-carve` is not a group attribute: the bus
has no carve, and putting one there does nothing. The bed being carved is a
single track, and it is that track which carries `data-fx-carve` — pointed AT a
group, per the rule above. Group and carve meet in `sources`, not on one element.
Nothing here needs a feature flag: the Studio UI for building groups is behind
the `audio-groups` canary, but a hand-authored `<hf-audio-group>` parses, plays
and renders for everyone.
**One knob.** `strength` is 0..1 and derives everything: how deep to cut, how
many bands, how wide, how far to favour intelligibility over raw voice energy,
how far the level may drop, how far under the voice to aim. Those six move
@@ -2,6 +2,10 @@
All three go on the `<audio>` / `<video>` element itself, JSON-encoded, so a
composition carries its whole mix in the HTML with nothing to load beside it.
`data-fx-chain` and `data-automation` also go on an `<hf-audio-group>` bus,
where they mean the same thing over the summed members — with one difference
worth knowing: a group's automation runs on COMPOSITION time, since a bus has no
`data-start` of its own. `data-fx-carve` is clip-only; a bus has no carve.
Nothing static validates them: preview plays an unreadable chain dry to stay
workable, and the render refuses the whole mix rather than shipping a dry track
that sounds plausible and is wrong.