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:
ukimsanov
2026-08-04 15:32:14 -07:00
parent c669616370
commit f742084fa8
17 changed files with 328 additions and 320 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+45 -267
View File
@@ -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.
+10
View File
@@ -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
View File
@@ -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"
] ]
}, },
+1 -1
View File
@@ -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.*
+2
View File
@@ -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.*
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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>
+2
View File
@@ -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.*
+2
View File
@@ -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.*
+2
View File
@@ -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.*
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -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.
+22 -10
View File
@@ -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
View File
@@ -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)