Files
wehub-resource-sync 85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

6.1 KiB

Variables and Media

Two separate concerns, grouped because both control "what flows in from outside the HTML": runtime parameters (variables) and external media files (video/audio).

Variables

Declare variables on the <html> element with data-composition-variables. Each declaration needs id, type, label, and default:

<html
  data-composition-variables='[
    {"id":"title","type":"string","label":"Title","default":"Hello"},
    {"id":"accent","type":"color","label":"Accent","default":"#66d9ef"}
  ]'
></html>

Prefer declarative bindings — no script needed for direct substitution:

<img class="clip" data-start="0" data-duration="5" data-var-src="heroImage" src="fallback.jpg" />
<h1 class="clip" data-start="0" data-duration="5" data-var-text="title">Fallback</h1>
<style>
  .card {
    color: var(--accent);
  }
</style>
  • data-var-src="id" substitutes the element's src (URL string or image {url}); the authored src is the fallback.
  • data-var-text="id" substitutes the element's own text; element children (nested clips, animated spans) are preserved.
  • Every scalar variable is applied automatically as a --{id} CSS custom property on the composition root, so var(--id) CSS responds to overrides — no setProperty boilerplate.
  • Bindings resolve identically in preview and render, and per-instance for sub-compositions.
  • Caveat: media with audio should keep a real fallback src — render audio extraction reads the authored attribute (lint: media_variable_src_no_fallback).

For logic beyond direct substitution (loops, conditionals, derived values), read values once during initialization:

const { title, accent } = window.__hyperframes.getVariables();
document.getElementById("title").textContent = title;

Variable Rules

  • Supported types and their extra options (consumed by Studio's editing UI):
    • string — optional placeholder, maxLength
    • number — optional min, max, step, unit
    • color — none
    • boolean — none
    • enumrequired options: [{ "value": "...", "label": "..." }, ...]
  • Always provide useful default values so preview works without CLI overrides.
  • Use data-variable-values='{"title":"Pro"}' on sub-composition hosts for per-instance overrides.
  • Use npx hyperframes render --variables '{"title":"Q4 Report"}' or --variables-file for render-time overrides.
  • Add --strict-variables in CI: turns undeclared keys, type mismatches, and enum values not in options into errors instead of warnings.
  • Read values once during init, not on every animation tick — variables don't change mid-render.
  • Media color grading can use exact variable references inside data-color-grading JSON. Use $gradingPreset or ${gradingIntensity} as the whole field value; the runtime resolves it from the current composition's variables before applying shader adjustments, finishing details, blur/pixelate effects, and custom LUTs.

Two JSON Shapes (Easy to Confuse)

  • data-composition-variables is an array of declarations (the schema): [{id, type, label, default}, ...]
  • --variables and data-variable-values are objects keyed by id (the values): { title: "Q4", accent: "#fff" }

Media

NON-NEGOTIABLE: <video>/<audio> must be a DIRECT child of the host composition root (index.html). The runtime only registers + drives media that is a direct root child. Media placed inside a sub-composition <template>, or wrapped in any intermediate <div>, is never seeked/decoded → renders blank (paper/white) or black. lint/validate/inspect do not catch this; per-frame snapshot shows the blank panel.

Consequences:

  • A scene-specific clip still lives at the host root, not in the scene's sub-comp. The sub-comp keeps only the frame/shell; the media is a sibling host element positioned over it.
  • A sub-composition cannot reach or animate host elements — neither document.querySelector("#host-id") nor a gsap selector string (tl.to("#host-id", …)) resolves across the boundary; a sub-comp timeline only drives its own subtree. So all per-scene motion on host media (scale/opacity/morph/tilt/breathing) must be authored on the MAIN timeline in index.html, at GLOBAL time (scene-local time + the scene slot's data-start). For 3D tilt without a perspective parent, use gsap transformPerspective on the element. See composition-patterns.md archetype B.

Video elements must be muted and inline. Audio must be a separate <audio> element, even when it uses the same source file.

<video
  id="a-roll"
  class="clip"
  src="assets/demo.mp4"
  data-start="0"
  data-duration="12"
  data-track-index="0"
  muted
  playsinline
></video>

<audio
  id="a-roll-audio"
  src="assets/demo.mp4"
  data-start="0"
  data-duration="12"
  data-track-index="10"
  data-volume="1"
></audio>

Media Rules

  • Do not call video.play(), audio.play(), pause, or seek in composition code. HyperFrames owns playback.
  • Do not place media inside a sub-comp <template> or any wrapper <div> — direct host-root child only (see above), else it never decodes.
  • Do not drive host media from a sub-comp timeline — it has no effect. Drive it from the main timeline at global time.
  • Do not animate timed media element dimensions; animate a non-timed wrapper instead.
  • Do not nest video inside a timed wrapper. Put timing on the media element or keep the wrapper untimed.
  • Add crossorigin="anonymous" for external media that needs canvas capture or pixel inspection.
  • Audio always lives on a separate <audio> element — even if its source file is the same as a <video>. The <video> is muted; the <audio> carries sound.
  • For volume fades/ducking, animate volume on the timeline (tl.to("#bgm", { volume: 0, duration: 1 }, "outro")) rather than swapping data-volume. The runtime probes the timeline's volume keyframes and applies them identically in preview and render; data-volume is the static baseline for elements no tween touches.

For media duration: <video> and <audio> can omit data-duration if the media's intrinsic length is known and you want the full clip. Otherwise provide data-duration explicitly.