Files
hyperframes/skills/hyperframes-animation/rules/depth-scatter-assemble.md
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.9 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
depth-scatter-assemble N elements scatter into / reassemble from a rotating 3D depth-cloud, each starting at a deterministic index-derived 3D offset and settling to a clean flat layout.
tags
3d, scatter, assemble, depth, cloud, tumble, kinetic, letter, fragment, logo, reassemble

Depth Scatter ↔ Assemble

N elements (glyphs, cards, logo fragments) fly in from a rotating 3D depth-cloud and lock into a flat layout — or the reverse. Each element has its OWN index-derived point in the cloud (translateZ depth + rotateX/Y tumble + x/y scatter). Distinct from orbit-3d-entry (flip-in then continuous orbit) and center-outward-expansion (flat burst from one shared center): here the resolve is a flat assembled layout.

How It Works

Each element's flat target lives in data-target-x/y; its scattered state is pure trig on its index — golden-angle spread, stepped depth — so the cloud is byte-identical every render with no Math.random:

const GOLDEN = Math.PI * (3 - Math.sqrt(5)); // ~2.39943 rad — even spread, no clumping
const a = i * GOLDEN;
const scatterX = Math.cos(a) * RADIUS;
const scatterY = Math.sin(a) * RADIUS;
const scatterZ = Z_NEAR - (i / (n - 1)) * (Z_NEAR - Z_FAR); // stepped depth
const rotX = Math.sin(a) * TUMBLE;
const rotY = Math.cos(a) * TUMBLE;

Elements are PARKED at their scatter points (gsap.set, opacity 0) before any tween, then each tweens to its flat target while the whole stage slowly rotates so the scatter has life before it locks. Requires perspective on the scene root and preserve-3d on the stage AND each element, or depth + tumble flatten to a 2D scale.

Recipe

<!-- inside a standard scene clip (hyperframes-core) -->
<div class="cloud-stage">
  <div class="frag" data-target-x="-260" data-target-y="0">{glyph1}</div>
  <div class="frag" data-target-x="-130" data-target-y="0">{glyph2}</div>
  <!-- … one .frag per glyph / fragment … -->
</div>
.scene-root {
  display: grid;
  place-items: center;
  perspective: 1400px; /* REQUIRED */
}
.cloud-stage {
  position: relative;
  display: grid;
  place-items: center;
  transform-style: preserve-3d;
  will-change: transform;
}
.frag {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-style: preserve-3d;
  backface-visibility: hidden; /* hides the mirrored face mid-tumble */
  will-change: transform, opacity;
}
const frags = Array.from(document.querySelectorAll(".frag"));
const n = frags.length;
const GOLDEN = Math.PI * (3 - Math.sqrt(5));

// 1) Park every fragment in the cloud BEFORE any tween fires
const scatter = frags.map((el, i) => {
  const a = i * GOLDEN;
  const depthT = n > 1 ? i / (n - 1) : 0;
  return {
    x: Math.cos(a) * RADIUS,
    y: Math.sin(a) * RADIUS,
    z: Z_NEAR - depthT * (Z_NEAR - Z_FAR),
    rotationX: Math.sin(a) * TUMBLE,
    rotationY: Math.cos(a) * TUMBLE,
  };
});
frags.forEach((el, i) => gsap.set(el, { xPercent: -50, yPercent: -50, ...scatter[i], opacity: 0 }));

// 2) The cloud rotates so the scatter has life during assembly
tl.to(
  ".cloud-stage",
  { rotationY: CLOUD_SPIN_DEG, duration: CLOUD_SPIN_DUR, ease: "power1.out" },
  0,
);

// 3) ASSEMBLE — cloud point → flat target, index stagger = cloud collapsing inward
frags.forEach((el, i) => {
  tl.to(
    el,
    {
      x: Number(el.dataset.targetX),
      y: Number(el.dataset.targetY),
      z: 0,
      rotationX: 0,
      rotationY: 0,
      opacity: 1,
      duration: ASSEMBLE_DUR,
      ease: ASSEMBLE_EASE,
    },
    i * STAGGER,
  );
});

Variations

  • Tumble-swap (the beat-change hand-off): two glyph sets share the cloud; ONE shared 0→1 progress tween drives both in its onUpdate — outgoing lerps layout→cloud with opacity: 1p, incoming lerps cloud→layout with opacity: p. Two separate tweens drift out of phase under seek and the cross stops reading as one hand-off. Inject per-glyph spans per phrase at setup (measure advance widths after document.fonts.ready — single-scene only).
  • Radial letter-explode → resolve: flat-plane special case — Z_NEAR = Z_FAR = 0, small TUMBLE; reverse the assemble for the explode. Pure in-plane.
  • Scatter-OUT: reverse assemble (layout → cloud, opacity 1→0) ONLY as the composition's final beat — mid-shot it reads as the shot ending.
  • Parallax lockup: back layers get deeper |Z_FAR| + longer ASSEMBLE_DUR, foreground shallower/shorter — depth-speeded slide-in that locks into the logo.

Values

token range notes
n 414 (fragments 49) above ~14 individual paths stop reading
RADIUS 250700px keep the farthest scatter in frame or fragments pop in with no travel
Z_NEAR / Z_FAR +150…+450 / 150…−500 large |z| needs a wider perspective or fragments smear
TUMBLE 40110° past 90° glyphs show blank mid-tween (intended); cap ~80° for one-faced cards
ASSEMBLE_DUR 0.71.4s
ASSEMBLE_EASE power3.out default expo.out snaps, back.out(1.4) seats with overshoot; never in
STAGGER 0.030.09s n × STAGGER < ASSEMBLE_DUR — one collapsing motion, not a queue
CLOUD_SPIN_DEG / _DUR 1560° over ≥ dur gentle life; too fast competes with the assembly
SWAP_DUR 0.51.0s on the beat boundary; shorter = hard cross

Critical Constraints

  • Every scattered value is index-derivedcos/sin(i × GOLDEN) + stepped z. The golden angle spreads points evenly with no clumps and no Math.random.
  • gsap.set the cloud BEFORE adding tweens — skipping it leaves frame 0 showing the assembled layout, then a teleport when the first tween starts.
  • perspective + preserve-3d on stage AND each fragment — missing any one flattens the depth.
  • Resolve flat — settled state is z: 0, rotations 0; a still-tilted resolve reads unfinished.
  • Tumble-swap: one shared progress for both glyph sets.
  • Depth ordering is automatic inside preserve-3d (paint order follows actual Z) — no manual z-index, unlike the orbit case's capped band.

See also

orbit-3d-entry (settles into a continuous orbit instead) · hacker-flip-3d (glyphs decode on arrival) · 3d-text-depth-layers (extrude the locked wordmark) · center-outward-expansion (flat 2D cousin) · sine-wave-loop (idle breathe on the resolved layout).