The HyperFrames docs teach you the API; this file is about taste. The numbers below come from a piece that actually shipped, so treat them as defaults: deviating is fine, but it must be deliberate, not just something you happened to overlook.
Adapted from whaleyxbt/claude-motion's
motion-designskill (MIT, see theLICENSEin the same folder). The original targets Remotion; this edition is rewritten for HyperFrames (HTML + GSAP) and adds Traditional Chinese typography rules. Sections marked "(new in this edition)" are not from the original.
When used within the motion-explainer workflow (new in this edition)
motion-explainer(and -images、-video、soil-motion-deck) using the official faceless-explainer workflow, the following three things follow that workflow and this file's approach does not apply:
- Timing: Beats come from
STORYBOARD.mdandaudio_meta.json(the timing of narration clauses), and each frame'sdata-start/data-durationis written byassemble-index.Do not create a separatetimeline.js/window.TL(Section 1). - Creating a project: Use
npx hyperframes init(see motion-explainer Section 3), not the approach in the "Workflow" section. - Fonts and colors: Determined by the project's
theme.json(default: Source Han Sans Bold, three-color theme);theme.pyoverrides the font and color palette written in each frame. The font family recommendations in Sections 5 and 6 apply only when there is notheme.json.
The remaining taste rules (easing, springs, reading dwell, composition, Chinese typography, accent color only for emphasis) apply as usual.
Division of labor with the official HyperFrames skills (new in this edition)
The official HeyGen HyperFrames skills are also installed locally (hyperframes、hyperframes-core、hyperframes-animation , etc.). The division of labor is as follows:
- Official skills govern "how to do it": project structure,
data-*attributes, API usage, workflow routing, ready-made registry components. Where API usage conflicts with this file, the official version prevails. - This file governs "taste": easing, rhythm, dwell time, fonts, color palettes, Chinese typography. When using the official
hyperframes-animationrules / blueprints, the timing and easing numbers still default to this file. - Checking: Always go through
review-loop, which already includes the officialhyperframes check。
0. Hard rules for HyperFrames (new in this edition)
Violating these will cause the picture to flicker, misalign, or differ on every output:
-
Timing is determined by HTML: each scene is a
class="clip"element, placed on the timeline withdata-start、data-duration. The root element must havedata-composition-idand an explicitdata-duration。 -
Motion is handled by GSAP:
gsap.timeline({ paused: true }), registered towindow.__timelines["<composition-id>"]; the key name must equaldata-composition-id。 -
Give every important tween an explicit position (the third argument); when both ends matter, use
fromTo(), because the output seeks around non-linearly. -
Only animate
transformandopacity; do not repeatedly animatetop/left/width/height。 -
Prohibited: CSS
transition/@keyframes、Date.now()、requestAnimationFrame、setTimeout, no seededMath.random(), resources fetched midway through output. -
Don't use scripts to play, pause, or seek audio/video, and don't use scripts to show or hide clips based on time. Leave all of that to data attributes. To move a video, move the outer wrapper element.
-
Always use a seeded function for randomness:
function rand(seed) { let t = (seed += 0x6D2B79F5); t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }
1. Time is centralized in one place
-
Every beat is a named point in time (in seconds), grouped by scene (
s1、s2...), written in the project root'stimeline.js:window.TL = { s1: { start: 0, title: 0.3, items: [1.2, 1.45, 1.7], exit: 3.6 }, s2: { start: 3.4, drawOn: 3.5, hook: 5.2, end: 8 }, };In
index.html, load it with<script src="timeline.js"></script>before all composition scripts. -
Always take GSAP position parameters from
TL(relative forms likeTL.s1.title、TL.s1.exit + 0.05are fine), and never hardcode seconds in scenes. -
The scene frame's
data-start/data-durationmust match the scene boundaries ofTL. When retiming, changetimeline.jsfirst, then update these two attributes in sync. -
Place sound effects and narration with
<audio data-start="…">on the same set of beats. The review-loop's screenshot times are also taken fromTL, so visuals, audio, and checks all move together. -
All visuals must be determined solely by time; see Section 0.
2. Easing
The original's four curves are precisely recreated in GSAP using CustomEase . To load more gsap@3/dist/CustomEase.min.js, then register with gsap.registerPlugin(CustomEase) :
CustomEase.create("enter", "0.16,1,0.3,1"); // easeOut
CustomEase.create("exit", "0.6,0,0.9,0.35"); // easeIn
CustomEase.create("move", "0.65,0,0.35,1"); // easeInOut
CustomEase.create("land", "0.55,0,0.2,1.12"); // overshoot
| Curve | Use | Closest built-in name |
|---|---|---|
enter = bezier(0.16, 1, 0.3, 1) |
Entrances, reveals, any "arrival" | expo.out(nearly identical) |
exit = bezier(0.6, 0, 0.9, 0.35) |
Exits: move fast, no dragging | about power3.in |
move = bezier(0.65, 0, 0.35, 1) |
Movement between two states (flip, dock, morph) | power2.inOut(exactly the same) |
land = bezier(0.55, 0, 0.2, 1.12) |
Values that "settle" (a slider climbing to a certain cell) | No good built-in substitute; use CustomEase |
ease: "none"(linear) is only used for continuous motion: rotation, drift, particles, second hands.
To make one animation follow another value (for example, subtitles following a slider position), drive a proxy object with the same tween and derive in onUpdate ; don't give it another independent clock.
3. Spring
The original uses Remotion's physical spring; GSAP has no spring physics. The table below shows the approximations chosen for this version (new to this version, not the original's numbers); after output, use review-loop to confirm the feel:
| Feel | GSAP setting | Used for |
|---|---|---|
| Default settle | back.out(1.2),0.6 s |
General UI entrances |
| Heavy, steady | power3.out,0.8–1.0 s |
Large panels, reversals |
| Knob detents | back.out(1.7),0.35 s |
Controls jumping to a certain cell |
| Pop | back.out(2.2),0.45 s |
Annotations, badges, success dots |
| Springy accent | elastic.out(1, 0.4),0.9 s |
The climax moment used only once per video |
Use springs for things with a physical feel (UI, objects), and Bezier curves for text and camera-like movement.
4. Rhythm
- Stagger: English words about 0.09 s; Chinese character by character about 0.04–0.05 s, word by word about 0.09 s (the Chinese part is new to this version); list items and labels about 0.2–0.25 s; rings and dots about 0.06 s.
- Text revealAbout 0.8 s, with a mask: the outer
overflow: hidden, the inneryPercent: 100 → 0plus a bit of rotation (rotation: 3 → 0)。 - Exits are about half the entrance(0.35–0.55 s versus 0.7–1.25 s), and the exit uses the
exitcurve. - Scenes must overlap: the next scene starts drawing in before the previous one has fully left (e.g.
s2.drawOn4.02 s is earlier thans1.flipEnd4.4 s). A hard cut to blank looks like "loading". - Typingabout 0.035 s per character; when idle the cursor blinks at about 2.4 Hz (use
stepsorsetscheduled on the timeline, not a timer). - Reading dwell: every state meant to be read must hold after it settles. At least 1 s for a short sentence; add about 0.25 s for each word beyond three; for Chinese, add about 0.25 s for each additional 4 characters (new in this version). The final closing line at least 2 s. The reason the original's 22-second version exists is that the 15-second version's text flashed too fast: when in doubt, hold longer and move less.
5. Typography
- At most three font families: a display serif, an interface sans-serif, and a monospace (for labels, prompts, numbers).
- English (original): Instrument Serif / Inter / JetBrains Mono.
- Traditional Chinese (new in this version): Noto Serif TC / Noto Sans TC, with JetBrains Mono for numbers and labels. Put the font files in the project's
assets/fonts/, load them with@font-face, and don't rely on system fonts (they'll differ on another computer).
- English display type should be large (about 138 px on a 1080-wide canvas), with tight tracking (
-0.02em),line-height: 1。 - Chinese display type (new in this version): about 96–120 px at 1080 wide, no more than about 12 characters per line, tracking
0to0.02em(no negative tracking for Chinese),line-height: 1.15–1.25。 - one emphasis per line: English uses one italic word in the accent color; Chinese has no true italics, so carry the emphasis with the accent color plus a heavier weight (new in this version).
- Monospace labels: uppercase,
letter-spacing: 0.2–0.24em, color muted. They are just decoration; anything the viewer must read should be made large. - Phone test: at 360 px wide (the actual size seen in social feeds), the key message must still be legible. Check with a contact sheet.
6. Color
- (new in this version) Set the tone for the whole piece withthree colors, decided by the user like a presentation template: background color
bg, primary colorfg(text and line art), accent coloraccent, written in the project'stheme.json, applied via themotion-explainerseries skill'stheme.py. Use the accent color only for "answers, conclusions, key numbers, chapter cards". - Define the palette as CSS variables in
:root(--bg、--ink、--muted、--accent...); no new color codes should appear in scenes. - Only one accent color, and it has meaning: the thing that changes, the answer, the winner. If everything is the accent color, nothing is.
- Glow is driven by state, not time (
glow = effort): the light must tell the story.
7. Texture: nothing is completely still
- Film grain: overlay an SVG
feTurbulencefilter at about 0.09 opacity, changingseedevery two frames (scheduled on the timeline withset, the seed computed from the frame number). - Vignette + dot grid, drifting a few px per second:
radial-gradientvignette, the grid uses one linear tween spanning the whole video. - Hand-drawn "line jitter" (
feTurbulence+feDisplacementMap, also swapping seeds every two frames): use it for draft-like, low-completion states, fading out as quality improves. - Idle elements should breathe: a ticking clock, a blinking cursor, a slow rotation.
8. Continuity and transitions
- Prefer letting objects continue rather than hard-cutting. The original's cue bars, sparks and sliders travel across scenes and change roles—that's the difference between "designed" and "cobbled together". In HyperFrames, objects that cross scenes go in the root composition, not inside a scene.
- Every transition should be carried by the objects on screen (five result dots merge into one, then become a halo). No boilerplate wipe transitions.
- Use velocity: motion blur and squash are derived from "the value difference between this frame and the previous one", don't fake it.
- One climax moment per video (halo + impact). Build up to it the whole way; don't spend it too early.
9. Anti-patterns
- Everything fades in at the same time, or everything uses the same duration.
- Using linear easing for things that start and stop.
- Text still moving when it should be read.
- More than one focal point in a single frame.
- Content closer than about 60 px to the frame edge (safe area), unless it's a deliberate crop.
- Frame 0 is blank or all black: when social platforms autoplay muted, the first frame is the thumbnail.
- (New in this version) Controlling media playback or clip visibility in script, using CSS animations, using random without a seed—see section 0.
Workflow
- First write the beats into
timeline.js(a storyboard written in numbers), and confirm the pacing with the user before starting. - Use
npx hyperframes init <專案資料夾>to create the project; each scene is a composition incompositions/, shared components and cross-scene objects go inindex.html。 - After every meaningful change, run the
review-loopskill. - Handle audio only after the visual timing is locked: sound effects and music use
<audio data-start>matched to the beats ofTL. Leave the accent and emotion of Chinese narration to the user's ear; don't substitute ASR or similarity scores for it (new in this version).