A timeline volume tween makes --variables be silently ignored at render
Summary
When a composition's GSAP timeline animates the volume of an <audio> element
(e.g. a music fade-out / ducking), hyperframes render --variables '{...}' (and
--variables-file) are silently ignored: every variable falls back to its
declared default. Remove the volume tween and the exact same --variables
are honored again.
This affects both consumption paths — declarative data-var-text / data-var-src
bindings and window.__hyperframes.getVariables() in an init script. No error
or warning is printed; the render "succeeds" with the wrong (default) content.
Environment
hyperframes 0.7.48 (via npx hyperframes@0.7.48 render)
- Node v26.4.0
- macOS (Darwin arm64, Apple M1), FFmpeg 8
- GSAP 3.14.2 (CDN, as scaffolded by
hyperframes init)
Minimal reproduction
Project: one <video>, one <audio>, one variable title bound via
data-var-text, and a volume tween on the audio. Full files in repro/.
index.html (essentials):
<html data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"DEFAULT_TITLE"}]'>
...
<div id="root" data-composition-id="main" data-start="0" data-width="1080" data-height="1920" data-duration="3">
<video id="clip" class="clip" src="assets/testclip.mp4" data-start="0" data-duration="3" data-track-index="0" muted playsinline></video>
<audio id="snd" src="assets/testaudio.mp3" data-start="0" data-duration="3" data-track-index="9" data-volume="1"></audio>
<h1 id="title" class="clip" data-start="0" data-duration="3" data-track-index="1" data-var-text="title">DEFAULT_TITLE</h1>
</div>
<script>
const tl = gsap.timeline({ paused: true });
tl.fromTo("#title", { opacity: 0 }, { opacity: 1, duration: 0.4 }, 0);
tl.to("#snd", { volume: 0, duration: 1, ease: "none" }, 2); // ← remove this line to "fix" it
window.__timelines = window.__timelines || {};
window.__timelines["main"] = tl;
</script>
</html>
Generate the two synthetic assets:
ffmpeg -f lavfi -i color=c=0x224466:s=640x360:d=3 -pix_fmt yuv420p assets/testclip.mp4
ffmpeg -f lavfi -i "sine=frequency=440:duration=3" -c:a libmp3lame assets/testaudio.mp3
Render:
npx hyperframes@0.7.48 render --variables '{"title":"OVERRIDE"}'
Expected vs actual
- Expected: the frame shows
OVERRIDE.
- Actual: the frame shows
DEFAULT_TITLE (override ignored).
- Control: comment out the single
tl.to("#snd", { volume: 0, ... }) line and
re-render → the frame correctly shows OVERRIDE.
Scope / things ruled out
The override IS honored in all of these (so none of them is the trigger on its own):
<video> present, <audio> present without a volume tween, a trimmed video that
forces frame extraction, three sequential <video>s, data-var-src on a <video>,
a real rotated .mov with odd framerate, and getVariables() used with a video.
The trigger is specifically the volume tween on the timeline.
Workarounds
- Don't animate
volume on the timeline; bake the fade into the audio file, or
use a static data-volume.
- Bake the intended values into the
default fields of
data-composition-variables (in a temp copy of index.html) and render without
--variables — the declared defaults are always honored.
Impact
Any template that both (a) is parameterized via --variables and (b) fades/ducks
audio on the timeline renders default content instead of the caller's values, with
no error — easy to ship silently.
A timeline
volumetween makes--variablesbe silently ignored at renderSummary
When a composition's GSAP timeline animates the
volumeof an<audio>element(e.g. a music fade-out / ducking),
hyperframes render --variables '{...}'(and--variables-file) are silently ignored: every variable falls back to itsdeclared
default. Remove thevolumetween and the exact same--variablesare honored again.
This affects both consumption paths — declarative
data-var-text/data-var-srcbindings and
window.__hyperframes.getVariables()in an init script. No erroror warning is printed; the render "succeeds" with the wrong (default) content.
Environment
hyperframes0.7.48 (vianpx hyperframes@0.7.48 render)hyperframes init)Minimal reproduction
Project: one
<video>, one<audio>, one variabletitlebound viadata-var-text, and avolumetween on the audio. Full files inrepro/.index.html(essentials):Generate the two synthetic assets:
ffmpeg -f lavfi -i color=c=0x224466:s=640x360:d=3 -pix_fmt yuv420p assets/testclip.mp4 ffmpeg -f lavfi -i "sine=frequency=440:duration=3" -c:a libmp3lame assets/testaudio.mp3Render:
npx hyperframes@0.7.48 render --variables '{"title":"OVERRIDE"}'Expected vs actual
OVERRIDE.DEFAULT_TITLE(override ignored).tl.to("#snd", { volume: 0, ... })line andre-render → the frame correctly shows
OVERRIDE.Scope / things ruled out
The override IS honored in all of these (so none of them is the trigger on its own):
<video>present,<audio>present without a volume tween, a trimmed video thatforces frame extraction, three sequential
<video>s,data-var-srcon a<video>,a real rotated
.movwith odd framerate, andgetVariables()used with a video.The trigger is specifically the
volumetween on the timeline.Workarounds
volumeon the timeline; bake the fade into the audio file, oruse a static
data-volume.defaultfields ofdata-composition-variables(in a temp copy ofindex.html) and render without--variables— the declared defaults are always honored.Impact
Any template that both (a) is parameterized via
--variablesand (b) fades/ducksaudio on the timeline renders default content instead of the caller's values, with
no error — easy to ship silently.