* feat: check names the filter fallback, the render log names the tier, Studio stops opting out hyperframes check reports motion_blur_filter_fallback (info) with the reason capture could not run and the way back to it. The engine's per-session log line gains tier=, and its pixel test covers the fallback over a WebGL page. Studio no longer sends previewMotionBlur: every Chrome can preview blur now, through the fallback where it lacks html-in-canvas; the server still accepts false from an older Studio. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(core): blur picks the cheaper filter tier when every mover only translates The scan now notes whether any mover turns or scales (the linear part of its transform chain changes) and whether it already forms a stacking context while it moves. When every mover only translates, the filter tier draws as well as capture and costs about what no blur costs: on an 8 s bench shot a render took 7.0 s against 9.9 s with capture and 7.3 s with no blur. Anything that turns or scales, or moves without a transform, stays on capture. data-motion-blur-tier="capture" or "filter" forces a tier. The report says which one drew and why (translation-only, tier-attribute); hyperframes check stays quiet for those two, which are choices, not fallbacks. The pixel tests run the shared assertions through both tiers. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * docs(motion-blur): the two tiers, the tier attribute, and what changed in limits The guide described blur turning off for WebGL, a root transform and similar. It now says the filter tier draws instead, how the tier is chosen and forced, what each does with a turn or a zoom, the measured cost of each, and which findings check emits. The skill note drops the off-for-WebGL advice and the 1.7x figure. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(core): blur defaults match a 240 degree shutter and at least 32 samples The shutter opens 240 degrees by default (was 180) and each mover gets at least 32 taps (was sized to the smear alone, down to 2), growing with the smear as before. These are the settings the shutter blur Jake built uses; on the card whip the default render is now 51 dB from his (34.6 dB at the old defaults). The guide's attribute table says so. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * chore(core): trim tier.ts comments to the ratchet Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * chore: allow deleting the Studio previewMotionBlur render test Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@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