Skip to main content
A composition is an HTML file that describes a video. You write elements, give them data-* timing attributes, and the framework turns them into frames. index.html is the top-level composition. It can hold other compositions inside it. There is no special “root” type — any composition can be imported into any other.

How an HTML file becomes frames

Here is a whole video: a logo, then a video clip.
index.html
Two rules make that work. The outer element needs data-composition-id, and every timed element needs class="clip" so the runtime can hide it outside its own window of time. The clips add up to five seconds. At 30 frames per second — the default — that is 150 frames, numbered 0 to 149: Nothing plays in real time during a render. The renderer asks for frame 0, then frame 1, and each answer is a single still picture. That is what makes rendering deterministic.

What can go on the timeline

A clip is any element with timing attributes on it:
  • <video> — video clips, B-roll, A-roll
  • <img> — stills and overlays
  • <audio> — music and sound effects
  • <div data-composition-id="..."> — another composition nested inside this one
The Data attributes page lists every timing attribute. The HTML schema has the full contract.

Put one composition inside another

You can either point at a separate file or write the nested composition inline. Use a separate file when you want to reuse it.
data-composition-src names the file. The framework fetches it, pulls the content out of its <template> tag, mounts it, runs its scripts, and registers its timeline.Paths resolve from the project root, not from the file doing the referencing. So a composition nested one level deep still writes compositions/foo.html, never ../compositions/foo.html.
index.html
The file it points at wraps everything in a <template>:
compositions/intro-anim.html
data-playback-start picks which moment of the child timeline shows first. It defaults to 0. Trimming or splitting from the left pushes this offset forward by the time elapsed multiplied by data-playback-rate, so the nested animation keeps going instead of jumping back to its beginning.

Where the files live

project
index.html
compositions
intro-anim.html
caption-overlay.html
outro-title.html

HTML sets the timing, scripts do the motion

Your HTML says what plays, when, and on which track, all through data attributes. Your scripts handle the creative part: effects, transitions, canvas, SVG, GSAP animation.
Never use a script to play, pause, or seek a media element, and never use one to show or hide a clip based on time. The framework already does that from the data attributes, and a script doing it too will fight the framework. See Common Mistakes for what that looks like.

Reuse one composition with different content

One source file can appear several times in the same video, each copy carrying its own text and colors. HyperFrames does not wire data-var-* attributes into your DOM or CSS for you — you do it in three steps:
  1. Declare each variable — id, type, default — on the sub-composition’s root with data-composition-variables. That root is the <html> element in a full-document composition, or the [data-composition-id] element in a template or fragment.
  2. Pass each copy’s values on its host element with data-variable-values.
  3. Read them inside the composition with window.__hyperframes.getVariables(), which layers the host’s values over the declared defaults, one copy at a time.
index.html
The second card’s data-start="card-pro" means “start when that one ends”. And the source file both cards share:
compositions/card.html
Variables covers the types, the bindings that need no script, CLI overrides, and which value wins. If you are building tooling on @hyperframes/core, extractCompositionMetadata() reads the same data-composition-variables array — that is how Studio builds its editing UI.

See every composition in a project