* fix(core): paint no timed clip until the runtime decides its visibility Until its first visibility pass, a paused page showed every timed clip at once: an embed without a loader flashed a later scene before the first, and a player's hidden iframe still decoded every scene's images. The runtime now hides timed non-media clips from the moment its script runs, and the first pass that decides them removes that rule, so each clip is shown only by the pass. * refactor(core): name media with the realm-safe helper in the first-pass check * fix(core): share the first-pass hide across runtime copies and shader captures A second evaluation of the runtime (a sub-composition carrying its own runtime tag, or a page that includes it twice) added a hide rule nothing removed, so the film stayed invisible. The rule and its flag now live on the page: a copy that finds either, or finds the runtime already initialised, adds none, and the first pass removes every copy. HyperShader's capture clone forces its scene visible with !important, so a transition captured before the first pass still shows the scene's clips. The runtime test now also pins evaluation-time hiding, inline visibility, media, and two copies. * fix(core): grade media inside a clip once the first pass shows it Color grading registers only media it can see. Media with no timing of its own inside a timed clip inherited the first-pass hide, so grading skipped it, and lifting the rule is not a body mutation, so nothing registered it again: the image or video stayed ungraded. The pass now refreshes grading when it lifts the rule. The second-copy test now asserts on a paused page, before any seek. * chore(core): keep the comment share of the first-pass hide files at or under their base The comment ratchet counts one added comment line each in entry.ts, init.ts and capture.ts, and a new file above its package's share. The three lines are gone, and the one note that has to stay (why the hide runs at script evaluation) now lives in the new module's shorter header.
@hyperframes/core
Types, parsers, generators, compiler, linter, runtime, and frame adapters for the Hyperframes video framework.
Install
npm install @hyperframes/core
Most users don't need to install core directly — the CLI, producer, and studio packages depend on it internally.
What's inside
| Module | Description |
|---|---|
| Types | TimelineElement, CompositionSpec, Asset, canvas dimensions, defaults |
| Parsers | parseHtml — extract timeline elements from HTML; parseGsapScript — parse GSAP animations |
| Generators | generateHyperframesHtml — produce valid Hyperframes HTML from a composition spec |
| Compiler | compileTimingAttrs — resolve data-start / data-duration into absolute times |
| Linter | lintHyperframeHtml — validate Hyperframes HTML (missing attributes, overlapping tracks, etc.) |
| Runtime | IIFE script injected into the browser — manages seek, media playback, and the window.__hf protocol |
| Frame Adapters | Pluggable animation drivers (GSAP, Lottie, CSS, or custom) |
Generated composition trust
Composition generators require trusted authors for code-bearing inputs. styles and
generateHyperframesStyles preserve authored CSS, which can load external resources.
animations may contain __raw: values that are emitted as JavaScript;
includeScripts: true includes executable timeline code. serializeGsapAnimations
also accepts raw preamble, postamble, and a code-bearing timelineVar. Never fill
these inputs with untrusted data. Attribute encoding and closing-tag containment
are not a sandbox; render untrusted compositions in an appropriately isolated
execution environment and never serve them on a privileged origin.
Text content retains the supported inline-formatting sanitizer contract. The clip parser intentionally flattens inner formatting to text, so parse/generate is not a lossless replacement for editing the source HTML.
Frame Adapters
A frame adapter tells the engine how to seek your animation to a specific frame:
import { createGSAPFrameAdapter } from "@hyperframes/core";
const adapter = createGSAPFrameAdapter({
getTimeline: () => gsap.timeline(),
compositionId: "my-video",
});
Implement FrameAdapter for custom animation runtimes:
import type { FrameAdapter } from "@hyperframes/core";
const myAdapter: FrameAdapter = {
id: "my-adapter",
getDurationFrames: () => 300,
seekFrame: (frame) => {
/* seek your animation */
},
};
Parsing and generating HTML
import { parseHtml, generateHyperframesHtml } from "@hyperframes/core";
const { elements, metadata } = parseHtml(htmlString);
const html = generateHyperframesHtml(spec);
Linting
import { lintHyperframeHtml } from "@hyperframes/core/lint";
const result = lintHyperframeHtml(htmlString);
// result.findings: { severity, message, elementId }[]
Documentation
Full documentation: hyperframes.heygen.com/packages/core
Related packages
@hyperframes/engine— rendering engine that drives the browser@hyperframes/producer— full render pipeline (capture + encode)hyperframes— CLI