You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(skills): add rendering guardrails to hyperframes skill
Adds critical constraints discovered from eval analysis of 27
agent-generated compositions:
- Ban `repeat: -1` (broke loading-spinner, prompt 20)
- Ban async timeline construction (broke particle-logo, prompt 16)
- Enforce minimum font sizes: 16px data labels, 20px body text
(prompts 7, 8, 13, 14, 15, 19 all had illegible small text)
- Ban full-screen dark linear gradients (prompts 3, 5, 10, 14
all showed H.264 color banding)
- Mandate `<link>` font loading over CSS `@import` (prompts 7,
24 had font loading races)
- Updated output checklist with all new constraints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: skills/hyperframes/SKILL.md
+24-4Lines changed: 24 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -92,12 +92,16 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
92
92
93
93
## Rules (Non-Negotiable)
94
94
95
-
**Deterministic:** No `Math.random()`, `Date.now()`, or time-based logic.
95
+
**Deterministic:** No `Math.random()`, `Date.now()`, or time-based logic. Use a seeded PRNG if you need pseudo-random values (e.g. mulberry32).
96
96
97
97
**GSAP:** Only animate visual properties (`opacity`, `x`, `y`, `scale`, `rotation`, `color`, `backgroundColor`, `borderRadius`, transforms). Do NOT animate `visibility`, `display`, or call `video.play()`/`audio.play()`.
98
98
99
99
**Animation conflicts:** Never animate the same property on the same element from multiple timelines simultaneously.
100
100
101
+
**No `repeat: -1`:** Infinite-repeat timelines break the capture engine. Calculate the exact repeat count from composition duration: `repeat: Math.ceil(duration / cycleDuration) - 1`.
102
+
103
+
**Synchronous timeline construction:** Never build timelines inside `async`/`await`, `setTimeout`, or Promises. The capture engine reads `window.__timelines` synchronously after page load. If you need fonts loaded first, use a synchronous `document.fonts.load()` call or rely on `font-display: block` — the engine waits for the page `load` event.
104
+
101
105
**Never do:**
102
106
103
107
1. Forget `window.__timelines` registration
@@ -107,16 +111,27 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
107
111
5. Animate video element dimensions — animate a wrapper div
108
112
6. Call play/pause/seek on media — framework owns playback
109
113
7. Create a top-level container without `data-composition-id`
114
+
8. Use `repeat: -1` on any timeline or tween — always finite repeats
-Every composition loads its own fonts (`@import`or`@font-face`)
114
-
- Use `font-display: block` for local fonts
119
+
-Load fonts via `<link>` tags with `display=block` in `<head>`, NOT via CSS `@import`—`@import` is async and may not complete before the first frame capture
120
+
- Use `font-display: block` for `@font-face` declarations
115
121
- Add `crossorigin="anonymous"` to external media
116
-
- Minimum readable text: 20px landscape, 18px portrait
122
+
-**Minimum font sizes for rendered video (1080p at DPR 1):**
123
+
- Body/label text: 20px minimum (landscape), 18px minimum (portrait)
124
+
- Data labels, axis labels, footnotes: 16px minimum — anything smaller becomes illegible after encoding
125
+
- Headlines: 36px+ recommended
126
+
- Avoid sub-14px text entirely — it will be unreadable in the final MP4
117
127
- For dynamic text overflow, use `window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })` — returns `{ fontSize, fits }`
118
128
- All files live at the project root alongside `index.html`; sub-compositions use `../`
119
129
130
+
### Backgrounds and Color
131
+
132
+
-**Avoid full-screen linear gradients on dark backgrounds** — H.264 encoding creates visible color banding. Prefer: solid colors, radial gradients with limited range, or subtle noise/texture overlays to break up banding.
133
+
- For dark themes, use solid `#000` or `#0A0A0A` with localized radial glows rather than a linear gradient spanning the full viewport.
134
+
120
135
## Editing Existing Compositions
121
136
122
137
- Read the full composition first — match existing fonts, colors, animation patterns
@@ -129,6 +144,11 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
129
144
-[ ] Compositions in own HTML files, loaded via `data-composition-src`
130
145
-[ ]`<template>` wrapper on sub-compositions
131
146
-[ ]`window.__timelines` registered for every composition
147
+
-[ ] Timeline construction is synchronous (no async/await wrapping timeline code)
148
+
-[ ] No `repeat: -1` on any tween or nested timeline
149
+
-[ ] No text below 16px (data labels, footnotes) or 20px (body text)
150
+
-[ ] No full-screen linear dark gradients (use radial or solid + localized glow)
151
+
-[ ] Fonts loaded via `<link>` with `display=block`, not CSS `@import`
132
152
-[ ] 100% deterministic
133
153
-[ ] Each composition includes GSAP script tag
134
154
-[ ]`npx hyperframes lint` and `npx hyperframes validate` both pass
0 commit comments