- Skill ID
- heygen-com/hyperframes/hyperframes-audio
- Publisher
- heygen-com
- Repository
- hyperframes
- Installs
- 7,438
- Files
- 7
- Synced
- Sep 16, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
heygen-com/hyperframes/hyperframes-audioInstalls these files- references/attributes.md
- references/diagnosis.md
- references/fx-registry.md
- references/presets.md
- scripts/carve.mjs
- scripts/carve.test.mjs
- SKILL.md
What this skill tells the agent
HyperFrames Audio
A mix is a set of relationships, not a stack of processors. Two tracks that each sound right alone can be unlistenable together, and the fix is almost never "turn one down" — it is finding what they are fighting over and giving it to whichever one needs it. Every tool here exists to express one of those relationships.
Effects live on the element as data-fx-chain, and preview and render run the same Web Audio graph — the studio in a live context, the engine in an offline one inside the browser it already drives. There is one implementation of each effect, so what you hear while scrubbing is what gets written. You never tune twice.
Clip timing remains /hyperframes-core: audio/video trims and source ranges use data-start, data-duration, and data-media-start, and crossfades overlap clips on different tracks. This skill owns placed-track fade-in/fade-out, crossfade envelopes, track gain/track volume, volume and effect automation, ducking/voiceover carve, and the effect chain. /media-use owns sourcing, generation, and preprocessing.
Constant data-playback-rate (0.1..5) is render-safe for picture and pitch-preserved sound when matching audio/video elements use the same timing, source offset, and rate. Source speed ramps are not supported because there is 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, 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 |
|---|---|
data-fx-chain | the effects, in signal order |
data-automation | envelopes on this track's volume or its effect parameters |
data-fx-carve | the carve's own settings, so it can be re-derived |
The shipped effect families are gain, EQ (highpass, lowpass, peaking, shelves), compressor, limiter, gate, saturate, delay, reverb, chorus, phaser, and bitcrush.
Exact JSON for each, and the rules a lane must satisfy: references/attributes.md. Every effect with its parameters, ranges and units: references/fx-registry.md. How to work out what is wrong with a file you cannot hear: references/diagnosis.md. Presets, named jobs and one-knob profiles, plus a symptom-to-fix table: `references/presets.md` — read that before hand-building a chain, because one of the presets or named jobs usually already names the problem.
How it fits together
Two authoring surfaces write those attributes; two runtimes read them through the same builders. That shared middle is why preview predicts the render.
flowchart TB
voice["voice track<br/>media file"]
bed["music bed<br/>media file"]
subgraph AUTHOR["Authoring — the only things that write attributes"]
panel["Studio<br/>Voiceover carve control"]
script["scripts/carve.mjs<br/>detects the pair, dynamic by default"]
analysis["core/audioCarve.ts<br/>carveProfile · analyseCarveBands<br/>analyseCarveDuck · analyseCarveDynamics"]
panel --> analysis
script --> analysis
end
voice --> analysis
bed --> analysis
subgraph ATTRS["Written onto the bed element"]
carveAttr["data-fx-carve<br/>source · strength · dynamic"]
chainAttr["data-fx-chain<br/>peaking xN + gain, tagged fromCarve"]
autoAttr["data-automation<br/>a lane per carved parameter"]
end
analysis --> carveAttr
analysis --> chainAttr
analysis --> autoAttr
subgraph SHARED["One implementation, read by both"]
build["audioFxGraph.ts · buildFxChain"]
sched["audioFxAutomation.ts · scheduleChainAutomation"]
end
chainAttr --> build
autoAttr --> sched
build --> preview["Preview<br/>live AudioContext<br/>attachElementFxChain"]
sched --> preview
build --> render["Render<br/>OfflineAudioContext in the headless browser<br/>applyAudioFxChain"]
sched --> render
preview --> heard["what you hear while scrubbing"]
render --> wav["processed WAV<br/>+ chainTailSeconds so the mix lets the tail through"]
wav --> mix["engine · audioMixer<br/>volume lane baked into the PCM here, not in the graph"]
mix --> out["the rendered mix"]
edit["editing the attribute mid-playback"] -.->|MutationObserver| previewThe carve's own settings are never read at playback — the chain and lanes it produced are what play. data-fx-carve exists so strength can be changed on an existing carve instead of guessed back out of the filters.
Inside a carved bed the signal runs through the dips first, then the level match, then anything you built yourself — which is why a limiter you add still acts as the last ceiling:
flowchart LR
src["decoded bed"] --> p1["peaking<br/>400 Hz"]
p1 --> p2["peaking<br/>1 kHz"]
p2 --> p3["peaking<br/>1.6 kHz"]
p3 --> g["gain<br/>level match"]
g --> hand["your own effects<br/>e.g. limiter"]
hand --> dest["track gain, then out"]
l1["lane fx.n1.gain"] -.->|"envelope of the voice's<br/>level in that band"| p1
l4["lane fx.n4.gain"] -.->|"how far the bed<br/>ducks overall"| gA static carve is the same graph with fixed values and no lanes at all.
First, work out what is wrong
The table below starts from "it sounds boomy" — which presumes somebody already listened and said so. Handed a file and "fix this", you have no such sentence and you cannot listen, so you have to measure. One rule governs all of it:
The absolute spectrum of a single unknown voice cannot be diagnosed. Formants are ±10 dB, fundamentals run 85–255 Hz, and sentences decline 5–6 dB as they end. Every one of those reads as a defect on its own, and every one of them is the speaker.
So compare, and compare against something inside the same file: the clean original if it exists, otherwise the pauses — whatever is audible in a gap is additive, and the gap's spectrum is the channel rather than the voice. Comparing against a published average spectrum or a synthesised control voice does not work: two speakers differ by more than most defects, and both wrong answers in the evaluation behind this guidance came from exactly that.
When there is no original and no usable silence, a static tonal defect is genuinely under-determined. Say so and offer the readings that fit, rather than picking one and building a chain on it.
Commands, traps and worked recipes: `references/diagnosis.md`. Read it before diagnosing a file nobody has described.
Start from the symptom
Once you know the band and the kind, name what is wrong with the audio. Most bad audio is one or two of these, and each has a shipped answer:
| It sounds like | Reach for |
|---|---|
| Hum or thump underneath | rumble-cut, or a highpass at 80 Hz |
| Boomy, chesty | Tame Boominess job (200 Hz) |
| Muffled, behind cardboard | Reduce Mud job (250 Hz) |
| Words hard to make out | Add Clarity job (3 kHz), or carve the bed |
| Harsh and tiring | Soften Harshness job (3.2 kHz) |
| Some words much louder than others | Evenness on a compressor, or Even Out Levels |
| Room tone between sentences | room-gate |
| Voice and music fighting | Voiceover carve — not an EQ on either |
| Dry, recorded nowhere | room-tight or room-natural |
