docs(prompting): validated showreel + handmade prompts, fix spec defects they exposed

This commit is contained in:
Vance Ingalls
2026-07-29 14:30:08 -07:00
parent 44f9db4259
commit d888be198d
3 changed files with 17 additions and 3 deletions
+8 -1
View File
@@ -78,7 +78,14 @@ Rule 1's ambient idle is *continuous* — a slow, smooth breathing scale. Rule 7
> A paper-cutout garden scene: flowers, leaves, and a bee drifting on visible wires, like stop-motion. Seed a random offset per element at the start (same seed every render) so each leaf sways to a slightly different rhythm, but hold each position for exactly two frames before stepping to the next — discrete jumps, not a smooth tween. It should read as intentionally handmade, not glitchy.
Name the seed and the hold length explicitly — "seeded" and "two-frame hold" are the two words that keep an agent from reaching for `Math.random()` and quietly breaking every re-render. Ask for it, and seeking to frame 214 twice gives you the identical frame twice, exactly like every other HyperFrames render — the imperfection is designed in, not left to chance.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-handmade.mp4" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — paper cutouts on visible wires, every position held for exactly two frames, and byte-identical across two separate renders.*
Name the seed and the hold length explicitly — "seeded" and "two-frame hold" are the two words that keep an agent from reaching for `Math.random()` and quietly breaking every re-render.
<Warning>
**Ask for the hold in frames, not seconds.** Validating this prompt surfaced a real trap: quantizing the hold in the *time* domain (`Math.floor(t / 0.0667)`) produces irregular holds — a three-frame group here, a one-frame group there — because the renderer's seek times don't land on exact 1/30-second doubles, so the arithmetic drifts across step boundaries. Quantize on the integer frame index instead (`Math.floor(Math.round(t * fps) / 2)`), which tolerates any seek error under half a frame. The visible symptom is a stop-motion cadence that stutters unevenly instead of ticking, and it is worth naming in the prompt: "hold each position for exactly two frames, quantized on the frame index." Note also that a hold specified in frames is coupled to the composition's fps — re-rendering at a different frame rate changes the hold's duration.
</Warning> Ask for it, and seeking to frame 214 twice gives you the identical frame twice, exactly like every other HyperFrames render — the imperfection is designed in, not left to chance.
Unseeded randomness is the obvious way to break cold-render correctness, but a handful of GSAP lint rules catch subtler seek-order mistakes — relative tweens colliding with another writer, `repeatRefresh` combined with a relative value, function-valued tween vars, DOM measurement inside timeline callbacks — see the [seek-order safety rows](/prompting/rules-and-anti-patterns#seek-order-safety) in the appendix if the linter ever flags one.
+1 -1
View File
@@ -11,7 +11,7 @@ The skills enforce these automatically, but if you hand-edit compositions or deb
1. **Register all timelines** on `window.__timelines` — the renderer can't seek animations it doesn't know about.
2. **Video elements must be `muted`** — audio goes in separate `<audio>` elements so the renderer can mix it.
3. **No `Math.random()`** — random values produce different frames on each render, breaking determinism. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-random values. See [Handmade, still deterministic](/prompting/motion#handmade-still-deterministic).
3. **No `Math.random()`** — random values produce different frames on each render, breaking determinism. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-random values. And when you want a stepped, stop-motion hold, quantize it on the **integer frame index**, not on elapsed seconds — seek times don't land on exact 1/fps doubles, so second-domain arithmetic drifts and the hold length stutters. See [Handmade, still deterministic](/prompting/motion#handmade-still-deterministic).
4. **Synchronous timeline construction** — no `async`/`await` or `fetch()` during GSAP timeline setup.
5. **Timed elements need `class="clip"`** — plus `data-start`, `data-duration`, and `data-track-index`.
6. **Add entrance animations to every scene** — elements appearing without animation feel broken on video.
+8 -1
View File
@@ -5,7 +5,14 @@ description: "Write a visual spec — every element named, positioned, colored,
This is the dial's third setting, [full visual spec](/prompting/specification-dial), pushed all the way: not a word from the [vocabulary](/prompting/vocabulary) list, but every element of a scene written out like a designer's spec. You can carry a specific look in words alone when the prompt reads like a designer's spec — every element named, positioned, colored, and timed. Compare a loose description ("dark night scene, mountain, glowing ring, title fades in") with a spec:
> 8-second 1920x1080 title card. Scene, back to front: #0a0e2a night sky with faint grain; a huge concentric ring system (5 rings, 1px strokes at 8% white opacity, innermost ring glowing #4a5fd9) centered 40% from the top; behind the rings an orange radial glow (#ff6a2b core → transparent, ~300px) igniting at the peak's right shoulder from 2s; a low-poly mountain (6-8 dark navy facets, #141a3d#1e2650) filling the lower third, a white road S-curving up its face with a soft glow; thin horizontal cloud streaks (white, 6% opacity) drifting right at two heights; a man's silhouette, pure black, ~90px tall, bottom-right, fading in at 2.5s. At 3.5s "SHOWREEL" — thin geometric sans, ~140px, 0.35em tracking, white at 90% — fades in per letter across the ring center, the orange glow bleeding through the O. Slow 4% push-in across the full 8s. No audio.
> 8-second 1920x1080 title card. Scene, back to front: #0a0e2a night sky with faint grain; an orange radial glow (#ff6a2b core ~150px, falling off to transparent by ~430px) igniting at the mountain peak's right shoulder from 2s, positioned so its upper falloff reaches the wordmark's baseline; over it a huge concentric ring system (5 rings, 1px strokes at 8% white opacity, innermost ring glowing #4a5fd9) centered 40% from the top; a low-poly mountain (6-8 dark navy facets, #141a3d#1e2650) filling the lower third with its apex left of center, a white road S-curving up its face with a soft glow; thin horizontal cloud streaks (white, 6% opacity) drifting right at two heights; a man's silhouette, pure black, ~90px tall, bottom-right, fading in at 2.5s. At 3.5s "SHOWREEL" — thin geometric sans, ~140px, 0.35em tracking, white at 90% — fades in per letter across the ring center, the glow bleeding up through the letterforms above the peak. Slow 4% push-in across the full 8s. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-showreel.mp4" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the spec above, unedited.*
<Note>
Two clauses in this spec were **corrected by building it.** The first draft listed the scene "back to front" but put the rings before a glow it described as sitting *behind* the rings, and it sized the glow at "~300px" — too small to physically reach the wordmark it was supposed to bleed through, given the ring center and mountain positions the same sentence pins down. Neither error is visible on the page; both are obvious the moment someone renders it. That is the argument for the [validation rule](/prompting/iterating#the-gates-cant-tell-you-its-good) this guide runs on: a spec dense enough to be useful is dense enough to be internally inconsistent, and only a render finds out.
</Note>
Every element is a primitive the agent can build directly. Two honest limits: