mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
I stripped the audio track with -an when encoding for the web. The source has a real mix at -19.2 dB mean / -3.1 dB peak, and the docs player is click-to-play, so there is no reason for it to be silent. Re-encoded with AAC at 128k; levels match the source exactly. The other published films have no audio track at all to preserve — they were rendered silent at source.
94 lines
2.7 KiB
Plaintext
94 lines
2.7 KiB
Plaintext
---
|
|
title: "Frame adapters"
|
|
description: "Connect a seekable animation timeline to a custom HyperFrames host."
|
|
---
|
|
|
|
<Info>
|
|
The exported `FrameAdapter` interface is experimental v0 API. Its signatures
|
|
may change before v1.
|
|
</Info>
|
|
|
|
A frame adapter answers one question: what state should an animation have at frame N?
|
|
|
|
Most composition authors do not implement this interface. HyperFrames already seeks registered GSAP, CSS, Anime.js, Lottie, Three.js, Web Animations, and TypeGPU animation through its browser runtime. Use the [GSAP guide](/guides/gsap-animation) for the normal authoring path.
|
|
|
|
Use `FrameAdapter` when you are building a custom host around a seekable animation object.
|
|
|
|
Rendering never plays the animation. It asks for one frame, waits, captures it,
|
|
then asks for the next — so the same frame always comes out the same.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant R as Renderer
|
|
participant A as Frame adapter
|
|
participant T as Your timeline
|
|
R->>A: getDurationFrames()
|
|
A-->>R: 120
|
|
loop every frame, in any order
|
|
R->>A: seekFrame(90)
|
|
A->>T: jump to 3.0s and settle
|
|
T-->>A: state at 3.0s
|
|
A-->>R: ready
|
|
R->>R: capture the pixels
|
|
end
|
|
R->>A: destroy()
|
|
```
|
|
|
|
## Interface
|
|
|
|
```ts
|
|
import type { FrameAdapter, FrameAdapterContext } from "@hyperframes/core";
|
|
|
|
type FrameAdapter = {
|
|
id: string;
|
|
init?: (context: FrameAdapterContext) => Promise<void> | void;
|
|
getDurationFrames: () => number;
|
|
seekFrame: (frame: number) => Promise<void> | void;
|
|
destroy?: () => Promise<void> | void;
|
|
};
|
|
```
|
|
|
|
The context contains the composition ID, frame rate, dimensions, and optional root element.
|
|
|
|
## Adapt a GSAP timeline
|
|
|
|
`@hyperframes/core` includes a helper for a GSAP-like timeline:
|
|
|
|
```ts
|
|
import { createGSAPFrameAdapter } from "@hyperframes/core";
|
|
|
|
const adapter = createGSAPFrameAdapter({
|
|
id: "intro",
|
|
fps: 30,
|
|
timeline,
|
|
});
|
|
|
|
await adapter.init?.({
|
|
compositionId: "intro",
|
|
fps: 30,
|
|
width: 1920,
|
|
height: 1080,
|
|
});
|
|
|
|
await adapter.seekFrame(90); // three seconds
|
|
```
|
|
|
|
The helper pauses the timeline, derives its frame length, and converts each frame request to seconds.
|
|
|
|
## Contract
|
|
|
|
A custom adapter must:
|
|
|
|
- return a finite, non-negative frame count;
|
|
- support forward, backward, and random seeks;
|
|
- return the same state when the same frame is requested again;
|
|
- avoid wall-clock timers and unseeded randomness;
|
|
- finish asynchronous work before the frame is captured;
|
|
- release listeners and other resources in `destroy()`.
|
|
|
|
The host still owns the capture and encoding pipeline. The adapter owns only the animation state.
|
|
|
|
## Continue
|
|
|
|
Read [Deterministic rendering](/concepts/determinism) for the timing rules or [`@hyperframes/core`](/packages/core) for the package exports.
|