Files
hyperframes/docs/concepts/frame-adapters.mdx
JamesandClaude Opus 4.6 915fe2f47a docs: improve quality based on Remotion/Stripe/Tailwind patterns
Major improvements across all 18 pages:

- Use Mintlify components: <Steps> for tutorials, <Tabs> for alternatives,
  <CodeGroup> for multi-platform commands, <Tree> for directory structures,
  <AccordionGroup> for FAQ/scannable content, <Mermaid> for diagrams
- Add filename annotations to all code blocks (e.g., ```html index.html)
- Add numbered comments inside multi-step code examples
- Show expected terminal output after CLI commands
- Add "When to use" / "When NOT to use" sections to all package pages
- Add "Next Steps" CardGroup to every page (no dead-end pages)
- Cross-link between pages at point of curiosity (not just "see also" dumps)
- Expand thin pages (engine, studio) with architecture details and examples
- Add decision guides (rendering modes, template selection)
- Use <Warning> and <Note> sparingly (max 2-3 per page)

Also adds DOCS_GUIDELINES.md at repo root with writing standards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 23:57:01 +00:00

145 lines
4.6 KiB
Plaintext

---
title: Frame Adapters
description: "Bring your own animation runtime to Hyperframes."
---
The Frame Adapter pattern is how Hyperframes supports multiple animation runtimes. The core question every adapter answers:
> What should the screen look like at frame N?
If a runtime can answer that, it can plug into Hyperframes.
<Info>
The Adapter API is currently at **v0** (experimental). Breaking changes are possible until v1. The core contract (seek-by-frame, deterministic output) is stable, but method signatures may evolve.
</Info>
## How It Works
The host application (the [engine](/packages/engine) or [producer](/packages/producer)) drives rendering by calling adapter methods in a strict sequence. The adapter never controls its own clock -- it only responds to seek commands.
```mermaid
sequenceDiagram
participant Host as Host (Engine)
participant Adapter as Frame Adapter
participant Chrome as Chrome / Browser
Host->>Adapter: init(context)
Adapter-->>Host: ready
Host->>Adapter: getDurationFrames()
Adapter-->>Host: 300 frames
loop For each frame 0..300
Host->>Host: normalize frame (clamp, floor)
Host->>Adapter: seekFrame(frame)
Adapter->>Chrome: Update DOM / canvas state
Adapter-->>Host: done
Host->>Chrome: Capture pixel buffer
end
Host->>Adapter: destroy()
Adapter-->>Host: cleaned up
```
## Adapter API (v0)
```typescript adapters/types.ts
type FrameAdapterContext = {
compositionId: string;
fps: number;
width: number;
height: number;
rootElement?: HTMLElement;
};
type FrameAdapter = {
id: string;
init?: (ctx: FrameAdapterContext) => Promise<void> | void;
getDurationFrames: () => number;
seekFrame: (frame: number) => Promise<void> | void;
destroy?: () => Promise<void> | void;
};
```
## Required Semantics
- `getDurationFrames()` must return a finite integer >= 0
- `seekFrame(frame)` must support arbitrary seek order (forward, backward, random)
- `seekFrame(frame)` must be idempotent for the same input frame
- `seekFrame(frame)` must clamp internal time to the adapter's range
- Adapters should be paused/seek-driven, not clock-driven
## Host Orchestration
The host normalizes frames before calling the adapter:
```typescript engine/render-loop.ts
normalizedFrame = clamp(Math.floor(frame), 0, durationFrames);
```
A typical render loop:
```typescript engine/render-loop.ts
await adapter.init?.({ compositionId, fps, width, height, rootElement });
const durationFrames = adapter.getDurationFrames();
for (let frame = 0; frame <= durationFrames; frame += 1) {
await adapter.seekFrame(frame);
// capture pixel buffer for this frame
}
await adapter.destroy?.();
```
## Determinism Contract
These rules are non-negotiable for any adapter. They are the foundation of Hyperframes' [deterministic rendering](/concepts/determinism) guarantee.
- Canonical clock: `t = frame / fps`
- No wall-clock dependencies (`Date.now`, drift-dependent logic)
- No unseeded randomness
- No render-time network fetches
- Fixed output params (`fps`, `width`, `height`)
- Finite duration only
- Deterministic frame quantization before seek
## Supported Runtimes
First-party adapters:
| Runtime | Seek Method | Status |
|---------|------------|--------|
| [GSAP](/guides/gsap-animation) | `timeline.seek(frame / fps)` | Available |
| CSS/WAAPI | `animation.currentTime` | Planned |
| Lottie | Set animation frame/progress | Planned |
| Three.js/WebGL | Compute deterministic scene state | Planned |
| SVG/Anime | Implement seek + duration contract | Planned |
Community adapters are welcome -- if it can seek by frame, it belongs in Hyperframes.
## Conformance Tests
Every adapter should pass these minimum tests:
1. **Repeatability** -- seek same frame twice, get identical output
2. **Random seek** -- seek order `[90, 10, 50, 10]` produces deterministic results
3. **Bounds** -- negative and overflow frame values do not break
4. **Duration** -- returned duration is a finite integer
5. **Cleanup** -- no leaked timers/listeners after `destroy`
## Next Steps
<CardGroup cols={2}>
<Card title="Deterministic Rendering" icon="lock" href="/concepts/determinism">
Understand the determinism guarantees adapters must uphold
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
See the first-party GSAP adapter in action
</Card>
<Card title="@hyperframes/engine" icon="gear" href="/packages/engine">
The capture engine that drives adapters during rendering
</Card>
<Card title="Contributing" icon="code-branch" href="/contributing">
Build and contribute your own adapter
</Card>
</CardGroup>