All skills
heygen-com avatar

/hyperframes-animation

@ff6e210
by HeyGenheygen-com/hyperframes55k stars
4,998

All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.

Use this Skill: https://skilld.dev/gh/heygen-com/hyperframes/hyperframes-animation

This session only. Nothing lands on disk.

rulesgsap-effects.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

GSAP Effects for HyperFrames

Drop-in animation patterns. Snippets show mechanism only, inside a standard scene clip (hyperframes-core); assume tl exists.

  • Typewriter — character-by-character reveal with optional cursor / backspace / word rotation
  • Audio Visualizer — pre-extract audio data, drive Canvas/DOM rendering from the timeline

Typewriter

Requires GSAP's TextPlugin alongside the core script:

<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/TextPlugin.min.js"></script>
<script>
  gsap.registerPlugin(TextPlugin);
</script>

Basic

const text = "Hello, world!";
const cps = 10; // chars per second — see timing table
tl.to(
  "#typed-text",
  { text: { value: text }, duration: text.length / cps, ease: "none" },
  startTime,
);

Blinking Cursor

Three rules: one cursor visible at a time (hide previous before showing next); cursor must blink when idle (after typing, during holds); no gap between text and cursor (elements flush in HTML).

<span id="typed-text"></span><span id="cursor" class="cursor-blink">|</span>
@keyframes blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}
.cursor-blink {
  animation: blink 0.8s step-end infinite;
}
.cursor-solid {
  animation: none;
  opacity: 1;
}
.cursor-hide {
  animation: none;
  opacity: 0;
}

Pattern: blink → solid (typing starts) → type → blink (typing done):

tl.call(() => cursor.classList.replace("cursor-blink", "cursor-solid"), [], startTime);
tl.to("#typed-text", { text: { value: text }, duration: dur, ease: "none" }, startTime);
tl.call(() => cursor.classList.replace("cursor-solid", "cursor-blink"), [], startTime + dur);

Multi-line handoff: hide previous cursor → blink new → brief pause (~0.5s) → solid when typing. Never go hidden → solid (skips the idle blink).

Backspacing

TextPlugin removes from the front — wrong for backspace. Use manual substring removal:

function backspace(tl, selector, word, startTime, cps) {
  const el = document.querySelector(selector);
  const interval = 1 / cps;
  for (let i = word.length - 1; i >= 0; i--) {
    tl.call(
      () => (el.textContent = word.slice(0, i)),
      [],
      startTime + (word.length - i) * interval,
    );
  }
  return word.length * interval;
}

Spacing With Static Text

A typewriter word next to static text (<span>Ship something</span><span style="margin-left:14px"><span id="word"></span><span id="cursor">|</span></span> in a baseline-aligned flex row): use margin-left on the wrapper span. Don't use flex gap (it spaces the cursor from the text) and don't put a trailing space in the static text (it collapses when the dynamic span is empty).

Word Rotation

Type → hold → backspace → next word; cursor blinks during every idle moment:

let offset = 0;
words.forEach((word, i) => {
  const typeDur = word.length / 10;
  // cursor: solid while typing, blink during holds (same call pattern as above)
  tl.to("#typed-text", { text: { value: word }, duration: typeDur, ease: "none" }, offset);
  offset += typeDur + 1.5; // hold
  if (i < words.length - 1) offset += backspace(tl, "#typed-text", word, offset, 20) + 0.3;
});

Appending Words

Build a sentence word-by-word into the same element: keep an accumulated string, each step tweens text: { value: accumulated + " " + word } with duration: newChars / cps, then advances the offset.

Timing Guide

CPS Feel Good for
3-5 Slow, deliberate Dramatic reveals, suspense
8-12 Natural typing Dialogue, narration
15-20 Fast, energetic Tech demos, code
30+ Near-instant Filling long blocks

Audio Visualizer

Pre-extract audio data, drive Canvas / DOM rendering from the timeline. Do not use the Web Audio API at render time — there's no playback during seek.

Extract Audio Data

Bundled extractor (requires ffmpeg + Python numpy):

python skills/hyperframes-creative/scripts/extract-audio-data.py audio.mp3 -o audio-data.json
python skills/hyperframes-creative/scripts/extract-audio-data.py video.mp4 --fps 30 --bands 16 -o audio-data.json

Output: { "fps": 30, "totalFrames": 5415, "frames": [{ "time": 0.0, "rms": 0.42, "bands": [0.8, 0.6, 0.3] }] } — rms (0-1) is overall loudness; bands[] (0-1) are frequency magnitudes, index 0 = bass, each band normalized independently.

Loading (Synchronously)

Inline the JSON for small files (< ~500 KB), or sync XHR for large ones:

const xhr = new XMLHttpRequest();
xhr.open("GET", "audio-data.json", false); // synchronous — deliberate
xhr.send();
const AUDIO_DATA = JSON.parse(xhr.responseText);

Do NOT use async fetch() — HyperFrames reads window.__timelines synchronously after page load; building the timeline inside .then() means it isn't ready when capture starts.

Driving the Timeline

Canvas 2D is the workhorse (bars, waveforms, circles, gradients) — one tl.call per frame:

const ctx = document.getElementById("viz").getContext("2d");
for (let f = 0; f < AUDIO_DATA.totalFrames; f++) {
  tl.call(
    () => {
      const frame = AUDIO_DATA.frames[f];
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // draw using frame.rms and frame.bands
    },
    [],
    f / AUDIO_DATA.fps,
  );
}

WebGL / Three.js: HyperFrames patches THREE.Clock for deterministic time — update uniforms from audio data each frame. DOM elements: fine under ~20 elements, slower than Canvas beyond that.

Smoothing

let prev = null;
const smoothing = 0.25; // 0.1-0.2 snappy, 0.3-0.5 flowing
function smooth(f) {
  const raw = AUDIO_DATA.frames[f];
  if (!prev) prev = { rms: raw.rms, bands: [...raw.bands] };
  else {
    prev = {
      rms: prev.rms * smoothing + raw.rms * (1 - smoothing),
      bands: raw.bands.map((b, i) => prev.bands[i] * smoothing + b * (1 - smoothing)),
    };
  }
  return prev;
}

Design Guide

  • Spatial mapping — horizontal: bass left, treble right; vertical: bass bottom; circular: bass at 12 o'clock, wrap clockwise (mirror for a full circle).
  • Bass drives big moves (scale, glow, position); treble drives detail (shimmer, flicker, edges); RMS drives globals (background brightness, overall energy).
  • Pick 2-3 animated properties — more looks noisy. Keep minimums above zero so quiet sections still have life.
  • Band count: 4 = background glow/pulse, 8 = bar charts, 16 = detailed EQ (default), 32 = dense radial layouts.
  • Layering: stack canvases with z-index — a background layer driven by bass/rms under a foreground layer driven by individual bands gives depth without per-element complexity.

Source: SKILL.md on GitHub

No alerts3d3 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    The skill is a comprehensive knowledge base and utility set for HyperFrames animations. It includes an auditing tool that dynamically installs vendor-specific dependencies from the NPM registry to analyze project timelines. The behavior is documented, tested, and follows established development patterns for CLI utilities.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: LOW · No issues

Signed by skilld at ff6e210. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 16 hours ago.

Activeupdated 4 days ago

README badge

README badge for heygen-com/hyperframes/hyperframes-animation