Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FXVsD1DgdzZLiPuwtKzMSY
4.3 KiB
Mixing in HyperFrames
Preview and render run the same Web Audio graph, so what plays while scrubbing is what renders. Everything lives in attributes on the <audio> or <video> element.
Level and fades
| Attribute | Meaning |
|---|---|
data-volume |
Track gain, 1 = 0 dB |
data-fade-in, data-fade-out |
Edge fades in seconds |
data-playback-rate |
Constant speed, 0.1–10, pitch preserved; give a video and its paired audio the same value |
A crossfade is two clips on different tracks that overlap, each with a fade.
data-automation: envelopes
<audio
id="music"
data-automation='{"version":1,"lanes":[{"target":"volume","points":[{"t":0,"v":1},{"t":2.5,"v":0.35}]}]}'
…
></audio>
targetisvolume,rate(a speed ramp, 0.1–10, overridesdata-playback-rate) orfx.<nodeId>.<param>.tis seconds from the start of the clip, not of the composition.vis in the parameter's unit: 0..1 for volume, dB for a gain, Hz for a frequency.- A lane holds its first value back to the clip's start and its last value to its end, so a bed that starts before the voice needs an explicit full-level point at
t: 0. curve(-1..1) bends the segment leaving a point. 512 points per lane at most.- Don't also tween
volumewith GSAP on a track that has a volume lane or adata-volume; lint flags both.
data-fx-chain: effects
{
"version": 1,
"nodes": [
{ "type": "highpass", "id": "n1", "label": "Remove rumble", "params": { "frequency": 80 } },
{
"type": "peaking",
"id": "n2",
"label": "Clarity",
"params": { "frequency": 3000, "gain": 2.5, "q": 1 }
},
{ "type": "limiter", "id": "n3", "params": { "limit": -1 } }
]
}
- Write the attribute double-quoted with the JSON's quotes as
". - Order is signal order.
idis what automation targets (fx.n2.gain); give every node one, and alabelwhen it does a named job.enabled: falsebypasses a node. - Effects:
gain,highpass,lowpass,peaking,lowshelf,highshelf,compressor,limiter,gate,saturate,delay,reverb,chorus,phaser,bitcrush. compressor,limiter,gateandbitcrushcan't be automated; a lane on them does nothing. Automate againbefore them instead.- Never set
fromCarveby hand. reverbanddelaymake the rendered track longer than its source; that is expected.
| It sounds like | Reach for |
|---|---|
| Hum, rumble, handling noise | highpass at 80 Hz |
| Boomy, too close | peaking 200 Hz, −4 dB |
| Muffled | peaking 250 Hz, −3 dB |
| Words hard to make out | peaking 3 kHz, +2.5 dB, or duck the music |
| Harsh | peaking 3.2 kHz, −3 dB |
| Peaks clipping | limiter last in the chain |
Music under a voice
Duck the bed with a volume lane on the music: full level before the voice, down to about 0.3–0.4 while anyone speaks (word timings from npx hyperframes transcribe or the voiceover give the times), back up after. Then carve the bed, which dips only the bands the voice uses: node <SKILL_DIR>/audio/scripts/carve.mjs --comp index.html (needs ffmpeg and @hyperframes/core in the project; --dry-run to see the plan). It is the same analysis as Studio's carve panel. It reads only double-quoted attributes (JSON quotes as "), so write the bed's chain and lanes that way or it overwrites them. Never put a carve on a voice track.
Group buses
Give clips the same data-audio-group="<id>" to treat them as one group. An <hf-audio-group id="<id>"> element with that id makes the group a submix bus with one data-fx-chain, one volume and one automation clock for every member. Keep one role per group: voiceover, music, sfx.