mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
docs: put the ten workflows in one place, and connect the two tracks
There are ten creation workflows. The Workflows nav group held eight of them plus `guides/export-and-share`, which is not a workflow at all — there is no such skill, it is a finishing task. The tenth, the Remotion port, sat under Developers > Composition, design & animation, a different tab entirely. So: export-and-share moved to Build the project where the other task pages live, the Remotion page moved into Workflows, and the chooser gained a tenth route. The chooser rendered `<video>` unconditionally, which would have drawn a black rectangle for a route with no clip — it now renders a neutral tile instead, and custom.css styles it to the same box. **The Prompt Guide and the workflow guides did not know about each other.** Level 1 is organised one page per workflow, and five of its six pages linked to no guide at all. A reader learning to prompt for motion graphics never discovered /guides/motion-graphics, and the traffic went nowhere in both directions. Each Level 1 page now names its workflow and says what that page adds. Four links pointed at redirects rather than the real page — /guides/claude-design, /guides/common-mistakes, /guides/website-to-video, /guides/media-overlays. Seven files now link to the destination directly. Checked after: 0 broken links, 0 links via redirect.
This commit is contained in:
@@ -166,7 +166,7 @@ animation.
|
|||||||
Never use a script to play, pause, or seek a media element, and never use one
|
Never use a script to play, pause, or seek a media element, and never use one
|
||||||
to show or hide a clip based on time. The framework already does that from the
|
to show or hide a clip based on time. The framework already does that from the
|
||||||
data attributes, and a script doing it too will fight the framework. See
|
data attributes, and a script doing it too will fight the framework. See
|
||||||
[Common Mistakes](/guides/common-mistakes) for what that looks like.
|
[Common Mistakes](/guides/troubleshooting) for what that looks like.
|
||||||
</Warning>
|
</Warning>
|
||||||
|
|
||||||
## Reuse one composition with different content
|
## Reuse one composition with different content
|
||||||
|
|||||||
@@ -94,4 +94,4 @@ that stutters in preview still renders perfectly — see
|
|||||||
- [Render to MP4](/guides/rendering)
|
- [Render to MP4](/guides/rendering)
|
||||||
- [Compositions](/concepts/compositions)
|
- [Compositions](/concepts/compositions)
|
||||||
- [@hyperframes/producer](/packages/producer)
|
- [@hyperframes/producer](/packages/producer)
|
||||||
- [Common mistakes that break determinism](/guides/common-mistakes)
|
- [Common mistakes that break determinism](/guides/troubleshooting)
|
||||||
|
|||||||
@@ -3,26 +3,20 @@ title: "Canary rollouts"
|
|||||||
description: "Ship a change to a percentage of installs instead of all-or-nothing."
|
description: "Ship a change to a percentage of installs instead of all-or-nothing."
|
||||||
---
|
---
|
||||||
|
|
||||||
Most flags in this repo are binary: a feature is off (and therefore never
|
Most flags in this repo are binary: a change is off, or on for everyone. A
|
||||||
exercised on real traffic) or on for everyone (and therefore a fleet-wide
|
canary is the rung in between — the same change, enabled for a stable slice of
|
||||||
bet). A canary is the rung in between — the same change, enabled for a stable
|
installs, ramped as the signal holds.
|
||||||
slice of installs, ramped as the signal holds.
|
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
**Canaries are part of telemetry, not a separate system.** A canary is a
|
A canary is a *measured* rollout: a slice is enrolled so it can be compared
|
||||||
*measured* rollout: a slice is enrolled specifically so it can be compared
|
against everyone else. So **opting out of telemetry opts you out of
|
||||||
against everyone else, and the comparison is what makes ramping safe. An
|
canaries** — an opted-out install is never bucketed at all. See the
|
||||||
install that reports nothing cannot be compared, so **opting out of telemetry
|
[`telemetry` command](/packages/cli#telemetry) for the opt-out routes.
|
||||||
opts you out of canaries** — via `hyperframes telemetry disable`,
|
|
||||||
`HYPERFRAMES_NO_TELEMETRY=1`, `DO_NOT_TRACK=1`, a dev build, or Studio's
|
|
||||||
`hyperframes-studio:telemetryDisabled`. See the
|
|
||||||
[`telemetry` command](/packages/cli#telemetry) for what is collected and how
|
|
||||||
to turn it off; everything on this page sits behind that switch.
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
## Add one
|
## Add one
|
||||||
|
|
||||||
**1. Register it at 0%.** In `packages/core/src/canaryRegistry.ts`:
|
**1. Register it at 0%** in `packages/core/src/canaryRegistry.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
{
|
{
|
||||||
@@ -48,232 +42,40 @@ if (isCanaryEnabled("my-feature")) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
That is the whole API. The percentage lives in the registry, never at the
|
That is the whole API. The percentage lives in the registry, never at the call
|
||||||
call site.
|
site.
|
||||||
|
|
||||||
**3. Ramp it** by editing `percentage` in a patch release: `0 → 5 → 25 → 100`.
|
**3. Ramp it** by editing `percentage` in a patch release: `0 → 5 → 25 → 100`.
|
||||||
|
Widening is inclusive — everyone already in the 10 stays in the 25, so a
|
||||||
|
before/after comparison survives the ramp. Do not rename a live canary: the
|
||||||
|
name is part of the bucket hash, so renaming reshuffles the cohort mid-rollout.
|
||||||
|
|
||||||
**4. Delete it** once it is at 100 and holding — both the registry entry and
|
**4. Delete it** at 100 and holding — the registry entry *and* the branch it
|
||||||
the branch it guarded. `sunsetAfter` exists to force this: the scheduled
|
guarded. The scheduled **Canary sunset** workflow enforces this: it runs weekly
|
||||||
**Canary sunset** workflow runs weekly and fails once the date passes, naming
|
and fails once `sunsetAfter` passes, naming the overdue rollout and its owner in
|
||||||
the overdue rollout and its owner in the run log. It does not notify anyone —
|
the run log. It notifies nobody, so watch it if you own a canary. Run it
|
||||||
watch the workflow if you own a canary.
|
yourself with `bun scripts/check-canary-sunset.ts`.
|
||||||
|
|
||||||
It is a scheduled job rather than a PR check on purpose. A current-date
|
## Override one
|
||||||
assertion in the unit suite would redden builds for authors who changed
|
|
||||||
nothing related, on a calendar date, with no fix available to them — so the
|
|
||||||
failure is routed to the person who can actually ramp or remove the canary.
|
|
||||||
Run it yourself with `bun scripts/check-canary-sunset.ts`.
|
|
||||||
|
|
||||||
## Overriding
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
HF_CANARY_MY_FEATURE=on # or off / true / false / 1 / 0 / yes / no
|
HF_CANARY_MY_FEATURE=on # or off / true / false / 1 / 0 / yes / no
|
||||||
```
|
```
|
||||||
|
|
||||||
Upper-snake-case the name. An override always wins over the percentage, in
|
Upper-snake-case the name. In Studio, use `?hf_canary_my_feature=on`. An
|
||||||
both directions — use it for support escalations, dogfooding, bisects, or a
|
override always wins over the percentage, in both directions — escalations,
|
||||||
panic-off.
|
dogfooding, bisects, a panic-off. It is also the way to exercise a canary with
|
||||||
|
telemetry off.
|
||||||
|
|
||||||
## Measuring
|
Everything else fails closed below 100%: no bucket seed, an unregistered name,
|
||||||
|
or a CI machine all resolve to *not enrolled*. At exactly 100% those exclusions
|
||||||
|
stop applying, so nothing takes the new path for the first time at deletion.
|
||||||
|
|
||||||
Every telemetry event carries the assignment as a flag-shaped property:
|
## Remove one from your machine
|
||||||
|
|
||||||
```
|
Cohorts bucket on a seed in `install-state.json`, which sits beside
|
||||||
$feature/canary-my-feature: "true" | "false"
|
`config.json` in `~/.hyperframes` — not on the telemetry id. So a full reset is
|
||||||
```
|
one command:
|
||||||
|
|
||||||
`$feature/<key>` is the de-facto flag-property convention in analytics
|
|
||||||
tooling, so whoever operates the telemetry backend can split any metric by
|
|
||||||
cohort with **nothing configured server-side** — while the decision itself
|
|
||||||
still happens locally and offline, which the render path requires.
|
|
||||||
(Assignments ride the same anonymous, opt-out telemetry pipeline as every
|
|
||||||
other event — and disabling telemetry disables the **enrolment**, not just the
|
|
||||||
reporting: an opted-out install is never bucketed at all. See the note at the
|
|
||||||
top of this page.)
|
|
||||||
|
|
||||||
Two details worth knowing:
|
|
||||||
|
|
||||||
- **Both arms are emitted.** A non-enrolled install reports `"false"`, not a
|
|
||||||
missing property. Absent means *this build predates the canary*, which is a
|
|
||||||
different fact from *this install is control* — collapsing them makes a ramp
|
|
||||||
unreadable.
|
|
||||||
- **Keys are namespaced with `canary-`.** The `$feature/` namespace is shared
|
|
||||||
with real product feature flags elsewhere in the analytics pipeline. The
|
|
||||||
infix guarantees a canary can never alias one and fight it for the same
|
|
||||||
property.
|
|
||||||
|
|
||||||
## Cumulative exposure — the number that actually bounds blast radius
|
|
||||||
|
|
||||||
The instantaneous share holds at the target forever: enrolment is a pure
|
|
||||||
function of `(feature, bucketSeed, percentage)` and fresh ids are uniformly
|
|
||||||
random, so ~10% of active installs and ~10% of renders are enrolled on any
|
|
||||||
given day. That part does not drift.
|
|
||||||
|
|
||||||
**The cumulative set does grow.** Install ids churn — measured on the desktop
|
|
||||||
render population, there are ~25× more distinct ids over 30 days than in any
|
|
||||||
single day. Ten percent of a pool that keeps turning over is a steadily larger
|
|
||||||
group of installs that have been enrolled *at some point*. If a single person
|
|
||||||
cycles through N ids during a rollout, their chance of having been exposed is
|
|
||||||
`1 − (1 − p)^N`, so at 10%: 19% after two ids, 41% after five.
|
|
||||||
|
|
||||||
So the number that bounds blast radius is *distinct installs ever enrolled
|
|
||||||
during the window*, not the instantaneous rate. Two practical consequences:
|
|
||||||
|
|
||||||
- **Keep canaries short.** Drift compounds with time; a 5-day window at 10% is
|
|
||||||
far tighter than a 60-day one.
|
|
||||||
- **The percentage is not the safety mechanism.** It bounds *initial* exposure
|
|
||||||
and decays from there. Per-render verification and per-install circuit
|
|
||||||
breakers are what actually bound harm. A breaker's tripped state lives in
|
|
||||||
`install-state.json`, separate from `config.json` and merged back in on every
|
|
||||||
read, so a `config.json` re-mint cannot re-enrol an install into a path that
|
|
||||||
already failed it. Deleting `~/.hyperframes` clears both, deliberately —
|
|
||||||
that is the user's reset.
|
|
||||||
|
|
||||||
For *measurement* — "is this feature better?" — churn is harmless: re-bucketing
|
|
||||||
is random, so it adds noise, not bias. It is specifically the blast-radius
|
|
||||||
guarantee that degrades.
|
|
||||||
|
|
||||||
## Calibration: validating the mechanism in the wild
|
|
||||||
|
|
||||||
Two inert canaries — `calibration-10` (10%) and `calibration-50` (50%) — gate
|
|
||||||
nothing and exist only to prove the mechanism behaves as designed on real
|
|
||||||
traffic. Their percentages stay FIXED for the whole window, which is what
|
|
||||||
makes check 3 below meaningful: while a percentage is constant, a cohort flip
|
|
||||||
is a bug, whereas during a real ramp a `false → true` flip is correct and
|
|
||||||
expected.
|
|
||||||
|
|
||||||
Four checks, written down before the data arrives so the read is not post-hoc.
|
|
||||||
(The maintainers monitor these on an internal dashboard; the definitions live
|
|
||||||
here so the experiment's terms are public and fixed.)
|
|
||||||
|
|
||||||
**1. Accuracy — does 10% mean 10%?** Measured install-weighted AND
|
|
||||||
event-weighted separately: render volume is heavily skewed toward a few heavy
|
|
||||||
installs, so the two can differ even when bucketing is perfect. Install share
|
|
||||||
is the one that must land on target.
|
|
||||||
|
|
||||||
**2. Drift — does cumulative exposure climb?** Measured over widening windows
|
|
||||||
(1/7/14/30 days). The instantaneous share should stay flat at 10%; the
|
|
||||||
cumulative enrolled-install count should grow with id churn.
|
|
||||||
|
|
||||||
**3. Stability — does any install ever change cohort?** MUST be zero. A single
|
|
||||||
id reporting both `true` and `false` at a fixed percentage means something is
|
|
||||||
broken: memoization, a registry edit mid-window, or two code paths
|
|
||||||
disagreeing. One innocent explanation to rule out first on a small-nonzero
|
|
||||||
read: events carry the assignment but not the decision *reason*, so a dev
|
|
||||||
toggling `HF_CANARY_CALIBRATION_10=on/off` mid-window is indistinguishable
|
|
||||||
from a real flip. Emitting a reason property is deliberately skipped — add it
|
|
||||||
only if this check comes back dirty.
|
|
||||||
|
|
||||||
**4. Cross-surface agreement — do CLI and Studio agree for the same install?**
|
|
||||||
A CLI-launched Studio adopts the CLI's id, so the same install must report the
|
|
||||||
same cohort on both. Disagreement means the two bindings have diverged. This
|
|
||||||
compares the *value reported per surface* — any disagreement here is also a
|
|
||||||
check-3 flip, so this check's job is attribution: it isolates the flips that
|
|
||||||
are binding divergence rather than within-surface instability.
|
|
||||||
|
|
||||||
**Independence bonus:** overlap between `calibration-10` and `calibration-50`
|
|
||||||
should be ~5% of installs (p1 x p2), not ~10% (which would mean the slices are
|
|
||||||
correlated and every canary hits the same unlucky cohort).
|
|
||||||
|
|
||||||
### What passing looks like, and what cannot be fixed
|
|
||||||
|
|
||||||
Checks 1, 3 and 4 should pass outright — they are properties of the design,
|
|
||||||
and failing any of them is a bug to fix before shipping a real rollout.
|
|
||||||
|
|
||||||
Check 2 will NOT come back flat, and that is expected rather than a defect.
|
|
||||||
Per-install cohorts never flip, but a *person* who wipes their config gets a
|
|
||||||
new id and a fresh roll of the dice. Preventing that entirely needs a stable
|
|
||||||
identity across resets, and both candidates were rejected: hardware
|
|
||||||
fingerprinting correlates the cohort with hardware (fatal for a rendering
|
|
||||||
experiment, and it survives uninstall), and account identity covers only
|
|
||||||
~3.6% of local rendering installs. So the drift itself stands as a measured,
|
|
||||||
accepted limit — but its worst consequence is mitigated, and its size is now
|
|
||||||
directly measurable rather than inferred:
|
|
||||||
|
|
||||||
- **Cohorts survive a config re-mint.** Canaries bucket on a dedicated
|
|
||||||
`bucketSeed` — not the telemetry id — held in `install-state.json` beside
|
|
||||||
`config.json` and write-once (the first install's seed wins forever). This
|
|
||||||
matters because `config.json` is rewritten on every command and every
|
|
||||||
render, and any parse/permission/IO failure makes the CLI mint a fresh
|
|
||||||
identity. The seed is never emitted, so it does not link the old id to the
|
|
||||||
new one.
|
|
||||||
- **Deleting `~/.hyperframes` clears the cohort too, deliberately.** The seed
|
|
||||||
does not live outside the config directory, so the user's reset is a real
|
|
||||||
reset. A seed that outlived it would be a persistent identifier defeating
|
|
||||||
the only lever they have — see [the removal path](#removing-your-canary-state).
|
|
||||||
- **A re-minted install cannot re-enter a path that already failed on that
|
|
||||||
machine.** The circuit breaker's tripped state is mirrored to the same
|
|
||||||
state file.
|
|
||||||
- **`install_predecessor_found`** on every event says whether this install's
|
|
||||||
mint found a previous install's state marker — i.e. `config.json` was lost
|
|
||||||
while `install-state.json` survived. Its true-share is the re-mint rate
|
|
||||||
directly; drift from a deliberate reset, a fresh machine, a container, or a
|
|
||||||
new user is not linkable by any local mechanism and is not counted.
|
|
||||||
|
|
||||||
With the seed carryover, check 2's residual drift comes from the
|
|
||||||
unrecoverable buckets only — deliberate resets, fresh machines, containers,
|
|
||||||
and genuinely new users — and canary window lengths get picked from that
|
|
||||||
residual, not the raw turnover.
|
|
||||||
|
|
||||||
## Behaviour worth knowing
|
|
||||||
|
|
||||||
**Ramping is inclusive.** Widening `10 → 25` keeps everyone who was already
|
|
||||||
in the 10. Cohorts never reshuffle, so a before/after comparison stays valid
|
|
||||||
across a ramp.
|
|
||||||
|
|
||||||
**Slices are independent per feature.** The bucket hashes `feature:seed`,
|
|
||||||
not the seed alone — two canaries at 10% select two different 10%s. If
|
|
||||||
they shared a slice, one unlucky cohort would receive every experiment at
|
|
||||||
once and no two rollouts could be read apart.
|
|
||||||
|
|
||||||
**Cohorts are keyed to the install directory, not to `config.json`.** The
|
|
||||||
bucketing unit is a dedicated seed in `install-state.json`, inherited across
|
|
||||||
`config.json` re-mints (see the calibration section) — distinct from the
|
|
||||||
telemetry id and never emitted. It does not outlive `~/.hyperframes`.
|
|
||||||
|
|
||||||
**A CLI-launched Studio adopts the CLI's decisions rather than re-deriving
|
|
||||||
them.** The CLI publishes `window.__HF_CLI_CANARY_DECISIONS` — a
|
|
||||||
`{ name: { enabled, forced } }` map. Re-deriving cannot agree in the cases
|
|
||||||
that matter: telemetry off (the CLI resolves `telemetry_opt_out`, but Studio's
|
|
||||||
opt-out is a separate localStorage flag it cannot see), an `HF_CANARY_*`
|
|
||||||
override (env vars never cross into the browser), or no seed injected (Studio
|
|
||||||
falls back to a different unit, so a different bucket). One render spanning
|
|
||||||
both surfaces must not run half-enrolled.
|
|
||||||
|
|
||||||
`forced` carries the provenance, and the precedence follows from it — highest
|
|
||||||
first:
|
|
||||||
|
|
||||||
1. A **forced** CLI decision (`HF_CANARY_*`). Wins outright, including over
|
|
||||||
this profile's opt-out, exactly as a local URL override does.
|
|
||||||
2. A local `?hf_canary_*=` override, same reasoning.
|
|
||||||
3. **This profile's telemetry opt-out.** Checked before any percentage
|
|
||||||
decision: the two surfaces have independent opt-outs, and CLI telemetry
|
|
||||||
being on says nothing about whether this browser profile agreed to be
|
|
||||||
measured. A cohort roll must never enrol an opted-out profile.
|
|
||||||
4. The CLI's percentage decision.
|
|
||||||
5. Local evaluation (standalone Studio, or a canary the CLI did not publish).
|
|
||||||
|
|
||||||
Identity is treated differently from decisions. `__HF_CLI_DISTINCT_ID` and
|
|
||||||
`__HF_CLI_BUCKET_SEED` are injected only for a `Host` this server can vouch
|
|
||||||
for — a page that rebinds its hostname to `127.0.0.1` would otherwise read
|
|
||||||
them as same-origin. That means loopback by default, and on an explicit LAN
|
|
||||||
bind (`HYPERFRAMES_PREVIEW_HOST=0.0.0.0`) a `Host` naming an address or
|
|
||||||
hostname this machine actually answers on. Setting the variable does not
|
|
||||||
disable the check; a hostile `Host` is refused in either mode.
|
|
||||||
|
|
||||||
The decisions map is not identifying, so it is published regardless, which
|
|
||||||
keeps a LAN preview in agreement with the CLI instead of silently
|
|
||||||
re-deriving.
|
|
||||||
|
|
||||||
The decisions map is published even when telemetry is off — that is the case
|
|
||||||
it exists for. It is safe to expose where the seed is not: booleans about
|
|
||||||
features, not the value cohorts are derived from. Studio still evaluates
|
|
||||||
locally when opened standalone, or for any canary the CLI did not publish.
|
|
||||||
|
|
||||||
## Removing your canary state
|
|
||||||
|
|
||||||
Both `config.json` and `install-state.json` live in `~/.hyperframes`, so:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rm -rf ~/.hyperframes # clears telemetry id, canary cohorts, and breaker state
|
rm -rf ~/.hyperframes # clears telemetry id, canary cohorts, and breaker state
|
||||||
@@ -282,48 +84,24 @@ rm -rf ~/.hyperframes # clears telemetry id, canary cohorts, and breaker stat
|
|||||||
`hyperframes telemetry status` prints both paths if you want to inspect or
|
`hyperframes telemetry status` prints both paths if you want to inspect or
|
||||||
delete them individually. Nothing canary-related is stored anywhere else.
|
delete them individually. Nothing canary-related is stored anywhere else.
|
||||||
|
|
||||||
**Opting out of telemetry opts you out of canaries.** A canary is a *measured*
|
|
||||||
rollout — we enrol a slice precisely so it can be compared against everyone
|
|
||||||
else. An install that sends nothing can't be compared, so enrolling it buys no
|
|
||||||
signal and only changes that user's code path, on an experimental feature,
|
|
||||||
without their knowledge. Every opt-out route counts: the persisted preference
|
|
||||||
(`hyperframes telemetry disable`), the runtime env vars
|
|
||||||
(`HYPERFRAMES_NO_TELEMETRY`, `DO_NOT_TRACK`), dev/telemetry-disabled builds,
|
|
||||||
and Studio's `hyperframes-studio:telemetryDisabled`. The decision resolves to
|
|
||||||
`telemetry_opt_out` *before* bucketing, so no cohort is assigned at all.
|
|
||||||
|
|
||||||
An explicit `HF_CANARY_<FEATURE>` (or `?hf_canary_<feature>=` in Studio)
|
|
||||||
override still wins — that is a deliberate local choice, and it stays the way
|
|
||||||
to exercise a canary with telemetry off.
|
|
||||||
|
|
||||||
**It fails closed below 100%.** No install id, an unregistered name, or a CI
|
|
||||||
machine all resolve to *not enrolled*. A canary exists to bound blast radius,
|
|
||||||
so "we don't know who this is" must never mean "enrol everyone". CI is
|
|
||||||
excluded because its config is regenerated per run, so its ids are ephemeral
|
|
||||||
and would hop cohorts between runs — an explicit override still reaches it,
|
|
||||||
which is how you test a canary in CI.
|
|
||||||
|
|
||||||
At exactly 100% those exclusions stop applying, deliberately. 100% is the step
|
|
||||||
before deleting the entry and the guard, so anything still resolving false
|
|
||||||
there would take the new path for the FIRST time at deletion, unstaged — and
|
|
||||||
CI and seedless installs are precisely the populations a dashboard cannot see,
|
|
||||||
so "100% and holding" would look green while they were never exercised.
|
|
||||||
|
|
||||||
**Decisions are stable and memoized.** The same install always resolves the
|
|
||||||
same way, and the answer is fixed for the life of a process — a render that
|
|
||||||
starts enrolled finishes enrolled, and its telemetry agrees with what ran.
|
|
||||||
|
|
||||||
**Do not rename a live canary.** The name is part of the hash, so renaming
|
|
||||||
reshuffles the cohort mid-rollout and invalidates the comparison.
|
|
||||||
|
|
||||||
## Where it lives
|
## Where it lives
|
||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `packages/core/src/canary.ts` | Pure evaluator — no fs, no network, browser-safe |
|
|
||||||
| `packages/core/src/canaryRegistry.ts` | Every rollout, with owner and sunset date |
|
| `packages/core/src/canaryRegistry.ts` | Every rollout, with owner and sunset date |
|
||||||
| `packages/cli/src/telemetry/canary.ts` | CLI binding: install id, env override, CI detection |
|
| `packages/core/src/canary.ts` | Pure evaluator — no fs, no network, browser-safe |
|
||||||
|
| `packages/cli/src/telemetry/canary.ts` | CLI binding: bucket seed, env override, CI detection |
|
||||||
|
| `packages/studio/src/telemetry/canary.ts` | Studio binding: adopts the CLI's published decisions |
|
||||||
|
|
||||||
The evaluator is deliberately dependency-free so studio and the embeddable
|
The evaluator is dependency-free so Studio and the embeddable player can use it
|
||||||
player can use it too; those surfaces need their own thin binding to supply an
|
too; each surface supplies its own unit id through a thin binding. Studio
|
||||||
id, since only the CLI has `anonymousId`.
|
adopts the CLI's decisions rather than re-deriving them, so one render spanning
|
||||||
|
both surfaces never runs half-enrolled.
|
||||||
|
|
||||||
|
## Related topics
|
||||||
|
|
||||||
|
- [Testing local changes](/contributing/testing-local-changes) — exercise your
|
||||||
|
canary before anyone else gets it.
|
||||||
|
- [Release channels](/contributing/release-channels) — which branch a ramp belongs on.
|
||||||
|
- [`telemetry` command](/packages/cli#telemetry) — what is collected, and how a
|
||||||
|
user turns it off.
|
||||||
|
|||||||
@@ -182,6 +182,16 @@ h3 {
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
/* A route with no preview clip: same box, no black rectangle. */
|
||||||
|
.hf-workflow-route-noclip {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: color-mix(in srgb, currentColor 8%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.hf-workflow-route-title {
|
.hf-workflow-route-title {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
+3
-3
@@ -173,7 +173,7 @@
|
|||||||
"guides/music-to-video",
|
"guides/music-to-video",
|
||||||
"guides/slideshow",
|
"guides/slideshow",
|
||||||
"guides/general-video",
|
"guides/general-video",
|
||||||
"guides/export-and-share"
|
"guides/hyperframes-vs-remotion"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -186,7 +186,8 @@
|
|||||||
"guides/voice-and-audio",
|
"guides/voice-and-audio",
|
||||||
"guides/remove-background",
|
"guides/remove-background",
|
||||||
"guides/color-grading",
|
"guides/color-grading",
|
||||||
"guides/media-effects"
|
"guides/media-effects",
|
||||||
|
"guides/export-and-share"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -530,7 +531,6 @@
|
|||||||
"concepts/frame-adapters",
|
"concepts/frame-adapters",
|
||||||
"concepts/determinism",
|
"concepts/determinism",
|
||||||
"guides/html-in-canvas",
|
"guides/html-in-canvas",
|
||||||
"guides/hyperframes-vs-remotion",
|
|
||||||
"guides/figma"
|
"guides/figma"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ These settle layout, palette, type, and scene direction first.
|
|||||||
3. Save it to disk, then open that folder with a coding agent.
|
3. Save it to disk, then open that folder with a coding agent.
|
||||||
4. Let the agent check timing, motion, media, captions, and rendering.
|
4. Let the agent check timing, motion, media, captions, and rendering.
|
||||||
|
|
||||||
[Claude Design instruction](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) · [Open Design handoff](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/open-design-hyperframes.md)
|
[Claude Design instruction](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/design-tools-hyperframes.md) · [Open Design handoff](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/open-design-hyperframes.md)
|
||||||
|
|
||||||
## Is the handoff any good?
|
## Is the handoff any good?
|
||||||
|
|
||||||
|
|||||||
@@ -99,4 +99,6 @@ What you can already steer from the prompt, before you've learned any technique.
|
|||||||
For the beat-timestamped skeleton these prompts share, see [Prompt anatomy](/prompting/anatomy); for adjectives that map to motion and emphasis settings, [Vocabulary](/prompting/vocabulary). To build a video from scratch instead of dressing existing footage, start at the router in `/hyperframes`.
|
For the beat-timestamped skeleton these prompts share, see [Prompt anatomy](/prompting/anatomy); for adjectives that map to motion and emphasis settings, [Vocabulary](/prompting/vocabulary). To build a video from scratch instead of dressing existing footage, start at the router in `/hyperframes`.
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
The workflow this level rides is documented at [Captions and talking head](/guides/captions-and-recuts) — what it takes as input, what it asks you before it builds, and what it returns.
|
||||||
|
|
||||||
*Next: [Music videos and slideshows](/prompting/music-and-slideshows) — swap footage for a soundtrack, or slides, and let the beat or the deck set the pace.*
|
*Next: [Music videos and slideshows](/prompting/music-and-slideshows) — swap footage for a soundtrack, or slides, and let the beat or the deck set the pace.*
|
||||||
|
|||||||
@@ -93,4 +93,6 @@ What you can already steer from the prompt, before you've learned any technique.
|
|||||||
- ❌ `a 40-second narrated walkthrough`
|
- ❌ `a 40-second narrated walkthrough`
|
||||||
- ✅ `a ~40-second narrated walkthrough`
|
- ✅ `a ~40-second narrated walkthrough`
|
||||||
|
|
||||||
|
The workflow this level rides is documented at [PR to video](/guides/pr-to-video) — what it takes as input, what it asks you before it builds, and what it returns.
|
||||||
|
|
||||||
*Next: [Captions and talking-head footage](/prompting/captions-and-talking-heads) — same one-prompt move, now dressing footage you already shot.*
|
*Next: [Captions and talking-head footage](/prompting/captions-and-talking-heads) — same one-prompt move, now dressing footage you already shot.*
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ If you composite a subject into the shot, do it **before** grading. A CRT warps
|
|||||||
/>
|
/>
|
||||||
*The footage degrades; the HUD does not. It is an overlay, composed above the media canvas.*
|
*The footage degrades; the HUD does not. It is an overlay, composed above the media canvas.*
|
||||||
|
|
||||||
The HUD in that shot is not part of the grade. It is a [`camcorder-hud`](/guides/media-overlays) registry block — real HTML, CSS, and a paused GSAP timeline composed *above* the media canvas. Grading never touches it, which is exactly what you want: burn a timestamp into the source pixels and it degrades with them; put it in an overlay and it stays crisp, editable, and timeline-driven while the footage falls apart underneath.
|
The HUD in that shot is not part of the grade. It is a [`camcorder-hud`](/catalog) registry block — real HTML, CSS, and a paused GSAP timeline composed *above* the media canvas. Grading never touches it, which is exactly what you want: burn a timestamp into the source pixels and it degrades with them; put it in an overlay and it stays crisp, editable, and timeline-driven while the footage falls apart underneath.
|
||||||
|
|
||||||
## Animating a grade
|
## Animating a grade
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ Restyling scene by scene afterward pulls threads out of a design that was balanc
|
|||||||
<Card title="The specification dial" icon="gauge" href="/prompting/specification-dial">
|
<Card title="The specification dial" icon="gauge" href="/prompting/specification-dial">
|
||||||
How pinning exact hexes and type direction removes drift.
|
How pinning exact hexes and type direction removes drift.
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Claude Design" icon="message" href="/guides/claude-design">
|
<Card title="Claude Design" icon="message" href="/guides/design-tools">
|
||||||
Attach a brand guide or screenshot to seed a first draft from your look.
|
Attach a brand guide or screenshot to seed a first draft from your look.
|
||||||
</Card>
|
</Card>
|
||||||
</CardGroup>
|
</CardGroup>
|
||||||
|
|||||||
@@ -137,4 +137,6 @@ the preset picks the palette. If you have colors, name them.
|
|||||||
- ❌ `make it look on-brand`
|
- ❌ `make it look on-brand`
|
||||||
- ✅ `brand color #FF5533 on off-black`
|
- ✅ `brand color #FF5533 on off-black`
|
||||||
|
|
||||||
|
The workflow this level rides is documented at [Faceless explainer](/guides/faceless-explainer) — what it takes as input, what it asks you before it builds, and what it returns.
|
||||||
|
|
||||||
*Next: [Code changes and PRs](/prompting/code-and-prs) — point a workflow at a merged GitHub PR instead of a blank page.*
|
*Next: [Code changes and PRs](/prompting/code-and-prs) — point a workflow at a merged GitHub PR instead of a blank page.*
|
||||||
|
|||||||
@@ -86,4 +86,6 @@ Each reuses a registry block, so the agent composes rather than hand-building fr
|
|||||||
For the six-part skeleton and the per-beat content formula these prompts share, see [Prompt anatomy](/prompting/anatomy); for the full set of run-verified examples, [Verified examples](/prompting/examples). Unsure whether your ask is a motion graphic at all? Start at the router in `/hyperframes`.
|
For the six-part skeleton and the per-beat content formula these prompts share, see [Prompt anatomy](/prompting/anatomy); for the full set of run-verified examples, [Verified examples](/prompting/examples). Unsure whether your ask is a motion graphic at all? Start at the router in `/hyperframes`.
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
The workflow this level rides is documented at [Motion graphics](/guides/motion-graphics) — what it takes as input, what it asks you before it builds, and what it returns.
|
||||||
|
|
||||||
*Next: [Prompt anatomy](/prompting/anatomy) — you've been riding workflows through six one-prompt inputs; Level 2 opens the prompt itself.*
|
*Next: [Prompt anatomy](/prompting/anatomy) — you've been riding workflows through six one-prompt inputs; Level 2 opens the prompt itself.*
|
||||||
|
|||||||
@@ -101,4 +101,6 @@ This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-
|
|||||||
/>
|
/>
|
||||||
*That clause, rendered — the region cut from the finished film.*
|
*That clause, rendered — the region cut from the finished film.*
|
||||||
|
|
||||||
|
The workflow this level rides is documented at [Music to video](/guides/music-to-video) — what it takes as input, what it asks you before it builds, and what it returns.
|
||||||
|
|
||||||
*Next: [Motion graphics](/prompting/motion-graphics) — the shortest one yet, a single motion graphic where motion alone is the message.*
|
*Next: [Motion graphics](/prompting/motion-graphics) — the shortest one yet, a single motion graphic where motion alone is the message.*
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ The installer shows a picker. Select the **core skills** below — every project
|
|||||||
|
|
||||||
## Claude Design
|
## Claude Design
|
||||||
|
|
||||||
Claude Design uses a different setup. Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) from GitHub (click the ↓ button), then **attach it to your chat** (don't paste the URL — file attachments produce better output):
|
Claude Design uses a different setup. Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/design-tools-hyperframes.md) from GitHub (click the ↓ button), then **attach it to your chat** (don't paste the URL — file attachments produce better output):
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Use the attached skill. 25-second LinkedIn video for my startup.
|
Use the attached skill. 25-second LinkedIn video for my startup.
|
||||||
@@ -90,7 +90,7 @@ Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
|
|||||||
CTA: autocrmhq.com
|
CTA: autocrmhq.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Claude Design produces a valid first draft (brand identity, scene content, animations, transitions). Download the ZIP and refine in any AI coding agent with `npx hyperframes preview` running. See the [Claude Design guide](/guides/claude-design) for the full workflow.
|
Claude Design produces a valid first draft (brand identity, scene content, animations, transitions). Download the ZIP and refine in any AI coding agent with `npx hyperframes preview` running. See the [Claude Design guide](/guides/design-tools) for the full workflow.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ A launch or promo that *sells*: SaaS promos, feature reveals, product demos, app
|
|||||||
| You want… | Route |
|
| You want… | Route |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| To market, launch, promote, or reveal a product (the default for any commercial URL) | `/product-launch-video` |
|
| To market, launch, promote, or reveal a product (the default for any commercial URL) | `/product-launch-video` |
|
||||||
| A video *of* a general site — a portfolio / blog / docs / landing-page tour or showcase, not a sales pitch | `/product-launch-video` too — say *tour* or *showcase* in the brief (see the [website-to-video guide](/guides/website-to-video)) |
|
| A video *of* a general site — a portfolio / blog / docs / landing-page tour or showcase, not a sales pitch | `/product-launch-video` too — say *tour* or *showcase* in the brief (see the [website-to-video guide](/guides/product-launch-video)) |
|
||||||
|
|
||||||
Both route to the same workflow now; what changes is the brief. "Promo for our site" is a launch — sell it. A neutral walkthrough of a docs site is a tour — say so, and the workflow shows the site's own captured screens instead of pitching. Unsure → start at `/hyperframes` and let it route.
|
Both route to the same workflow now; what changes is the brief. "Promo for our site" is a launch — sell it. A neutral walkthrough of a docs site is a tour — say so, and the workflow shows the site's own captured screens instead of pitching. Unsure → start at `/hyperframes` and let it route.
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ export const WorkflowChooser = () => {
|
|||||||
video: `${CDN}/wfv2-general.mp4`,
|
video: `${CDN}/wfv2-general.mp4`,
|
||||||
poster: `${CDN}/wfv2-general.jpg`,
|
poster: `${CDN}/wfv2-general.jpg`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Port a Remotion composition",
|
||||||
|
bring: "Bring an existing Remotion project. One-way migration, not a new build.",
|
||||||
|
href: "/guides/hyperframes-vs-remotion",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Lazy initializer, not a post-mount effect: with useState(false) the first
|
// Lazy initializer, not a post-mount effect: with useState(false) the first
|
||||||
@@ -110,16 +115,23 @@ export const WorkflowChooser = () => {
|
|||||||
aria-label={`${route.title}. ${route.bring}`}
|
aria-label={`${route.title}. ${route.bring}`}
|
||||||
className="hf-workflow-route"
|
className="hf-workflow-route"
|
||||||
>
|
>
|
||||||
<video
|
{/* A route without a preview renders no <video> at all. An empty
|
||||||
src={reducedMotion ? undefined : route.video}
|
video element draws a black rectangle, which reads as a broken
|
||||||
poster={route.poster}
|
tile rather than a route with no clip yet. */}
|
||||||
autoPlay={!reducedMotion}
|
{route.video ? (
|
||||||
muted
|
<video
|
||||||
loop={!reducedMotion}
|
src={reducedMotion ? undefined : route.video}
|
||||||
playsInline
|
poster={route.poster}
|
||||||
preload="metadata"
|
autoPlay={!reducedMotion}
|
||||||
aria-hidden="true"
|
muted
|
||||||
/>
|
loop={!reducedMotion}
|
||||||
|
playsInline
|
||||||
|
preload="metadata"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="hf-workflow-route-noclip" aria-hidden="true" />
|
||||||
|
)}
|
||||||
<span>
|
<span>
|
||||||
<span className="hf-workflow-route-title">{route.title}</span>
|
<span className="hf-workflow-route-title">{route.title}</span>
|
||||||
<span className="hf-workflow-route-copy">{route.bring}</span>
|
<span className="hf-workflow-route-copy">{route.bring}</span>
|
||||||
|
|||||||
+230
-32
@@ -4,48 +4,246 @@ sidebarTitle: 30 Days of HyperFrames
|
|||||||
description: "The official 30 Days of HyperFrames lessons, collected in one place as the series is published."
|
description: "The official 30 Days of HyperFrames lessons, collected in one place as the series is published."
|
||||||
---
|
---
|
||||||
|
|
||||||
The series began on [@HeyGen](https://x.com/HeyGen) and continues on [@HyperFrames_](https://x.com/HyperFrames_). Open any day for the original lesson and example.
|
import { DocsVideo } from "/snippets/docs-video.jsx";
|
||||||
|
|
||||||
|
One lesson a day, each one built and rendered before it was posted. Every day below links to its original post for the prompt and the discussion; where the film or a frame from it is published, you can see the result here first.
|
||||||
|
|
||||||
|
<Frame caption="The launch film the series was built around — one person, one prompt, one finished launch video.">
|
||||||
|
<DocsVideo
|
||||||
|
title="HyperFrames launch film"
|
||||||
|
src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/hyperframes-launch-preview.mp4"
|
||||||
|
poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/hyperframes-launch-poster.jpg"
|
||||||
|
/>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
The series began on [@HeyGen](https://x.com/HeyGen) and continues on [@HyperFrames_](https://x.com/HyperFrames_).
|
||||||
|
|
||||||
|
## Three lessons, in full
|
||||||
|
|
||||||
|
Three days that between them cover the range: a code change, an audio track, and one project rendered many ways. No account needed — they play here.
|
||||||
|
|
||||||
|
<Frame caption="Day 3 — a pull request becomes a 32-second review anyone on the team can watch.">
|
||||||
|
<DocsVideo
|
||||||
|
title="Day 3 — PR-to-Video"
|
||||||
|
src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/full-prvideo.mp4"
|
||||||
|
poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-prvideo.jpg"
|
||||||
|
/>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
<Frame caption="Day 4 — a track analysed for beats and sections, then every cut snapped to that grid.">
|
||||||
|
<DocsVideo
|
||||||
|
title="Day 4 — Music-to-Video"
|
||||||
|
src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/full-music-v2.mp4"
|
||||||
|
poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-music.jpg"
|
||||||
|
/>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
<Frame caption="Day 23 — one composition, two variable sets, two finished vertical cuts.">
|
||||||
|
<DocsVideo
|
||||||
|
title="Day 23 — Templates and variables"
|
||||||
|
src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/full-variables.mp4"
|
||||||
|
poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-variables.jpg"
|
||||||
|
/>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
## Start and create
|
## Start and create
|
||||||
|
|
||||||
1. [**Install HyperFrames**](https://x.com/HeyGen/status/2074176916819685648) — give your coding agent the HyperFrames skills.
|
Days 1 to 9 cover the inputs: a repo, a track, a topic, a speaker, a design file. Every card opens the original post.
|
||||||
2. [**Give your brand a motion language**](https://x.com/HeyGen/status/2074574265714905167) — turn a visual identity into reusable motion direction.
|
|
||||||
3. [**PR-to-Video**](https://x.com/HeyGen/status/2074954182583554051) — explain an open pull request as a short video.
|
<CardGroup cols={2}>
|
||||||
4. [**Music-to-Video**](https://x.com/HeyGen/status/2075262117964615956) — build a video around a track and its beat.
|
<Card title="Day 1 — Install HyperFrames" href="https://x.com/HeyGen/status/2074176916819685648">
|
||||||
5. [**Faceless explainer**](https://x.com/HeyGen/status/2075682104302882908) — turn a topic into a designed explainer without footage.
|
<img alt="Nineteen skills in three groups: the /hyperframes router, ten creation workflows, and the supporting set" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/skills-install-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
6. [**Talking-head recut**](https://x.com/HeyGen/status/2075998619325604238) — add synchronized graphics to an existing speaker video.
|
|
||||||
7. [**Motion graphics**](https://x.com/HeyGen/status/2076381187640324427) — make a short, design-led animation.
|
Give your coding agent the HyperFrames skills. One router reads the request and picks the workflow.
|
||||||
8. [**Music and SFX with the HeyGen CLI**](https://x.com/HeyGen/status/2076719036742922504) — find and use audio from the command line.
|
|
||||||
9. [**Figma to HyperFrames**](https://x.com/HeyGen/status/2077072141309382770) — turn an existing design into a video composition.
|
</Card>
|
||||||
|
<Card title="Day 2 — Give your brand a motion language" href="https://x.com/HeyGen/status/2074574265714905167">
|
||||||
|
{/* VISUAL: needs the FRAME.md launch film published — a brand's type, colour and motion direction written to FRAME.md, then applied. Local source: /tmp/rend/out/frame-md-launch-storyboard.mp4 (60.3s, 1920x1080, audio). */}
|
||||||
|
|
||||||
|
Turn a visual identity into reusable motion direction, written down once as `FRAME.md` and read on every later build.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 3 — PR-to-Video" href="https://x.com/HeyGen/status/2074954182583554051">
|
||||||
|
<img alt="A pull request turned into a storyboard of scenes, then a finished video file" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-prvideo.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Explain an open pull request as a short video. Plays in full [above](#three-lessons-in-full).
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 4 — Music-to-Video" href="https://x.com/HeyGen/status/2075262117964615956">
|
||||||
|
<img alt="A waveform analysed into sections and beat markers, with every cut snapped to the grid" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-music.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Build a video around a track and its beat. Plays in full [above](#three-lessons-in-full).
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 5 — Faceless explainer" href="https://x.com/HeyGen/status/2075682104302882908">
|
||||||
|
<img alt="A heat-pump refrigerant cycle drawn as an animated diagram with a caption reading Outside, the refrigerant is colder than the air" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/faceless-explainer-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Turn a topic into a designed explainer without footage. The visuals are invented per scene.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 6 — Talking-head recut" href="https://x.com/HeyGen/status/2075998619325604238">
|
||||||
|
<img alt="A presenter clip marked UNMODIFIED with a designed lower third and a four-step pipeline panel layered over it" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/captions-recut-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Add synchronized graphics to an existing speaker video. The source clip plays untouched underneath.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 7 — Motion graphics" href="https://x.com/HeyGen/status/2076381187640324427">
|
||||||
|
<img alt="A bar chart building on screen, measuring the seconds per beat of the film it appears in" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/motion-graphics-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Make a short, design-led animation where the motion carries the message.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 8 — Music and SFX with the HeyGen CLI" href="https://x.com/HeyGen/status/2076719036742922504">
|
||||||
|
<img alt="Voiceover and music tracks on a mixer, with the music fader ducked to minus 16.5 dB under the speech" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/voice-audio-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Find and use audio from the command line, then let the mix duck itself under the narration.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 9 — Figma to HyperFrames" href="https://x.com/HeyGen/status/2077072141309382770">
|
||||||
|
<img alt="A Figma login form as a flat PNG export beside the same form imported as live HTML, both taking the same cursor" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/figma-to-html-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Turn an existing design into a video composition — as live HTML, not a flattened export.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
## Direct and refine
|
## Direct and refine
|
||||||
|
|
||||||
10. [**Anatomy of a prompt**](https://x.com/HeyGen/status/2077438104982667282) — structure a request so the agent gets the important intent right.
|
Days 10 to 19 are about steering: how you ask, what you inspect, and what you fix before rendering.
|
||||||
11. [**Repurpose an existing video**](https://x.com/HeyGen/status/2077798995762823593) — extract useful patterns from a reference.
|
|
||||||
12. [**Dynamic captions**](https://x.com/HeyGen/status/2078190361932103716) — make captions part of the visual design.
|
<CardGroup cols={2}>
|
||||||
13. [**Studio preview, part 1**](https://x.com/HeyGen/status/2078589328562016559) — inspect a composition before rendering.
|
<Card title="Day 10 — Anatomy of a prompt" href="https://x.com/HeyGen/status/2077438104982667282">
|
||||||
14. [**Studio preview, part 2: keyframes**](https://x.com/HeyGen/status/2078945075787452660) — adjust motion with visible keyframe controls.
|
{/* VISUAL: a still of a prompt broken into its parts — intent, input, format, constraints — with the resulting composition beside it. No published asset matches this lesson. */}
|
||||||
15. [**Sample the web**](https://x.com/HeyGen/status/2079251321484722675) — bring useful web motion into a composition.
|
|
||||||
16. [**Media use**](https://x.com/HeyGen/status/2079609229737111978) — work with the available asset libraries.
|
Structure a request so the agent gets the important intent right the first time.
|
||||||
17. [**Storyboard**](https://x.com/HeyGen/status/2080012076454719554) — agree on the sequence before building every scene.
|
|
||||||
18. [**HyperFrames in Video Agent**](https://x.com/HeyGen/status/2080407266755510452) — create with HyperFrames inside HeyGen Video Agent.
|
</Card>
|
||||||
19. [**Background removal**](https://x.com/HeyGen/status/2080762012330807438) — use alpha channels to layer subjects into a scene.
|
<Card title="Day 11 — Repurpose an existing video" href="https://x.com/HeyGen/status/2077798995762823593">
|
||||||
|
{/* VISUAL: a reference film beside the rebuilt composition, showing which patterns were extracted. No published asset matches this lesson. */}
|
||||||
|
|
||||||
|
Extract useful patterns from a reference instead of describing them from memory.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 12 — Dynamic captions" href="https://x.com/HeyGen/status/2078190361932103716">
|
||||||
|
<img alt="A presenter with a caption rail across the lower frame, the current word highlighted in the brand accent colour" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/avatar-presenter-caption-rail-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Make captions part of the visual design rather than a track laid on top.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 13 — Studio preview, part 1" href="https://x.com/HeyGen/status/2078589328562016559">
|
||||||
|
<img alt="The same composition open three ways — in Studio, through an agent, and from the terminal" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/studio-front-door-v2.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Inspect a composition before rendering — in Studio, through your agent, or from the terminal.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 14 — Studio preview, part 2: keyframes" href="https://x.com/HeyGen/status/2078945075787452660">
|
||||||
|
<img alt="A Studio timeline expanded into per-property keyframe lanes for opacity, position and scale" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/studio-animation-keyframe-loop-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Adjust motion with visible keyframe controls, one lane per animated property.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 15 — Sample the web" href="https://x.com/HeyGen/status/2079251321484722675">
|
||||||
|
<img alt="Four agent sessions each pointed at a different site, under the words Now any site becomes video" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/website-to-hyperframes-poster.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Bring useful web motion into a composition by capturing the real page.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 16 — Media use" href="https://x.com/HeyGen/status/2079609229737111978">
|
||||||
|
<img alt="A video tag with object-fit cover highlighted, next to the desert footage it crops into a 1:1 slot" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/media-basics-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Work with the available asset libraries, and control how each one crops and fits.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 17 — Storyboard" href="https://x.com/HeyGen/status/2080012076454719554">
|
||||||
|
{/* VISUAL: needs the STORYBOARD.md launch film published — a website captured, then FRAME.md and STORYBOARD.md written before any scene is built. Local source: /tmp/rend/out/frame-md-launch-storyboard.mp4 (60.3s, 1920x1080, audio). */}
|
||||||
|
|
||||||
|
Agree on the sequence before building every scene, so revisions land on the plan rather than the render.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 18 — HyperFrames in Video Agent" href="https://x.com/HeyGen/status/2080407266755510452">
|
||||||
|
{/* VISUAL: a Video Agent session creating a HyperFrames composition. The published mcp-flow-demo-v1 asset shows the MCP surface, not Video Agent, so it is deliberately not reused here. */}
|
||||||
|
|
||||||
|
Create with HyperFrames inside HeyGen Video Agent.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 19 — Background removal" href="https://x.com/HeyGen/status/2080762012330807438">
|
||||||
|
<img alt="A frame-for-frame comparison: the original presenter shot beside the same shot matted onto transparency" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/remove-background-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Use alpha channels to layer subjects into a scene, with the matte holding while the subject moves.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
## Extend and ship
|
## Extend and ship
|
||||||
|
|
||||||
20. [**Claude Design**](https://x.com/HyperFrames_/status/2081184676430160379) — take a design direction into a HyperFrames project.
|
Days 20 to 30 move from one composition to a repeatable pipeline: reusable blocks, variables, grading, and rendering somewhere other than your laptop.
|
||||||
21. [**Cloud rendering**](https://x.com/HyperFrames_/status/2081491370485952790) — render without making one local computer the bottleneck.
|
|
||||||
22. [**Components Catalog**](https://x.com/HyperFrames_/status/2081798071353278651) — add reusable scenes and effects instead of rebuilding them.
|
|
||||||
23. [**Templates and variables**](https://x.com/HyperFrames_/status/2082197435246600341) — turn one composition into repeatable versions.
|
|
||||||
24. [**Color grading**](https://x.com/HyperFrames_/status/2082536413829235004) — correct or stylize images and footage in code.
|
|
||||||
25. [**Prompt Guide**](https://x.com/HyperFrames_/status/2082892893027749917) — find practical language and examples for directing an agent.
|
|
||||||
26. [**Media effects and overlays**](https://x.com/HyperFrames_/status/2083324288632045910) — add designed treatments to real media.
|
|
||||||
27. [**Deploy**](https://x.com/HyperFrames_/status/2083633492894900456) — run rendering on your own cloud infrastructure.
|
|
||||||
28. [**Contribute to the Catalog**](https://x.com/HyperFrames_/status/2084070152975634743) — publish reusable building blocks for other creators.
|
|
||||||
29. [**Components update**](https://x.com/HyperFrames_/status/2084338269194732003) — combine the expanded component set in a project.
|
|
||||||
30. **Coming next** — the official Day 30 lesson has not been published yet. Follow [@HyperFrames_](https://x.com/HyperFrames_) for the final entry.
|
|
||||||
|
|
||||||
## Continue in the docs
|
<CardGroup cols={2}>
|
||||||
|
<Card title="Day 20 — Claude Design" href="https://x.com/HyperFrames_/status/2081184676430160379">
|
||||||
|
{/* VISUAL: needs the Claude Design film published — a moodboard of swatches, specimens and reference shots resolving into one connected direction. Local source: /tmp/rend/out/claude-design-hyperframes-video.mp4 (40.0s, 1920x1080, audio). */}
|
||||||
|
|
||||||
|
Take a design direction into a HyperFrames project without redrawing it by hand.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 21 — Cloud rendering" href="https://x.com/HyperFrames_/status/2081491370485952790">
|
||||||
|
<img alt="Two commands, hyperframes auth login and hyperframes cloud render, zipping and uploading a project to HeyGen Cloud" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/render-targets-demo-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Render without making one local computer the bottleneck. Two commands hand the job to the cloud.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 22 — Components Catalog" href="https://x.com/HyperFrames_/status/2081798071353278651">
|
||||||
|
<img alt="The Studio Catalog tab, searching for a macOS Notification block and dropping it onto the timeline" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/studio-assets-catalog-loop-v1.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Add reusable scenes and effects instead of rebuilding them each time.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 23 — Templates and variables" href="https://x.com/HyperFrames_/status/2082197435246600341">
|
||||||
|
<img alt="Variant A and Variant B variable sets — different hook text, accent colour and creator clip — around one shared vertical composition" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-variables.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Turn one composition into repeatable versions. Plays in full [above](#three-lessons-in-full).
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 24 — Color grading" href="https://x.com/HyperFrames_/status/2082536413829235004">
|
||||||
|
<img alt="Rain-soaked street footage in Studio with a custom LUT applied and exposure, contrast and shadow controls beside it" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/tile-grading-v2.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Correct or stylize images and footage in code, and bring your own LUT packs.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 25 — Prompt Guide" href="https://x.com/HyperFrames_/status/2082892893027749917">
|
||||||
|
Find practical language and examples for directing an agent. The written guide lives in these docs, at [Prompting overview](/prompting/overview).
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 26 — Media effects and overlays" href="https://x.com/HyperFrames_/status/2083324288632045910">
|
||||||
|
<img alt="One halftone illustration rendered through four authored colour ramps — game green, synthwave, amber terminal and arctic" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/media-effects-showcase-v2.jpg" loading="lazy" decoding="async" style={{width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "8px", marginBottom: "12px", display: "block"}} />
|
||||||
|
|
||||||
|
Add designed treatments to real media. Two to six authored colour ramps change the whole visual language.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 27 — Deploy" href="https://x.com/HyperFrames_/status/2083633492894900456">
|
||||||
|
{/* VISUAL: routes 3 and 4 of the render-target film — AWS Lambda and self-hosted Cloud Run. The published render-targets-demo-v1 still shows route 2, so it is used on Day 21 instead of here. */}
|
||||||
|
|
||||||
|
Run rendering on your own cloud infrastructure. See [Deploy](/guides/deploy) for the four render routes.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 28 — Contribute to the Catalog" href="https://x.com/HyperFrames_/status/2084070152975634743">
|
||||||
|
{/* VISUAL: a contributed block going from local scaffold to merged catalog entry. No published asset matches this lesson. */}
|
||||||
|
|
||||||
|
Publish reusable building blocks for other creators. The [Catalog](/catalog) lists what has landed so far.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 29 — Components update" href="https://x.com/HyperFrames_/status/2084338269194732003">
|
||||||
|
{/* VISUAL: the three themed registry families — mk, yt and hw — combined in one composition. No published asset matches this lesson. */}
|
||||||
|
|
||||||
|
Combine the expanded component set in a single project.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="Day 30 — Coming next" href="https://x.com/HyperFrames_">
|
||||||
|
The official Day 30 lesson has not been published yet. Follow [@HyperFrames_](https://x.com/HyperFrames_) for the final entry.
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Related topics
|
||||||
|
|
||||||
- [Choose a creation workflow](/workflows)
|
- [Choose a creation workflow](/workflows)
|
||||||
- [Direct your agent with the Prompt Guide](/prompting/overview)
|
- [Direct your agent with the Prompt Guide](/prompting/overview)
|
||||||
|
|||||||
Reference in New Issue
Block a user