mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-06 02:41:48 +00:00
feat(skill): add hyperframes optional creative skill
Adds an optional creative skill that integrates HyperFrames, an HTML-based video rendering framework, as a sibling to manim-video. Complements manim's math-focused animation with motion-graphics, captioned narration, audio-reactive visuals, shader transitions, and website-to-video production. Scope: - optional-skills/creative/hyperframes/SKILL.md — entry point - references/composition.md — data-attr schema, timeline contract - references/cli.md — every npx hyperframes command - references/gsap.md — GSAP core API for compositions - references/website-to-video.md — 7-step capture-to-video workflow - references/troubleshooting.md — OpenClaw / Chromium 147 fix - scripts/setup.sh — idempotent one-time setup OpenClaw / Chromium 147 fix (hyperframes#294): Pinning hyperframes@>=0.4.2 (commit 4c72ba4 ships the HeadlessExperimental.beginFrame auto-detect + screenshot fallback). setup.sh pre-caches chrome-headless-shell so the fast BeginFrame path is preferred over system Chrome. The PRODUCER_FORCE_SCREENSHOT=true escape hatch is documented in troubleshooting.md and in SKILL.md Pitfalls. Placed under optional-skills/ (not bundled) per CONTRIBUTING.md guidance for heavyweight deps: requires Node.js >= 22, FFmpeg, and ~300 MB chrome-headless-shell download.
This commit is contained in:
parent
8fabef9d35
commit
50aabb9eb2
7 changed files with 993 additions and 0 deletions
145
optional-skills/creative/hyperframes/references/cli.md
Normal file
145
optional-skills/creative/hyperframes/references/cli.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# HyperFrames CLI
|
||||
|
||||
Everything runs through `npx hyperframes` (or the globally-installed `hyperframes` after `npm install -g hyperframes`). Requires Node.js >= 22 and FFmpeg.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Scaffold** — `npx hyperframes init my-video`
|
||||
2. **Write** — author HTML composition (see `composition.md`)
|
||||
3. **Lint** — `npx hyperframes lint`
|
||||
4. **Preview** — `npx hyperframes preview`
|
||||
5. **Render** — `npx hyperframes render`
|
||||
|
||||
Always lint before preview/render — catches missing `data-composition-id`, overlapping tracks, and unregistered timelines.
|
||||
|
||||
## init — Scaffold a Project
|
||||
|
||||
```bash
|
||||
npx hyperframes init my-video # interactive wizard
|
||||
npx hyperframes init my-video --example warm-grain # pick an example template
|
||||
npx hyperframes init my-video --video clip.mp4 # seed with a video file
|
||||
npx hyperframes init my-video --audio track.mp3 # seed with an audio file
|
||||
npx hyperframes init my-video --non-interactive # skip prompts (CI / agent use)
|
||||
```
|
||||
|
||||
Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`.
|
||||
|
||||
`init` creates the correct file structure, copies media, transcribes audio with Whisper, and installs authoring skills. Use it instead of creating files by hand.
|
||||
|
||||
## lint
|
||||
|
||||
```bash
|
||||
npx hyperframes lint # current directory
|
||||
npx hyperframes lint ./my-project # specific project
|
||||
npx hyperframes lint --verbose # include info-level findings
|
||||
npx hyperframes lint --json # machine-readable output
|
||||
```
|
||||
|
||||
Lints `index.html` and all files in `compositions/`. Reports errors (must fix), warnings (should fix), and info (only with `--verbose`).
|
||||
|
||||
## preview
|
||||
|
||||
```bash
|
||||
npx hyperframes preview # serve current directory (port 3002)
|
||||
npx hyperframes preview --port 4567 # custom port
|
||||
```
|
||||
|
||||
Hot-reloads on file changes. Opens the Studio in your browser automatically.
|
||||
|
||||
## render
|
||||
|
||||
```bash
|
||||
npx hyperframes render # standard MP4
|
||||
npx hyperframes render --output final.mp4 # named output
|
||||
npx hyperframes render --quality draft # fast iteration
|
||||
npx hyperframes render --fps 60 --quality high # final delivery
|
||||
npx hyperframes render --format webm # transparent WebM
|
||||
npx hyperframes render --docker # byte-identical reproducible render
|
||||
```
|
||||
|
||||
| Flag | Options | Default | Notes |
|
||||
| -------------- | ----------------------- | ------------------------------ | --------------------------- |
|
||||
| `--output` | path | `renders/<name>_<timestamp>.mp4` | Output path |
|
||||
| `--fps` | 24, 30, 60 | 30 | 60fps doubles render time |
|
||||
| `--quality` | `draft`, `standard`, `high` | standard | draft for iterating |
|
||||
| `--format` | `mp4`, `webm` | mp4 | WebM supports transparency |
|
||||
| `--workers` | 1–8 or `auto` | auto | Each spawns Chrome |
|
||||
| `--docker` | flag | off | Reproducible output |
|
||||
| `--gpu` | flag | off | GPU-accelerated encoding |
|
||||
| `--strict` | flag | off | Fail on lint errors |
|
||||
| `--strict-all` | flag | off | Fail on errors AND warnings |
|
||||
|
||||
**Quality guidance:** `draft` while iterating, `standard` for review, `high` for final delivery.
|
||||
|
||||
## transcribe
|
||||
|
||||
```bash
|
||||
npx hyperframes transcribe audio.mp3
|
||||
npx hyperframes transcribe video.mp4 --model medium.en --language en
|
||||
npx hyperframes transcribe subtitles.srt # import existing
|
||||
npx hyperframes transcribe subtitles.vtt
|
||||
npx hyperframes transcribe openai-response.json
|
||||
```
|
||||
|
||||
Produces word-level timings suitable for caption components. First run downloads the Whisper model (cached after).
|
||||
|
||||
## tts
|
||||
|
||||
```bash
|
||||
npx hyperframes tts "Text here" --voice af_nova --output narration.wav
|
||||
npx hyperframes tts script.txt --voice bf_emma
|
||||
npx hyperframes tts --list # show all voices
|
||||
```
|
||||
|
||||
Uses Kokoro (local, no API key). Voice prefixes: `af_` (American female), `am_` (American male), `bf_` (British female), `bm_` (British male).
|
||||
|
||||
## doctor
|
||||
|
||||
```bash
|
||||
npx hyperframes doctor
|
||||
```
|
||||
|
||||
Verifies environment:
|
||||
- Node.js >= 22
|
||||
- FFmpeg present on PATH
|
||||
- Available RAM (renders are memory-hungry — 4 GB minimum)
|
||||
- Chrome binary resolution (`chrome-headless-shell` preferred over system Chrome)
|
||||
- Current `hyperframes` version
|
||||
|
||||
Run this **first** when a render fails. See `troubleshooting.md` for interpreting the output.
|
||||
|
||||
## browser
|
||||
|
||||
```bash
|
||||
npx hyperframes browser --install # install the bundled chrome-headless-shell
|
||||
npx hyperframes browser --path # print the resolved browser binary path
|
||||
npx hyperframes browser --clean # clear the bundled browser cache
|
||||
```
|
||||
|
||||
## info
|
||||
|
||||
```bash
|
||||
npx hyperframes info
|
||||
```
|
||||
|
||||
Prints version, Node version, FFmpeg version, OS, and resolved browser path — useful in bug reports.
|
||||
|
||||
## upgrade
|
||||
|
||||
```bash
|
||||
npx hyperframes upgrade -y
|
||||
```
|
||||
|
||||
Check for and install updates. Run this if you hit `HeadlessExperimental.beginFrame` errors — the auto-detect fix shipped in `hyperframes@0.4.2` (commit 4c72ba4, March 2026).
|
||||
|
||||
## Other
|
||||
|
||||
```bash
|
||||
npx hyperframes compositions # list compositions in the project
|
||||
npx hyperframes docs # open documentation in browser
|
||||
npx hyperframes benchmark . # benchmark render performance
|
||||
npx hyperframes add <block> # install a block/component from the catalog
|
||||
npx hyperframes add --list # browse the catalog
|
||||
```
|
||||
|
||||
Popular catalog blocks: `flash-through-white` (shader transition), `instagram-follow` (social overlay), `data-chart` (animated chart), `lower-third` (talking-head overlay). See [hyperframes.heygen.com/catalog](https://hyperframes.heygen.com/catalog).
|
||||
129
optional-skills/creative/hyperframes/references/composition.md
Normal file
129
optional-skills/creative/hyperframes/references/composition.md
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# Composition Authoring
|
||||
|
||||
HTML structure, data attributes, timeline contract, and non-negotiable rules.
|
||||
|
||||
## Root Structure
|
||||
|
||||
Standalone `index.html` — the top-level composition. **Does NOT use `<template>`**. Put the `data-composition-id` div directly in `<body>`.
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<div
|
||||
id="stage"
|
||||
data-composition-id="root"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<!-- clips go here -->
|
||||
<video id="clip-1" data-start="0" data-duration="5" data-track-index="0" src="intro.mp4" muted playsinline></video>
|
||||
<img id="logo" data-start="2" data-duration="3" data-track-index="1" src="logo.png" />
|
||||
<audio id="music" data-start="0" data-duration="10" data-track-index="2" data-volume="0.5" src="music.wav"></audio>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
tl.from("#logo", { opacity: 0, y: 40, duration: 0.6 }, 2);
|
||||
window.__timelines["root"] = tl;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Sub-compositions loaded via `data-composition-src` **DO** use `<template>`:
|
||||
|
||||
```html
|
||||
<template id="my-comp-template">
|
||||
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
|
||||
<!-- content + scoped <style> + <script> with window.__timelines["my-comp"] -->
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Load from the root: `<div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>`
|
||||
|
||||
## Data Attributes
|
||||
|
||||
### All clips
|
||||
|
||||
| Attribute | Required | Values |
|
||||
| ------------------ | --------------------------------- | ------------------------------------------------------ |
|
||||
| `id` | Yes | Unique identifier |
|
||||
| `data-start` | Yes | Seconds, or clip ID reference (`"el-1"`, `"intro + 2"`) |
|
||||
| `data-duration` | Required for img/div/compositions | Seconds. Video/audio defaults to media duration. |
|
||||
| `data-track-index` | Yes | Integer. Same-track clips cannot overlap. |
|
||||
| `data-media-start` | No | Trim offset into source (seconds) |
|
||||
| `data-volume` | No | 0–1 (default 1) |
|
||||
|
||||
`data-track-index` controls timeline layout only — **not** visual layering. Use CSS `z-index` for layering.
|
||||
|
||||
### Composition clips
|
||||
|
||||
| Attribute | Required | Values |
|
||||
| ---------------------------- | -------- | -------------------------------------------- |
|
||||
| `data-composition-id` | Yes | Unique composition ID |
|
||||
| `data-start` | Yes | Start time (root composition: `"0"`) |
|
||||
| `data-duration` | Yes | Takes precedence over GSAP timeline duration |
|
||||
| `data-width` / `data-height` | Yes | Pixel dimensions (1920x1080 or 1080x1920) |
|
||||
| `data-composition-src` | No | Path to external HTML file |
|
||||
|
||||
## Timeline Contract
|
||||
|
||||
- Every timeline starts `{ paused: true }` — the player controls playback.
|
||||
- Register every timeline: `window.__timelines["<composition-id>"] = tl`.
|
||||
- Duration comes from `data-duration`, not from the GSAP timeline length.
|
||||
- Framework auto-nests sub-timelines — do NOT manually add them.
|
||||
- Never create empty tweens just to set duration.
|
||||
|
||||
## Non-Negotiable Rules
|
||||
|
||||
1. **Deterministic.** No `Math.random()`, `Date.now()`, or time-based logic. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-randomness.
|
||||
2. **GSAP only on visual properties.** `opacity`, `x`, `y`, `scale`, `rotation`, `color`, `backgroundColor`, `borderRadius`, transforms. Never animate `visibility`, `display`, or call `video.play()`/`audio.play()`.
|
||||
3. **No property conflicts across timelines.** Never animate the same property on the same element from multiple timelines simultaneously.
|
||||
4. **No `repeat: -1`.** Infinite-repeat tweens break the capture engine. Compute `repeat: Math.ceil(duration / cycleDuration) - 1`.
|
||||
5. **Synchronous timeline construction.** Never build timelines inside `async`/`await`, `setTimeout`, or Promises. The capture engine reads `window.__timelines` synchronously after page load. Fonts are embedded by the compiler — no need to wait for load.
|
||||
6. **Root composition has no `<template>` wrapper.** Only sub-compositions use `<template>`.
|
||||
7. **Video is always `muted playsinline`.** Audio is always a separate `<audio>` element — even if it's the same source file.
|
||||
8. **Content containers use padding, not absolute positioning.** `.scene-content { width: 100%; height: 100%; padding: Npx; display: flex; flex-direction: column; gap: Npx; box-sizing: border-box }`. Absolute-positioned content containers overflow. Reserve `position: absolute` for decoratives only.
|
||||
|
||||
## Scene Transitions
|
||||
|
||||
Multi-scene compositions MUST follow all of these:
|
||||
|
||||
1. **Always use a transition between scenes.** No jump cuts.
|
||||
2. **Always use entrance animations** on every scene element. Every element animates IN via `gsap.from(...)`. No element may appear fully-formed.
|
||||
3. **Never use exit animations** (except on the final scene). This means NO `gsap.to()` that animates `opacity` to 0, `y` offscreen, etc. The transition IS the exit. Outgoing scene content must be fully visible at the moment the transition starts.
|
||||
4. **Final scene only:** may fade elements out. This is the only scene where `gsap.to(..., { opacity: 0 })` is allowed.
|
||||
|
||||
## Typography and Assets
|
||||
|
||||
- **Fonts:** write the `font-family` you want in CSS — the compiler embeds supported fonts automatically. Unsupported fonts produce a compiler warning.
|
||||
- Add `crossorigin="anonymous"` to external media.
|
||||
- For dynamic text sizing, use `window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })`.
|
||||
- All project files live at the project root alongside `index.html`. Sub-compositions reference assets with `../`.
|
||||
- For rendered video: 60px+ headlines, 20px+ body, 16px+ data labels. `font-variant-numeric: tabular-nums` on number columns. Avoid full-screen linear gradients on dark backgrounds (H.264 banding — use radial or solid + localized glow).
|
||||
|
||||
## Animation Guardrails
|
||||
|
||||
- Offset the first animation 0.1–0.3s (not `t=0`).
|
||||
- Vary eases across entrance tweens — at least 3 different eases per scene.
|
||||
- Don't repeat an entrance pattern within a scene.
|
||||
|
||||
## Never Do
|
||||
|
||||
1. Forget `window.__timelines` registration.
|
||||
2. Use video for audio — always muted video + separate `<audio>`.
|
||||
3. Nest video inside a timed div — use a non-timed wrapper.
|
||||
4. Use `data-layer` (use `data-track-index`) or `data-end` (use `data-duration`).
|
||||
5. Animate video element dimensions — animate a wrapper div instead.
|
||||
6. Call `play`/`pause`/`seek` on media — framework owns playback.
|
||||
7. Create a top-level container without `data-composition-id`.
|
||||
8. Use `repeat: -1` on any timeline or tween.
|
||||
9. Build timelines asynchronously.
|
||||
10. Use `gsap.set()` on elements from later scenes — they don't exist in the DOM at page load. Use `tl.set(selector, vars, timePosition)` inside the timeline at or after the clip's `data-start`.
|
||||
11. Use `<br>` in content text — causes unwanted extra breaks when the text wraps naturally. Use `max-width` instead. Exception: short display titles (e.g., "THE\nIMMORTAL\nGAME") where each word is deliberately on its own line.
|
||||
136
optional-skills/creative/hyperframes/references/gsap.md
Normal file
136
optional-skills/creative/hyperframes/references/gsap.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# GSAP for HyperFrames
|
||||
|
||||
GSAP is the animation engine for all HyperFrames compositions. Load from CDN inside the composition:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
```
|
||||
|
||||
## Core Tween Methods
|
||||
|
||||
- **`gsap.to(targets, vars)`** — animate from current state to `vars`. Most common.
|
||||
- **`gsap.from(targets, vars)`** — animate from `vars` to current state (entrances).
|
||||
- **`gsap.fromTo(targets, fromVars, toVars)`** — explicit start and end.
|
||||
- **`gsap.set(targets, vars)`** — apply immediately (duration 0). Don't use on clip elements that enter later — use `tl.set(selector, vars, time)` inside the timeline instead.
|
||||
|
||||
Always use **camelCase** property names (`backgroundColor`, `rotationX`, not `background-color`).
|
||||
|
||||
## Common vars
|
||||
|
||||
- **`duration`** — seconds (default 0.5).
|
||||
- **`delay`** — seconds before start.
|
||||
- **`ease`** — `"power1.out"` (default), `"power3.inOut"`, `"back.out(1.7)"`, `"elastic.out(1, 0.3)"`, `"none"`, `"expo.out"`, `"circ.inOut"`.
|
||||
- **`stagger`** — number `0.1` or object: `{ amount: 0.3, from: "center" }`, `{ each: 0.1, from: "random" }`.
|
||||
- **`overwrite`** — `false` (default), `true`, or `"auto"`.
|
||||
- **`repeat`** — number (never `-1` in HyperFrames). **`yoyo`** — alternates direction with repeat.
|
||||
- **`onComplete`**, **`onStart`**, **`onUpdate`** — callbacks.
|
||||
- **`immediateRender`** — default `true` for `from()`/`fromTo()`. Set `false` on later tweens targeting the same property+element to avoid overwrite surprises.
|
||||
|
||||
## Transforms
|
||||
|
||||
Prefer GSAP's transform aliases over raw CSS `transform`:
|
||||
|
||||
| GSAP property | Equivalent |
|
||||
| --------------------------- | -------------------------- |
|
||||
| `x`, `y`, `z` | translateX/Y/Z (px) |
|
||||
| `xPercent`, `yPercent` | translateX/Y (%) |
|
||||
| `scale`, `scaleX`, `scaleY` | scale |
|
||||
| `rotation` | rotate (deg) |
|
||||
| `rotationX`, `rotationY` | 3D rotate |
|
||||
| `skewX`, `skewY` | skew |
|
||||
| `transformOrigin` | transform-origin |
|
||||
|
||||
- **`autoAlpha`** — prefer over `opacity`. At 0, also sets `visibility: hidden`.
|
||||
- **CSS variables** — `"--hue": 180`.
|
||||
- **Directional rotation** — `"360_cw"`, `"-170_short"`, `"90_ccw"`.
|
||||
- **`clearProps`** — `"all"` or comma-separated; removes inline styles on complete.
|
||||
- **Relative values** — `"+=20"`, `"-=10"`, `"*=2"`.
|
||||
|
||||
## Function-based Values
|
||||
|
||||
```js
|
||||
gsap.to(".item", {
|
||||
x: (i, target, targets) => i * 50,
|
||||
stagger: 0.1,
|
||||
});
|
||||
```
|
||||
|
||||
## Easing
|
||||
|
||||
Built-in eases: `power1` through `power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`. Each has `.in`, `.out`, `.inOut`.
|
||||
|
||||
Rule of thumb:
|
||||
- Entrances: `power3.out`, `expo.out`, `back.out(1.4)`
|
||||
- Exits: `power2.in`, `expo.in`
|
||||
- Scrubbed sections: `none` (linear)
|
||||
- Vary eases across entrance tweens within a scene — at least 3 different eases.
|
||||
|
||||
## Defaults
|
||||
|
||||
```js
|
||||
gsap.defaults({ duration: 0.6, ease: "power2.out" });
|
||||
```
|
||||
|
||||
## Timelines (HyperFrames primary pattern)
|
||||
|
||||
```js
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
const tl = gsap.timeline({ paused: true, defaults: { duration: 0.6, ease: "power2.out" } });
|
||||
|
||||
tl.from(".title", { y: 50, opacity: 0 }, 0.3);
|
||||
tl.from(".subtitle", { y: 30, opacity: 0 }, 0.5);
|
||||
tl.from(".cta", { scale: 0.8, opacity: 0, ease: "back.out(1.7)" }, 0.8);
|
||||
|
||||
window.__timelines["root"] = tl;
|
||||
```
|
||||
|
||||
### Position parameter
|
||||
|
||||
Third argument to `.from()` / `.to()` / `.add()`:
|
||||
|
||||
- Absolute seconds: `0.5`, `2.1`.
|
||||
- Relative to end: `">+0.2"` (0.2s after previous), `"<"` (same time as previous), `"<+0.3"` (0.3s after previous's start).
|
||||
- Named labels: `tl.addLabel("act2", 5); tl.from(".x", { y: 30 }, "act2");`
|
||||
|
||||
### Nesting
|
||||
|
||||
HyperFrames auto-nests sub-composition timelines. **Do not** manually `tl.add(subTl)` — the framework wires sub-timelines into the parent at the sub-composition's `data-start`.
|
||||
|
||||
### Playback
|
||||
|
||||
The player controls playback. Don't call `tl.play()`, `tl.pause()`, or `tl.reverse()` at construction time. `{ paused: true }` is required.
|
||||
|
||||
## Stagger
|
||||
|
||||
```js
|
||||
// even distribution
|
||||
tl.from(".card", { opacity: 0, y: 40, stagger: 0.1 });
|
||||
|
||||
// control total amount
|
||||
tl.from(".card", { opacity: 0, stagger: { amount: 0.6, from: "center" } });
|
||||
|
||||
// deterministic "random" stagger (HyperFrames compositions must be deterministic)
|
||||
tl.from(".dot", { opacity: 0, stagger: { each: 0.05, from: "random" } });
|
||||
```
|
||||
|
||||
`stagger.from`: `"start"` | `"end"` | `"center"` | `"edges"` | `"random"` | index | `[x, y]` for grid.
|
||||
|
||||
## Performance
|
||||
|
||||
- Animate transforms (`x`, `y`, `scale`, `rotation`, `opacity`) — cheap, GPU-accelerated.
|
||||
- Avoid animating `width`, `height`, `top`, `left`, `margin` — causes layout thrash.
|
||||
- Avoid box-shadow or filter animations on large elements — expensive.
|
||||
- `will-change` is rarely needed; GSAP handles promotion.
|
||||
|
||||
## gsap.matchMedia (rarely needed in HyperFrames)
|
||||
|
||||
Compositions have fixed dimensions (`data-width`/`data-height`), so responsive breakpoints don't apply. You may still use `matchMedia` for `prefers-reduced-motion` when authoring UI previews, but it's not used in rendered video output.
|
||||
|
||||
## Don't Do
|
||||
|
||||
- `repeat: -1` anywhere — breaks the capture engine.
|
||||
- `Math.random()`, `Date.now()`, performance.now()` inside tween values — non-deterministic.
|
||||
- `async` / `setTimeout` / `Promise` around timeline construction — the capture engine reads `window.__timelines` synchronously.
|
||||
- Animate `visibility` or `display` directly — use `autoAlpha`.
|
||||
- `gsap.set()` on clip elements that enter later in the timeline — they don't exist in the DOM at page-load. Use `tl.set(sel, vars, time)` inside the timeline.
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
# Troubleshooting
|
||||
|
||||
## `HeadlessExperimental.beginFrame' wasn't found` (first thing to check)
|
||||
|
||||
**Symptom:** `npx hyperframes render` fails with:
|
||||
|
||||
```
|
||||
✗ Render failed
|
||||
Protocol error (HeadlessExperimental.beginFrame):
|
||||
'HeadlessExperimental.beginFrame' wasn't found
|
||||
```
|
||||
|
||||
**Cause:** Chromium 147+ removed the `HeadlessExperimental.beginFrame` CDP command. This affected sandbox environments (e.g., OpenClaw, some containerized agent hosts) that ship modern Chromium as the system browser. See [hyperframes#294](https://github.com/heygen-com/hyperframes/issues/294).
|
||||
|
||||
**Fix (permanent — preferred):** upgrade.
|
||||
|
||||
```bash
|
||||
npx hyperframes upgrade -y
|
||||
# or
|
||||
npm install -g hyperframes@latest
|
||||
```
|
||||
|
||||
`hyperframes >= 0.4.2` auto-detects whether the resolved browser supports `beginFrame` (checks for `chrome-headless-shell` in the binary path) and falls back to screenshot capture mode when it doesn't. Commit [`4c72ba4`](https://github.com/heygen-com/hyperframes/commit/4c72ba4a36ec2bd6733f7b9cb2a9e63f9fb234b9) (March 2026) shipped this auto-detect.
|
||||
|
||||
**Fix (escape hatch — if you can't upgrade):**
|
||||
|
||||
```bash
|
||||
export PRODUCER_FORCE_SCREENSHOT=true
|
||||
npx hyperframes render
|
||||
```
|
||||
|
||||
This forces screenshot mode regardless of the binary. Screenshot mode is slightly slower but visually identical.
|
||||
|
||||
**Fix (prevent — recommended):** install `chrome-headless-shell` so the engine can use the fast BeginFrame path:
|
||||
|
||||
```bash
|
||||
npx puppeteer browsers install chrome-headless-shell
|
||||
# or let the CLI do it
|
||||
npx hyperframes browser --install
|
||||
```
|
||||
|
||||
`scripts/setup.sh` runs this automatically.
|
||||
|
||||
## `npx hyperframes render` hangs for 120s then times out
|
||||
|
||||
**Cause:** the resolved browser is system Chrome (e.g., `/usr/bin/google-chrome`) and doesn't support the BeginFrame path, but auto-detect also missed it (older `hyperframes` version).
|
||||
|
||||
**Fix:**
|
||||
1. Check which binary is being used: `npx hyperframes browser --path`
|
||||
2. If it's system Chrome, either:
|
||||
- Install `chrome-headless-shell`: `npx hyperframes browser --install`, OR
|
||||
- Set the escape hatch: `export PRODUCER_FORCE_SCREENSHOT=true`, OR
|
||||
- Upgrade: `npx hyperframes upgrade -y`
|
||||
|
||||
## `ffmpeg: command not found`
|
||||
|
||||
Install FFmpeg via your system package manager:
|
||||
|
||||
| OS / distro | Command |
|
||||
| --------------- | ----------------------------------- |
|
||||
| Ubuntu / Debian | `sudo apt-get install -y ffmpeg` |
|
||||
| Fedora / RHEL | `sudo dnf install -y ffmpeg` |
|
||||
| Arch | `sudo pacman -S ffmpeg` |
|
||||
| macOS | `brew install ffmpeg` |
|
||||
| Windows | `winget install Gyan.FFmpeg` |
|
||||
|
||||
Verify: `ffmpeg -version`.
|
||||
|
||||
## `Node version X is not supported`
|
||||
|
||||
HyperFrames requires Node.js >= 22. Check with `node --version`.
|
||||
|
||||
- **nvm:** `nvm install 22 && nvm use 22`
|
||||
- **Homebrew (macOS):** `brew install node@22 && brew link --overwrite node@22`
|
||||
- **apt:** follow [nodesource](https://github.com/nodesource/distributions) for Node 22 LTS.
|
||||
|
||||
## `ENOSPC: no space left on device` or OOM kills during render
|
||||
|
||||
Renders are memory- and disk-hungry. Minimums:
|
||||
|
||||
- **RAM:** 4 GB free (8 GB recommended for 60fps / `--quality high`)
|
||||
- **Disk:** 2 GB free scratch space — frames are written to `/tmp` during capture
|
||||
|
||||
Mitigations:
|
||||
- Lower quality: `--quality draft`.
|
||||
- Lower fps: `--fps 24`.
|
||||
- Lower worker count: `--workers 1`.
|
||||
- Set `TMPDIR` to a volume with more space: `export TMPDIR=/mnt/scratch`.
|
||||
|
||||
## Lint passes but the render is blank / black frames
|
||||
|
||||
Check the browser console in `preview` — usually:
|
||||
- A timeline was registered with the wrong key (`__timelines["typo"]` instead of `__timelines["root"]`).
|
||||
- The root composition was wrapped in `<template>` (only sub-compositions use `<template>`).
|
||||
- A script tag failed to load — check Network tab in preview.
|
||||
|
||||
Run `npx hyperframes lint --verbose` to see info-level findings.
|
||||
|
||||
## Contrast warnings from `hyperframes validate`
|
||||
|
||||
```
|
||||
⚠ WCAG AA contrast warnings (3):
|
||||
· .subtitle "secondary text" — 2.67:1 (need 4.5:1, t=5.3s)
|
||||
```
|
||||
|
||||
- **Dark backgrounds:** brighten the failing color until it clears 4.5:1 (normal text) or 3:1 (large text — 24px+ or 19px+ bold).
|
||||
- **Light backgrounds:** darken it.
|
||||
- Stay within the palette family — don't invent a new color, adjust the existing one.
|
||||
- Skip the check temporarily with `--no-contrast` if iterating rapidly, but clear it before delivery.
|
||||
|
||||
## `Font family 'X' not supported by compiler`
|
||||
|
||||
The compiler embeds a curated set of web-safe + open-source fonts. If a font isn't supported, either:
|
||||
- Swap to a supported alternative from the warning.
|
||||
- Register a custom font via `@font-face` pointing to a `.woff2` in the project directory (the compiler embeds referenced `@font-face` files).
|
||||
|
||||
## Video plays back muted or with no audio
|
||||
|
||||
Check:
|
||||
- The `<video>` element has `muted playsinline` (required — browser autoplay policy).
|
||||
- Audio is a **separate** `<audio>` element, not the video element.
|
||||
- Audio `data-volume` is set (defaults to 1).
|
||||
- The audio file is at the expected path — compositions load relative to their own directory.
|
||||
|
||||
## Docker render fails on Linux with rootless Docker
|
||||
|
||||
Add `--privileged` or pass `--cap-add=SYS_ADMIN`:
|
||||
|
||||
```bash
|
||||
npx hyperframes render --docker --docker-args "--cap-add=SYS_ADMIN"
|
||||
```
|
||||
|
||||
The headless browser needs namespace permissions for sandboxing.
|
||||
|
||||
## Bug reports
|
||||
|
||||
Include `npx hyperframes info` output + the full error log. File at [github.com/heygen-com/hyperframes](https://github.com/heygen-com/hyperframes/issues).
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# Website to Video
|
||||
|
||||
Capture a website, produce a professional video from it. Use when the user provides a URL and wants a video — social ad, product tour, 30-second promo, etc.
|
||||
|
||||
The workflow has 7 steps. Each produces an artifact that gates the next. **Do not skip steps** — each artifact prevents a downstream failure mode.
|
||||
|
||||
## Step 1: Capture & Understand
|
||||
|
||||
```bash
|
||||
npx hyperframes capture https://example.com --out captured/
|
||||
```
|
||||
|
||||
Produces:
|
||||
- `captured/snapshot.html` — self-contained page
|
||||
- `captured/screenshot.png` — above-the-fold visual
|
||||
- `captured/assets/` — logos, hero images, background video (if any)
|
||||
- `captured/palette.json` — extracted colors (sorted by pixel coverage)
|
||||
- `captured/text.md` — extracted headings, paragraphs, CTAs
|
||||
- `captured/fonts.json` — font families and stacks detected in computed styles
|
||||
|
||||
**Gate:** Print a site summary — name, top 3 colors, primary + display fonts, hero asset path, one-sentence vibe. Keep it in your context — don't re-capture.
|
||||
|
||||
## Step 2: Write DESIGN.md
|
||||
|
||||
Small brand reference at the project root. 6 sections, ~90 lines. This is the cheat sheet — not the creative plan.
|
||||
|
||||
```markdown
|
||||
# DESIGN
|
||||
|
||||
## Brand
|
||||
- Name: Example Co.
|
||||
- One-line mission: "…"
|
||||
|
||||
## Colors
|
||||
- Background: #0B0F14
|
||||
- Primary: #00E0A4 (accent, CTA)
|
||||
- Secondary: #7A8B9B (body text)
|
||||
- Text: #FFFFFF
|
||||
|
||||
## Typography
|
||||
- Display: "Inter Tight", 700, tight letter-spacing
|
||||
- Body: "Inter", 400
|
||||
|
||||
## Motion
|
||||
- Mood: precise, technical, confident
|
||||
- Eases: `power3.out` for entrances, `expo.in` for exits
|
||||
|
||||
## Assets
|
||||
- Logo: `captured/assets/logo.svg`
|
||||
- Hero image: `captured/assets/hero.png`
|
||||
|
||||
## What NOT to Do
|
||||
- No purple, no pastels, no serif body
|
||||
- No playful/bubbly eases (`elastic`, `bounce`)
|
||||
- No drop shadows on text
|
||||
```
|
||||
|
||||
**Gate:** `DESIGN.md` exists in the project directory.
|
||||
|
||||
## Step 3: Write SCRIPT.md
|
||||
|
||||
Narration script. Story backbone. **Scene durations come from the narration, not from guessing.**
|
||||
|
||||
```markdown
|
||||
# SCRIPT
|
||||
|
||||
## Scene 1 — Hook (0:00–0:04)
|
||||
"What if your dashboards wrote themselves?"
|
||||
|
||||
## Scene 2 — Problem (0:04–0:11)
|
||||
"Teams spend hours stitching together queries, charts, and callouts — every Monday."
|
||||
|
||||
## Scene 3 — Solution (0:11–0:22)
|
||||
"Example Co. watches your data streams and proposes the dashboard you'd have built — in seconds."
|
||||
|
||||
## Scene 4 — CTA (0:22–0:28)
|
||||
"Try it free at example.com."
|
||||
```
|
||||
|
||||
Run `npx hyperframes tts SCRIPT.md --voice af_nova --output narration.wav` to generate TTS audio. Note the exact duration — that's the video's duration.
|
||||
|
||||
**Gate:** `SCRIPT.md` + `narration.wav` exist and durations match the plan (±0.3s).
|
||||
|
||||
## Step 4: Storyboard
|
||||
|
||||
Text-only scene plan: for each scene, describe the hero frame — what's on screen at the scene's most-visible moment.
|
||||
|
||||
```markdown
|
||||
# STORYBOARD
|
||||
|
||||
## Scene 1 (0:00–0:04) — Hook
|
||||
Hero frame: giant "WHAT IF YOUR DASHBOARDS WROTE THEMSELVES?" in display font, centered, on near-black. Logo top-left at 40% opacity.
|
||||
Entrance: each word staggers in, 0.08s apart.
|
||||
Transition out: flash-through-white into Scene 2.
|
||||
```
|
||||
|
||||
One paragraph per scene. Do NOT skip this step — it's where you catch narrative gaps before writing HTML.
|
||||
|
||||
**Gate:** `STORYBOARD.md` exists. Each scene has: hero frame, entrance, transition.
|
||||
|
||||
## Step 5: Composition
|
||||
|
||||
Write `index.html` scene-by-scene:
|
||||
- Each scene is a `<div class="scene scene-N">` positioned absolutely, full-bleed.
|
||||
- Static HTML+CSS for the hero frame first (no GSAP).
|
||||
- Layer the narration `<audio>` at `data-start="0"` on a high track index.
|
||||
- Add a transitions component (`flash-through-white`, `liquid-wipe`, etc.) between each scene.
|
||||
- THEN add GSAP entrances (`gsap.from()`), no exits — transitions own the exit.
|
||||
- Register `window.__timelines["root"] = tl`.
|
||||
|
||||
Install transitions as needed:
|
||||
|
||||
```bash
|
||||
npx hyperframes add flash-through-white
|
||||
```
|
||||
|
||||
## Step 6: Render
|
||||
|
||||
```bash
|
||||
npx hyperframes lint --strict # must pass
|
||||
npx hyperframes validate # WCAG contrast audit
|
||||
npx hyperframes render --quality draft --output draft.mp4
|
||||
```
|
||||
|
||||
Watch the draft. Note issues in a `REVIEW.md` bullet list (scene, timestamp, issue). Fix, re-render.
|
||||
|
||||
When happy:
|
||||
|
||||
```bash
|
||||
npx hyperframes render --quality high --output final.mp4
|
||||
```
|
||||
|
||||
## Step 7: Deliver
|
||||
|
||||
- Report file path + duration + file size to the user.
|
||||
- If the user wants a vertical cut, re-render with a 9:16 composition (`data-width="1080" data-height="1920"`) — typically requires a separate `index-vertical.html` with tighter typography and re-stacked scene layout.
|
||||
|
||||
## Common Failure Modes
|
||||
|
||||
- **Skipped DESIGN.md** → colors drift scene-to-scene; output feels like "AI slides."
|
||||
- **Skipped STORYBOARD.md** → scenes overlap or hero frames collide with transitions.
|
||||
- **Exit animations** before transitions → empty frames when the transition fires.
|
||||
- **Narration longer than `data-duration`** → audio clips mid-sentence. Update the composition's `data-duration` to match the TTS output length + 0.5s buffer.
|
||||
Loading…
Add table
Add a link
Reference in a new issue