Files
hyperframes/skills/hyperframes-animation/rules/asr-keyword-glow.md
T
WaterrrForever 853256403b feat(skills): c2v mining pass — 7 new blueprints, 10 new rules, compacted recipe corpus (#2680)
* feat(skills): c2v mining pass over animation blueprints and rules

Compacts ~45 existing animation rules/blueprints into tighter recipe form
(net -3.4k lines) and adds 17 mined from the c2v corpus:

- 7 blueprints: agent-progress-theater, camera-journey, fixed-anchor-cycle,
  panel-edit-live-sync, prompt-type-submit-generate,
  transcript-scroll-artifact-reveal, zoom-out-workspace-reveal
- 10 rules: 3d-camera-flight, anchored-layout-expand, chart-scrub-readout,
  chromatic-glitch, control-target-sync, cursor-drag, gradient-text-sweep,
  multi-cursor-choreography, particle-burst, theme-crossfade-morph






Both indexes updated.

* feat(skills): sync product-launch script bank with mined blueprint roles

The role->blueprint script bank in product-launch-video/story-design.md is
kept 1:1 with blueprints-index role declarations, which the c2v mining pass
expanded. Adds the 25 missing entries (script-shape descriptor + example
lines + pattern): 13 for the 7 new blueprints, 12 for role widenings on 7
existing ones (cursor-ui-demo, dataviz-countup, titlecard-reveal, et al.),
and states the 1:1 sync contract in the bank's intro.

* docs(skills): cover constellation-hub scatter-drift variant in the script bank

Review follow-up on #2680: the SOCIAL_PROOF constellation-hub entry
patterned only the orbit shape; the c2v pass added a scatter-drift
end-card variant with the opposite geometry (no hub, no ring). Adds an
example line and extends the pattern so a scatter-drift beat's VO isn't
steered toward the orbit shape.
2026-07-21 17:28:55 +08:00

6.6 KiB
Raw Blame History

name, description, metadata
name description metadata
asr-keyword-glow Keywords glow + scale up when "spoken" — attack/sustain/release envelope synced to per-word timestamps. Even without real audio, hardcoded timings create a "narrator emphasis" effect.
tags
asr, audio-sync, highlight, glow, keyword, text, speech, emphasis

ASR Keyword Glow

Words in a phrase visually activate (glow blur + scale) when "spoken", following an attack-sustain-release envelope over per-word { start, end } timestamps. In a real ASR pipeline the timings come from a word-level transcript (hyperframes transcribe — same shape); for promo video, hand-author them to control emphasis pacing. The envelope never falls to zero after a word — it decays to a rest level, leaving a breadcrumb of recent emphasis.

How It Works

A single linear driver tween (ease: "none" — any other ease distorts the per-word envelope; do not change) sweeps scene time; its onUpdate loops over ALL words computing each one's envelope: 0 before start, linear attack to 1 over ATTACK_DUR, sustain at 1 until end, decay to REST_LEVEL over RELEASE, then hold at rest. The envelope drives text-shadow blur and scale — one driver for the whole phrase, never one tween per word (60+ words would bloat the timeline).

Recipe

<!-- inside a standard scene clip (hyperframes-core) -->
<div class="phrase">
  <span class="word" data-word="{w1Key}">{w1}</span>
  <span class="word" data-word="{w2Key}">{w2}</span>
  <!-- … the final word may be the brand, with the .brand modifier -->
  <span class="word brand" data-word="{brandKey}">{brandWord}</span>
</div>
.phrase {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  color: {restColor};
}
.word {
  display: inline-block; /* required for transform on <span> */
  transform-origin: 50% 50%;
  text-shadow: 0 0 0 {glowColorTransparent};
}
.word.brand {
  color: {brandAccentColor};
}
// Per-word spoken windows — one entry per span; brand word 1.5-2× a normal word's window.
const TIMINGS = {
  // {w1Key}: { start: …, end: … },  — seconds, local to the scene
};

function envelope(time, start, end) {
  if (time < start) return 0;
  if (time < end) return Math.min((time - start) / ATTACK_DUR, 1);
  const releaseEnd = end + RELEASE;
  if (time < releaseEnd) return 1 - ((time - end) / RELEASE) * (1 - REST_LEVEL);
  return REST_LEVEL;
}

const words = document.querySelectorAll(".word");
const driver = { t: 0 };
tl.to(
  driver,
  {
    t: SCENE_DURATION,
    duration: SCENE_DURATION,
    ease: "none", // linear — t maps 1:1 to scene time
    onUpdate: () => {
      words.forEach((el) => {
        const timing = TIMINGS[el.dataset.word];
        if (!timing) return;
        const env = envelope(driver.t, timing.start, timing.end);
        el.style.textShadow = `0 0 ${MAX_BLUR * env}px ${glowColorRgba(env)}`;
        el.style.transform = `scale(${1 + MAX_SCALE_BOOST * env})`;
      });
    },
  },
  0,
);

glowColorRgba(env) returns the glow color with env-modulated alpha.

Variations

  • Karaoke style (RECOMMENDED for video narration) — the default amplitudes read too subtle in video: inactive words still dominate. Render inactive words DIM and lerp the active word toward bright + larger; at any moment 12 words are bright (spoken + lingering rest) and the rest is dim. Use for short phrases (510 words) where one word at a time should POP; keep the subtle default for long dense text. Pushes MAX_BLUR, MAX_SCALE_BOOST, and REST↔ACTIVE contrast; everything else identical:
function lerpChannel(a, b, t) {
  return Math.round(a + (b - a) * t);
}
function colorAt(env, isBrand) {
  const target = isBrand ? BRAND_RGB : ACTIVE_RGB;
  return `rgb(${lerpChannel(REST_RGB.r, target.r, env)}, ${lerpChannel(REST_RGB.g, target.g, env)}, ${lerpChannel(REST_RGB.b, target.b, env)})`;
}
// in onUpdate: el.style.color = colorAt(env, el.classList.contains("brand"));
  • Multi-octave glow — multiply the sustain by 1 + sin(driver.t × PULSE_HZ) × PULSE_AMPLITUDE so high-emphasis words breathe at peak.
  • Color shift on the peak — same channel-lerp from restColorpeakColor as env rises (non-karaoke form).
  • 3D pop-out — add translateZ(env × MAX_POP_Z) so the spoken word leans toward camera; requires perspective on the parent.
  • From real ASR transcripts — convert { word, start_ms, end_ms } entries to seconds and feed in identically.

Values

token default style karaoke style notes
ATTACK_DUR 0.10.25s same must be < the shortest word's window or it never reaches 1
RELEASE 0.20.5s same decay to rest
REST_LEVEL 0.150.4 0.050.2 > 0 (breadcrumb), < 1
MAX_BLUR 1525px 3045px bigger = "shouting"
MAX_SCALE_BOOST 0.030.10 0.150.25 additive at peak (0.08 ⇒ scale 1.08)
PULSE_HZ / AMP 410 rad/s / 0.10.3 multi-octave variation
MAX_POP_Z 2060px 3D variation
SCENE_DURATION = data-duration same driver must end in sync with the scene's seek window

Critical Constraints

  • Timings monotonic, non-overlapping — every entry's end < the next entry's start; overlapping windows make the envelope ambiguous.
  • Brand word window 1.52× a normal word — the brand is the headline; let it sustain.
  • Driver ease stays "none" — any other ease warps every word's envelope timing.
  • text-shadow, not box-shadow — the glow must hug the GLYPH (speaking emphasis), not the inline-block rectangle.
  • One driver looping all words — never one tween per word.
  • Commit to a style — values between the default and karaoke columns yield awkward "half-loud" emphasis.
  • Climax dwell ≥1s after the final word's emphasis — the last word IS the headline beat.

See also

3d-text-depth-layers (depth on the active word at peak) · sine-wave-loop (idle breathe between emphasis moments) · context-sensitive-cursor (typewriter matching the ASR cadence) · /media-use for hyperframes transcribe and caption rendering.