Skip to content

docs(skills): teach shared cut constants and fill lights for Three.js scenes - #5077

Merged
miga-heygen merged 1 commit into
mainfrom
docs/three-in-canvas-cuts
Oct 5, 2026
Merged

miga-heygen merged 1 commit into
mainfrom
docs/three-in-canvas-cuts

Conversation

@miga-heygen

Copy link
Copy Markdown
Contributor

What

skills/hyperframes-animation/adapters/three.md gains two sections:

  • Camera Cuts Inside One Canvas: declare each in-canvas cut once and read the constant from both the shot selector and every object's visibility window. Verify on the last frame before each cut with snapshot --at (ceil(cut*fps)-1)/fps.
  • Lighting Lit Meshes: a MeshStandardMaterial lit by a single key light, with no ambient, env map or emissive, renders as a flat black cutout when the camera faces the light. Add a fill such as a HemisphereLight driven from the same source as the key at a fixed ratio, not the same number.

skills-manifest.json is regenerated.

Why

A Three.js composition that switched shots by time had visibility windows written as separate literals (t > 22.9 against a cut at t < 23). At 30 fps that put the next shot's meshes into frames 688 and 689 of the previous shot. Checking one still per shot did not catch it. The same scene's rocks, lit only by a sun PointLight while the camera faced the sun, rendered as black silhouettes. The skill covered neither case.

A re-render with the CLI matched the original output frame for frame (PSNR 34 to 39 dB), so neither problem comes from the engine.

Verification

  • bun packages/cli/scripts/gen-skills-manifest.ts --check: in sync (21 skills).
  • oxfmt --check: clean.
  • Claims checked against the code: snapshot --at seeks the exact instant (snapshot.ts, exactTime: true), and (ceil(23*30)-1)/30 = 22.9667 is frame 689.
  • An independent review flagged that the first sample used t at top level and that it implied copying the key light's intensity onto the fill. Both are fixed in this head.

Signed: Miga

… scenes

The three adapter skill had no guidance for camera cuts made inside one
canvas. A visibility window written as its own literal near a cut shows
the next shot's objects for the last frames of the previous shot, a
two-frame pop-in that a still per shot does not reveal. The skill now
says to declare each cut once, read it in both the shot selector and
every visibility window, and snapshot the last frame before each cut.

It also explains why a MeshStandardMaterial lit by a single key light
renders as a flat black cutout when the camera faces that light, and to
add a fill such as a HemisphereLight driven at a fixed ratio to the key.

Signed: Miga

Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>

@terencecho terencecho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocker at b6999bc4fdf97ba42f693092eff36efa1eaafe63. Approving; the CI jobs were all green when I stamped (26 pass, 30 skipping).

What I checked

  • Effective diff is skills/hyperframes-animation/adapters/three.md (+32/-1 across two sections) and the skills-manifest.json hash line; nothing else.
  • bun packages/cli/scripts/gen-skills-manifest.ts --check on the PR head: "skills-manifest.json is in sync (21 skills)".
  • The CLI claims hold: snapshot has --at and --no-end (packages/cli/src/commands/snapshot.ts, the --no-end text says it captures only your exact --at times). (Math.ceil(23*30)-1)/30 = 689/30 = 22.9667.
  • The pop-in claim: against a cut at 23, a window of t > 22.9 is true for frames 688 (22.9333) and 689 (22.9667) and false for frame 687 (22.9), so it is a two-frame leak at 30 fps, as written.
  • The sample is self-consistent: CUT_B/CUT_C are read by both shotFor and the visibility window, and renderAt(t) is the existing adapter shape used above it in the file.
  • The lighting advice is standard Three.js behaviour: a MeshStandardMaterial with only a point or directional light and no ambient, env map or emissive is black on its unlit side. A HemisphereLight fill with its own intensity (the units differ from the key light) is sound.
  • No credentials or URLs beyond the existing CLI usage.

Non-blocking

  • The Camera Cuts sample is a pattern, not a runnable file (shotA/shotB/shotC and the prop arrays are placeholders); it reads as intended in context.

Reviewed on b6999bc4fdf97ba42f693092eff36efa1eaafe63. This is a review verdict, not authorization to merge.

— Review by tai (pr-review)

@miga-heygen
miga-heygen added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit 6c353d8 Oct 5, 2026
56 checks passed
@miga-heygen
miga-heygen deleted the docs/three-in-canvas-cuts branch October 5, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants