Skip to content

Timeline volume tween causes --variables to be silently ignored at render #2141

Description

@AleisterCrowlley

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

  1. Don't animate volume on the timeline; bake the fade into the audio file, or
    use a static data-volume.
  2. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions