frameyard 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/CHANGELOG.md +164 -7
  2. package/GLOSSARY.md +152 -18
  3. package/README.md +328 -285
  4. package/dist/assets/integrations.d.ts +8 -4
  5. package/dist/assets/integrations.d.ts.map +1 -1
  6. package/dist/assets/integrations.js +8 -4
  7. package/dist/assets/integrations.js.map +1 -1
  8. package/dist/assets/provenance.d.ts +2 -2
  9. package/dist/assets/provenance.d.ts.map +1 -1
  10. package/dist/assets/provenance.js +5 -3
  11. package/dist/assets/provenance.js.map +1 -1
  12. package/dist/assets/tts-supertonic.d.ts +28 -0
  13. package/dist/assets/tts-supertonic.d.ts.map +1 -0
  14. package/dist/assets/tts-supertonic.js +156 -0
  15. package/dist/assets/tts-supertonic.js.map +1 -0
  16. package/dist/assets/tts.d.ts +18 -6
  17. package/dist/assets/tts.d.ts.map +1 -1
  18. package/dist/assets/tts.js +219 -97
  19. package/dist/assets/tts.js.map +1 -1
  20. package/dist/cli.js +1705 -106
  21. package/dist/cli.js.map +1 -1
  22. package/dist/composition/descriptor.d.ts +17 -3
  23. package/dist/composition/descriptor.d.ts.map +1 -1
  24. package/dist/composition/descriptor.js +51 -28
  25. package/dist/composition/descriptor.js.map +1 -1
  26. package/dist/composition/format.d.ts +12 -0
  27. package/dist/composition/format.d.ts.map +1 -0
  28. package/dist/composition/format.js +28 -0
  29. package/dist/composition/format.js.map +1 -0
  30. package/dist/composition/index.d.ts +2 -1
  31. package/dist/composition/index.d.ts.map +1 -1
  32. package/dist/composition/index.js +1 -0
  33. package/dist/composition/index.js.map +1 -1
  34. package/dist/context.d.ts +82 -0
  35. package/dist/context.d.ts.map +1 -0
  36. package/dist/context.js +277 -0
  37. package/dist/context.js.map +1 -0
  38. package/dist/contracts.d.ts +80 -6
  39. package/dist/contracts.d.ts.map +1 -1
  40. package/dist/contracts.js +3 -0
  41. package/dist/contracts.js.map +1 -1
  42. package/dist/doctor.d.ts +14 -1
  43. package/dist/doctor.d.ts.map +1 -1
  44. package/dist/doctor.js +166 -7
  45. package/dist/doctor.js.map +1 -1
  46. package/dist/errors.d.ts +1 -1
  47. package/dist/errors.d.ts.map +1 -1
  48. package/dist/errors.js.map +1 -1
  49. package/dist/fonts/add.d.ts +41 -0
  50. package/dist/fonts/add.d.ts.map +1 -0
  51. package/dist/fonts/add.js +259 -0
  52. package/dist/fonts/add.js.map +1 -0
  53. package/dist/fonts/binary.d.ts +4 -0
  54. package/dist/fonts/binary.d.ts.map +1 -0
  55. package/dist/fonts/binary.js +21 -0
  56. package/dist/fonts/binary.js.map +1 -0
  57. package/dist/fonts/css.d.ts +4 -0
  58. package/dist/fonts/css.d.ts.map +1 -0
  59. package/dist/fonts/css.js +50 -0
  60. package/dist/fonts/css.js.map +1 -0
  61. package/dist/fonts/fontsource.d.ts +107 -0
  62. package/dist/fonts/fontsource.d.ts.map +1 -0
  63. package/dist/fonts/fontsource.js +565 -0
  64. package/dist/fonts/fontsource.js.map +1 -0
  65. package/dist/fonts/index.d.ts +11 -0
  66. package/dist/fonts/index.d.ts.map +1 -0
  67. package/dist/fonts/index.js +11 -0
  68. package/dist/fonts/index.js.map +1 -0
  69. package/dist/fonts/install.d.ts +24 -0
  70. package/dist/fonts/install.d.ts.map +1 -0
  71. package/dist/fonts/install.js +145 -0
  72. package/dist/fonts/install.js.map +1 -0
  73. package/dist/fonts/manifest.d.ts +21 -0
  74. package/dist/fonts/manifest.d.ts.map +1 -0
  75. package/dist/fonts/manifest.js +96 -0
  76. package/dist/fonts/manifest.js.map +1 -0
  77. package/dist/fonts/options.d.ts +9 -0
  78. package/dist/fonts/options.d.ts.map +1 -0
  79. package/dist/fonts/options.js +66 -0
  80. package/dist/fonts/options.js.map +1 -0
  81. package/dist/fonts/schema.d.ts +4 -0
  82. package/dist/fonts/schema.d.ts.map +1 -0
  83. package/dist/fonts/schema.js +316 -0
  84. package/dist/fonts/schema.js.map +1 -0
  85. package/dist/fonts/summary.d.ts +16 -0
  86. package/dist/fonts/summary.d.ts.map +1 -0
  87. package/dist/fonts/summary.js +22 -0
  88. package/dist/fonts/summary.js.map +1 -0
  89. package/dist/fonts/types.d.ts +56 -0
  90. package/dist/fonts/types.d.ts.map +1 -0
  91. package/dist/fonts/types.js +8 -0
  92. package/dist/fonts/types.js.map +1 -0
  93. package/dist/fonts/verify.d.ts +33 -0
  94. package/dist/fonts/verify.d.ts.map +1 -0
  95. package/dist/fonts/verify.js +200 -0
  96. package/dist/fonts/verify.js.map +1 -0
  97. package/dist/html/document-adapter.d.ts +3 -3
  98. package/dist/html/document-adapter.d.ts.map +1 -1
  99. package/dist/html/document-adapter.js +1 -1
  100. package/dist/html/document-adapter.js.map +1 -1
  101. package/dist/html/index.d.ts +1 -1
  102. package/dist/html/index.d.ts.map +1 -1
  103. package/dist/html/parse.d.ts +10 -2
  104. package/dist/html/parse.d.ts.map +1 -1
  105. package/dist/html/parse.js +14 -5
  106. package/dist/html/parse.js.map +1 -1
  107. package/dist/html/validate.d.ts +12 -1
  108. package/dist/html/validate.d.ts.map +1 -1
  109. package/dist/html/validate.js +292 -61
  110. package/dist/html/validate.js.map +1 -1
  111. package/dist/index.d.ts +4 -4
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -2
  114. package/dist/index.js.map +1 -1
  115. package/dist/inspect.d.ts +61 -0
  116. package/dist/inspect.d.ts.map +1 -0
  117. package/dist/inspect.js +325 -0
  118. package/dist/inspect.js.map +1 -0
  119. package/dist/lint.d.ts +63 -0
  120. package/dist/lint.d.ts.map +1 -0
  121. package/dist/lint.js +265 -0
  122. package/dist/lint.js.map +1 -0
  123. package/dist/policy.d.ts +69 -0
  124. package/dist/policy.d.ts.map +1 -0
  125. package/dist/policy.js +361 -0
  126. package/dist/policy.js.map +1 -0
  127. package/dist/preview/main.d.ts +2 -2
  128. package/dist/preview/main.d.ts.map +1 -1
  129. package/dist/preview/main.js +35 -14
  130. package/dist/preview/main.js.map +1 -1
  131. package/dist/preview/timeline-data.d.ts +17 -65
  132. package/dist/preview/timeline-data.d.ts.map +1 -1
  133. package/dist/preview/timeline-host.d.ts.map +1 -1
  134. package/dist/preview/timeline-host.js +73 -38
  135. package/dist/preview/timeline-host.js.map +1 -1
  136. package/dist/preview/timeline-main.d.ts.map +1 -1
  137. package/dist/preview/timeline-main.js +47 -3
  138. package/dist/preview/timeline-main.js.map +1 -1
  139. package/dist/project.d.ts +13 -8
  140. package/dist/project.d.ts.map +1 -1
  141. package/dist/project.js +20 -17
  142. package/dist/project.js.map +1 -1
  143. package/dist/renderer/browser.d.ts +2 -2
  144. package/dist/renderer/browser.d.ts.map +1 -1
  145. package/dist/renderer/browser.js +10 -4
  146. package/dist/renderer/browser.js.map +1 -1
  147. package/dist/renderer/ffmpeg.d.ts +44 -1
  148. package/dist/renderer/ffmpeg.d.ts.map +1 -1
  149. package/dist/renderer/ffmpeg.js +371 -73
  150. package/dist/renderer/ffmpeg.js.map +1 -1
  151. package/dist/renderer/index.d.ts +40 -4
  152. package/dist/renderer/index.d.ts.map +1 -1
  153. package/dist/renderer/index.js +100 -25
  154. package/dist/renderer/index.js.map +1 -1
  155. package/dist/renderer/keyframe-expressions.d.ts +39 -0
  156. package/dist/renderer/keyframe-expressions.d.ts.map +1 -0
  157. package/dist/renderer/keyframe-expressions.js +180 -0
  158. package/dist/renderer/keyframe-expressions.js.map +1 -0
  159. package/dist/renderer/timeline.d.ts +2 -0
  160. package/dist/renderer/timeline.d.ts.map +1 -1
  161. package/dist/renderer/timeline.js +168 -106
  162. package/dist/renderer/timeline.js.map +1 -1
  163. package/dist/runtime/host.d.ts.map +1 -1
  164. package/dist/runtime/host.js +159 -31
  165. package/dist/runtime/host.js.map +1 -1
  166. package/dist/scaffold.d.ts +3 -0
  167. package/dist/scaffold.d.ts.map +1 -1
  168. package/dist/scaffold.js +48 -317
  169. package/dist/scaffold.js.map +1 -1
  170. package/dist/server/composition-html.d.ts +15 -7
  171. package/dist/server/composition-html.d.ts.map +1 -1
  172. package/dist/server/composition-html.js +72 -16
  173. package/dist/server/composition-html.js.map +1 -1
  174. package/dist/server/server.d.ts.map +1 -1
  175. package/dist/server/server.js +56 -6
  176. package/dist/server/server.js.map +1 -1
  177. package/dist/server/timeline-preview-data.d.ts.map +1 -1
  178. package/dist/server/timeline-preview-data.js +12 -23
  179. package/dist/server/timeline-preview-data.js.map +1 -1
  180. package/dist/server/timeline-preview-html.d.ts.map +1 -1
  181. package/dist/server/timeline-preview-html.js +20 -3
  182. package/dist/server/timeline-preview-html.js.map +1 -1
  183. package/dist/templates.d.ts +16 -0
  184. package/dist/templates.d.ts.map +1 -0
  185. package/dist/templates.js +186 -0
  186. package/dist/templates.js.map +1 -0
  187. package/dist/timeline/easing.d.ts +36 -0
  188. package/dist/timeline/easing.d.ts.map +1 -0
  189. package/dist/timeline/easing.js +225 -0
  190. package/dist/timeline/easing.js.map +1 -0
  191. package/dist/timeline/keyframes.d.ts +23 -0
  192. package/dist/timeline/keyframes.d.ts.map +1 -0
  193. package/dist/timeline/keyframes.js +95 -0
  194. package/dist/timeline/keyframes.js.map +1 -0
  195. package/dist/timeline/plan.d.ts +6 -12
  196. package/dist/timeline/plan.d.ts.map +1 -1
  197. package/dist/timeline/plan.js +17 -30
  198. package/dist/timeline/plan.js.map +1 -1
  199. package/dist/timeline/resolve.d.ts +16 -3
  200. package/dist/timeline/resolve.d.ts.map +1 -1
  201. package/dist/timeline/resolve.js +119 -13
  202. package/dist/timeline/resolve.js.map +1 -1
  203. package/dist/timeline/scene.d.ts +126 -0
  204. package/dist/timeline/scene.d.ts.map +1 -0
  205. package/dist/timeline/scene.js +109 -0
  206. package/dist/timeline/scene.js.map +1 -0
  207. package/dist/timeline/schema.d.ts +38 -0
  208. package/dist/timeline/schema.d.ts.map +1 -1
  209. package/dist/timeline/schema.js +290 -9
  210. package/dist/timeline/schema.js.map +1 -1
  211. package/dist/timeline/transitions.d.ts +21 -0
  212. package/dist/timeline/transitions.d.ts.map +1 -0
  213. package/dist/timeline/transitions.js +23 -0
  214. package/dist/timeline/transitions.js.map +1 -0
  215. package/dist/version.d.ts +1 -1
  216. package/dist/version.js +1 -1
  217. package/docs/adr/0007-make-html-documents-the-composition-unit.md +2 -2
  218. package/docs/adr/0008-share-the-composition-document-grammar.md +1 -1
  219. package/docs/adr/0010-make-timelines-the-render-unit.md +3 -2
  220. package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +1 -1
  221. package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +1 -1
  222. package/docs/adr/0014-compose-timeline-clips-with-css-blend-modes.md +38 -0
  223. package/docs/adr/0015-inherit-composition-format-from-the-project.md +28 -0
  224. package/docs/adr/0016-author-compositions-as-html-fragments.md +32 -0
  225. package/docs/adr/0017-render-timeless-compositions-as-stills.md +99 -0
  226. package/docs/adr/0018-animate-timeline-clips-with-keyframes.md +35 -0
  227. package/docs/adr/0019-make-same-track-overlap-the-transition-window.md +36 -0
  228. package/docs/adr/0020-crossfade-audio-clips-on-audio-tracks.md +27 -0
  229. package/docs/adr/0021-project-timeline-scenes-once.md +32 -0
  230. package/docs/adr/0022-gate-heavy-operations-with-a-workspace-resource-policy.md +35 -0
  231. package/docs/adr/0023-pin-fonts-as-project-assets.md +98 -0
  232. package/docs/migration-0.4.md +5 -3
  233. package/docs/migration-0.8.md +163 -0
  234. package/docs/preview-testing.md +23 -3
  235. package/docs/{v0.1-design.md → v0.x-design.md} +86 -53
  236. package/examples/blend/README.md +106 -0
  237. package/examples/blend/assets/ink.mp4 +0 -0
  238. package/examples/blend/compositions/aurora.html +55 -0
  239. package/examples/blend/compositions/backdrop.html +74 -0
  240. package/examples/blend/compositions/labels.html +149 -0
  241. package/examples/blend/fy.config.js +11 -0
  242. package/examples/blend/scripts/make-ink.sh +26 -0
  243. package/examples/blend/timelines/blend.timeline.json +155 -0
  244. package/examples/demo/README.md +2 -2
  245. package/examples/demo/compositions/demo.html +243 -251
  246. package/examples/intro/README.md +71 -0
  247. package/examples/intro/assets/fonts/figtree/LICENSE.txt +93 -0
  248. package/examples/intro/assets/fonts/figtree/figtree-latin-wght-normal.woff2 +0 -0
  249. package/examples/intro/assets/fonts/instrument-serif/LICENSE.txt +93 -0
  250. package/examples/intro/assets/fonts/instrument-serif/instrument-serif-latin-400-italic.woff2 +0 -0
  251. package/examples/intro/assets/fonts/space-mono/LICENSE.txt +93 -0
  252. package/examples/intro/assets/fonts/space-mono/space-mono-latin-400-normal.woff2 +0 -0
  253. package/examples/intro/assets/fonts/space-mono/space-mono-latin-700-normal.woff2 +0 -0
  254. package/examples/intro/assets/grain.png +0 -0
  255. package/examples/intro/assets/score.ogg +0 -0
  256. package/examples/intro/compositions/brand.html +412 -0
  257. package/examples/intro/compositions/chrome.controller.js +63 -0
  258. package/examples/intro/compositions/chrome.html +256 -0
  259. package/examples/intro/compositions/lattice.controller.js +148 -0
  260. package/examples/intro/compositions/lattice.html +308 -0
  261. package/examples/intro/compositions/opening.html +504 -0
  262. package/examples/intro/compositions/studio.html +930 -0
  263. package/examples/intro/compositions/theme.css +218 -0
  264. package/examples/intro/fy.config.js +13 -0
  265. package/examples/intro/fy.fonts.json +100 -0
  266. package/examples/intro/scripts/make-grain.mjs +79 -0
  267. package/examples/intro/scripts/make-score.mjs +702 -0
  268. package/examples/intro/timelines/intro.timeline.json +60 -0
  269. package/examples/keyframes/README.md +76 -0
  270. package/examples/keyframes/compositions/backdrop.html +60 -0
  271. package/examples/keyframes/compositions/card.html +99 -0
  272. package/examples/keyframes/fy.config.js +10 -0
  273. package/examples/keyframes/timelines/keyframes.timeline.json +60 -0
  274. package/examples/nested/README.md +112 -0
  275. package/examples/nested/assets/pulse.wav +0 -0
  276. package/examples/nested/compositions/backdrop.html +84 -0
  277. package/examples/nested/compositions/card.html +275 -0
  278. package/examples/nested/compositions/overlay.html +307 -0
  279. package/examples/nested/fy.config.js +13 -0
  280. package/examples/nested/scripts/make-pulse.sh +20 -0
  281. package/examples/nested/timelines/chapter-a.timeline.json +39 -0
  282. package/examples/nested/timelines/chapter-b.timeline.json +39 -0
  283. package/examples/nested/timelines/nested-reel.timeline.json +52 -0
  284. package/examples/static/README.md +17 -0
  285. package/examples/static/compositions/still.html +37 -0
  286. package/examples/static/fy.config.js +9 -0
  287. package/examples/static/timelines/still.timeline.json +13 -0
  288. package/examples/transitions/README.md +128 -0
  289. package/examples/transitions/assets/alpha.wav +0 -0
  290. package/examples/transitions/assets/beta.wav +0 -0
  291. package/examples/transitions/assets/gamma.wav +0 -0
  292. package/examples/transitions/compositions/alpha.html +56 -0
  293. package/examples/transitions/compositions/beta.html +56 -0
  294. package/examples/transitions/compositions/gamma.html +56 -0
  295. package/examples/transitions/fy.config.js +12 -0
  296. package/examples/transitions/timelines/audio-crossfade.timeline.json +44 -0
  297. package/examples/transitions/timelines/transitions.timeline.json +31 -0
  298. package/examples/video/README.md +4 -4
  299. package/examples/video/compositions/video-mp4.html +349 -357
  300. package/examples/video/compositions/video-overlay.html +337 -345
  301. package/examples/video/compositions/video.html +349 -357
  302. package/examples/webgl/README.md +1 -1
  303. package/examples/webgl/compositions/webgl.html +205 -213
  304. package/package.json +13 -5
  305. package/scaffolds/init/AGENTS.md +131 -0
  306. package/scaffolds/init/README.md +56 -0
  307. package/scaffolds/init/compositions/hello.html +79 -0
  308. package/scaffolds/init/fy.config.js +9 -0
  309. package/scaffolds/init/gitignore.template +1 -0
  310. package/scaffolds/init/package.json +11 -0
  311. package/scaffolds/init/props/hello.json +4 -0
  312. package/scaffolds/init/props/main.json +6 -0
  313. package/scaffolds/init/timelines/main.timeline.json +20 -0
  314. package/scaffolds/local-models/LOCAL-MODELS.md +77 -0
  315. package/scaffolds/local-models/fy.policy.json +5 -0
  316. package/scaffolds/local-models/opencode/opencode.jsonc +17 -0
  317. package/scaffolds/local-models/pi/APPEND_SYSTEM.md +21 -0
  318. package/scaffolds/local-models/pi/sandbox.json +15 -0
  319. package/scaffolds/local-models/pi/settings.json +15 -0
  320. package/skills/frameyard/README.md +1 -0
  321. package/skills/frameyard/SKILL.md +59 -59
  322. package/skills/frameyard/references/audio-assets.md +32 -3
  323. package/skills/frameyard/references/common-mistakes.md +81 -7
  324. package/skills/frameyard/references/controllers.md +5 -4
  325. package/skills/frameyard/references/deterministic-authoring.md +49 -37
  326. package/skills/frameyard/references/diagnostics.md +42 -4
  327. package/skills/frameyard/references/failure-recovery.md +56 -10
  328. package/skills/frameyard/references/fonts.md +132 -0
  329. package/skills/frameyard/references/json-results.md +165 -10
  330. package/skills/frameyard/references/quick-reference.md +222 -0
  331. package/skills/frameyard/references/timelines.md +109 -15
  332. /package/examples/demo/{video.config.js → fy.config.js} +0 -0
  333. /package/examples/video/{video.config.js → fy.config.js} +0 -0
  334. /package/examples/webgl/{video.config.js → fy.config.js} +0 -0
@@ -0,0 +1,106 @@
1
+ # Frameyard blend example
2
+
3
+ A 12-second, 1920×1080, 30 fps timeline that demonstrates every supported CSS
4
+ blend mode on composition and video clips. Four video tracks build one frame:
5
+
6
+ - `v1` **backdrop** — a static opaque HTML composition with a grid, concentric
7
+ rings, and a dark vignette. It omits the time metadata and all dimensions, so
8
+ it inherits the project format and is captured once at the timeline's size.
9
+ - `v2` **aurora** — a static transparent HTML composition with three blurred
10
+ color blobs. It declares its dimensions but no time metadata, so it is also
11
+ captured once. Its clip uses `blend: "screen"` for the full 12 seconds.
12
+ - `v3` **ink** — a local 1280×720 H.264 clip scaled to fill the project frame.
13
+ Twelve touching clips play one second each, cycling through all 12 supported
14
+ modes in timeline order.
15
+ - `v4` **labels** — an animated transparent HTML composition in normal mode. It
16
+ lists the modes and their time ranges, and draws a progress bar from the
17
+ `--fy-progress` frame-state variable, so it is evaluated per frame.
18
+
19
+ The mode sequence on `v3` is:
20
+
21
+ | Time | Mode |
22
+ | --- | --- |
23
+ | 0–1 s | `normal` |
24
+ | 1–2 s | `multiply` |
25
+ | 2–3 s | `screen` |
26
+ | 3–4 s | `overlay` |
27
+ | 4–5 s | `darken` |
28
+ | 5–6 s | `lighten` |
29
+ | 6–7 s | `color-dodge` |
30
+ | 7–8 s | `color-burn` |
31
+ | 8–9 s | `hard-light` |
32
+ | 9–10 s | `soft-light` |
33
+ | 10–11 s | `difference` |
34
+ | 11–12 s | `exclusion` |
35
+
36
+ The aurora is transparent, so its `screen` blends only where its blobs have
37
+ alpha. The video is opaque, so its whole scaled frame participates in the blend.
38
+ The labels layer stays in `normal` mode so the copy remains readable.
39
+
40
+ Blend modes are static per clip. The example uses consecutive clips on `v3` to
41
+ show every mode in one timeline; it does not animate `blend` itself. `hue`,
42
+ `saturation`, `color`, and `luminosity` are rejected because they need
43
+ cross-channel math and are not part of the supported set.
44
+
45
+ `backdrop` and `aurora` are static compositions: they declare no
46
+ `duration-in-frames` or `fps`, are captured once, and the same image is reused
47
+ for every timeline frame. `labels` remains animated because its progress bar
48
+ reads `--fy-progress`; making it static would freeze the bar.
49
+
50
+ Preview composites with Chromium `mix-blend-mode`; production uses FFmpeg's
51
+ `blend` filter plus an alpha mask. The preview stage stacks by track order and
52
+ is isolated from the preview chrome. Small scaling, alpha, and color differences
53
+ between preview and the final encode are expected.
54
+
55
+ ## Files
56
+
57
+ ```text
58
+ examples/blend/
59
+ compositions/backdrop.html opaque base composition
60
+ compositions/aurora.html transparent screen layer
61
+ compositions/labels.html transparent mode list and progress layer
62
+ timelines/blend.timeline.json
63
+ assets/ink.mp4 procedural 12-second source clip
64
+ scripts/make-ink.sh regenerate assets/ink.mp4
65
+ fy.config.js
66
+ ```
67
+
68
+ The timeline lives in `timelines/blend.timeline.json`; the project format and
69
+ entries are fixed in `fy.config.js`. `backdrop.html` is a static composition
70
+ that omits all dimensions and time metadata, so it inherits the project's
71
+ 1920×1080 size. `aurora.html` is a static composition that declares its
72
+ dimensions but no time metadata. `labels.html` is an animated composition and
73
+ keeps its `fps` and `duration-in-frames`.
74
+
75
+ ## Commands
76
+
77
+ From the repository root:
78
+
79
+ ```sh
80
+ pnpm build
81
+ node dist/cli.js check blend-timeline --project examples/blend
82
+ node dist/cli.js still blend-timeline --frame 15,45,75,105,135,165,195,225,255,285,315,345 --project examples/blend --out blend-stills
83
+ node dist/cli.js still blend-timeline --frame 285 --project examples/blend --out blend-soft-light.png
84
+ FRAMEYARD_POLICY=full node dist/cli.js render blend-timeline --project examples/blend
85
+ node dist/cli.js dev --project examples/blend
86
+ ```
87
+
88
+ The batch still captures one representative frame per mode into `blend-stills/`
89
+ using the same clip capture and composite path as the render. `render` writes
90
+ `out/blend-timeline.mp4` inside the project by default; use `--out <file.mp4>`
91
+ to choose another path. `dev` lists the compositions and the timeline in the
92
+ preview.
93
+
94
+ ## Source clip
95
+
96
+ `assets/ink.mp4` is a 12-second, 1280×720, 30 fps rotating gradient with a
97
+ silent AAC track, generated by FFmpeg. Regenerate it with:
98
+
99
+ ```sh
100
+ bash examples/blend/scripts/make-ink.sh
101
+ ```
102
+
103
+ Pass an output path as the first argument to write elsewhere. Replace
104
+ `assets/ink.mp4` with any local, seekable MP4 or WebM that is at least 12 seconds
105
+ long; remote URLs and live streams are not supported. If you use a different
106
+ length, update the clip durations and source in-points in the timeline.
Binary file
@@ -0,0 +1,55 @@
1
+ <style>
2
+ html,
3
+ body,
4
+ fy-composition {
5
+ margin: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ background: transparent;
9
+ }
10
+ fy-composition {
11
+ display: block;
12
+ overflow: hidden;
13
+ }
14
+ .scene {
15
+ position: relative;
16
+ width: 100%;
17
+ height: 100%;
18
+ overflow: hidden;
19
+ isolation: isolate;
20
+ }
21
+ .blob {
22
+ position: absolute;
23
+ border-radius: 50%;
24
+ filter: blur(78px);
25
+ opacity: 0.9;
26
+ }
27
+ .pink {
28
+ left: 6%;
29
+ top: -18%;
30
+ width: 980px;
31
+ height: 980px;
32
+ background: radial-gradient(circle, rgba(255, 64, 164, 0.92), rgba(255, 64, 164, 0) 68%);
33
+ }
34
+ .blue {
35
+ right: -12%;
36
+ top: 4%;
37
+ width: 1180px;
38
+ height: 1180px;
39
+ background: radial-gradient(circle, rgba(46, 146, 255, 0.88), rgba(46, 146, 255, 0) 68%);
40
+ }
41
+ .amber {
42
+ left: 30%;
43
+ bottom: -30%;
44
+ width: 900px;
45
+ height: 900px;
46
+ background: radial-gradient(circle, rgba(255, 186, 72, 0.7), rgba(255, 186, 72, 0) 70%);
47
+ }
48
+ </style>
49
+ <fy-composition width="1920" height="1080">
50
+ <div class="scene">
51
+ <div class="blob pink"></div>
52
+ <div class="blob blue"></div>
53
+ <div class="blob amber"></div>
54
+ </div>
55
+ </fy-composition>
@@ -0,0 +1,74 @@
1
+ <style>
2
+ html,
3
+ body {
4
+ margin: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ background: #05070d;
8
+ }
9
+ fy-composition {
10
+ display: block;
11
+ width: 100%;
12
+ height: 100%;
13
+ overflow: hidden;
14
+ }
15
+ .scene {
16
+ position: relative;
17
+ width: 100%;
18
+ height: 100%;
19
+ overflow: hidden;
20
+ background:
21
+ radial-gradient(1200px 760px at 72% 34%, rgba(42, 104, 220, 0.5), transparent 62%),
22
+ radial-gradient(860px 620px at 18% 82%, rgba(255, 76, 140, 0.14), transparent 64%),
23
+ linear-gradient(145deg, #060a12 0%, #0b1424 52%, #05070d 100%);
24
+ }
25
+ .grid {
26
+ position: absolute;
27
+ inset: -1px;
28
+ background-image:
29
+ linear-gradient(rgba(150, 190, 255, 0.075) 1px, transparent 1px),
30
+ linear-gradient(90deg, rgba(150, 190, 255, 0.075) 1px, transparent 1px);
31
+ background-size: 72px 72px;
32
+ }
33
+ .fade {
34
+ position: absolute;
35
+ inset: 0;
36
+ background:
37
+ radial-gradient(ellipse at 58% 38%, transparent 18%, rgba(5, 7, 13, 0.55) 62%, rgba(5, 7, 13, 0.92) 100%),
38
+ linear-gradient(90deg, rgba(5, 7, 13, 0.5), transparent 38%, transparent 74%, rgba(5, 7, 13, 0.55));
39
+ }
40
+ .ring {
41
+ position: absolute;
42
+ left: 58%;
43
+ top: 44%;
44
+ width: 1060px;
45
+ height: 1060px;
46
+ border: 1px solid rgba(160, 205, 255, 0.18);
47
+ border-radius: 50%;
48
+ box-shadow:
49
+ 0 0 140px rgba(54, 118, 255, 0.16) inset,
50
+ 0 0 90px rgba(54, 118, 255, 0.08);
51
+ transform: translate(-50%, -50%);
52
+ }
53
+ .ring::before,
54
+ .ring::after {
55
+ content: "";
56
+ position: absolute;
57
+ border: 1px solid rgba(255, 118, 190, 0.13);
58
+ border-radius: 50%;
59
+ }
60
+ .ring::before {
61
+ inset: 138px;
62
+ }
63
+ .ring::after {
64
+ inset: 286px;
65
+ border-color: rgba(255, 179, 71, 0.12);
66
+ }
67
+ </style>
68
+ <fy-composition>
69
+ <div class="scene">
70
+ <div class="grid"></div>
71
+ <div class="ring"></div>
72
+ <div class="fade"></div>
73
+ </div>
74
+ </fy-composition>
@@ -0,0 +1,149 @@
1
+ <style>
2
+ html,
3
+ body,
4
+ fy-composition {
5
+ margin: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ background: transparent;
9
+ }
10
+ fy-composition {
11
+ display: block;
12
+ overflow: hidden;
13
+ }
14
+ .scene {
15
+ position: relative;
16
+ width: 100%;
17
+ height: 100%;
18
+ overflow: hidden;
19
+ color: #eef4ff;
20
+ font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
21
+ }
22
+ .panel {
23
+ position: absolute;
24
+ left: 92px;
25
+ top: 96px;
26
+ width: 920px;
27
+ padding: 44px 46px 40px;
28
+ border: 1px solid rgba(160, 205, 255, 0.24);
29
+ border-radius: 22px;
30
+ background: linear-gradient(135deg, rgba(6, 12, 24, 0.91), rgba(6, 12, 24, 0.64));
31
+ box-shadow: 0 38px 110px rgba(0, 0, 0, 0.38);
32
+ }
33
+ .kicker {
34
+ color: #7fd6ff;
35
+ font-family: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
36
+ font-size: 13px;
37
+ font-weight: 650;
38
+ letter-spacing: 0.22em;
39
+ text-transform: uppercase;
40
+ }
41
+ h1 {
42
+ margin: 18px 0 14px;
43
+ font-size: 78px;
44
+ font-weight: 680;
45
+ letter-spacing: -0.045em;
46
+ line-height: 0.95;
47
+ }
48
+ h1 span {
49
+ color: #ffb347;
50
+ }
51
+ .subtitle {
52
+ margin: 0;
53
+ max-width: 780px;
54
+ color: #a9b8d0;
55
+ font-size: 20px;
56
+ line-height: 1.4;
57
+ }
58
+ .modes {
59
+ display: grid;
60
+ grid-template-columns: repeat(4, minmax(0, 1fr));
61
+ gap: 14px 18px;
62
+ margin-top: 28px;
63
+ }
64
+ .modes div {
65
+ padding-top: 9px;
66
+ border-top: 2px solid rgba(127, 214, 255, 0.5);
67
+ }
68
+ .modes div:nth-child(4n + 2) {
69
+ border-top-color: rgba(255, 179, 71, 0.5);
70
+ }
71
+ .modes div:nth-child(4n + 3) {
72
+ border-top-color: rgba(255, 100, 180, 0.5);
73
+ }
74
+ .modes div:nth-child(4n + 4) {
75
+ border-top-color: rgba(160, 190, 255, 0.5);
76
+ }
77
+ .modes b {
78
+ display: block;
79
+ margin-bottom: 5px;
80
+ color: #eef4ff;
81
+ font-family: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
82
+ font-size: 15px;
83
+ font-weight: 600;
84
+ letter-spacing: -0.01em;
85
+ }
86
+ .modes span {
87
+ display: block;
88
+ color: #7f90a8;
89
+ font-family: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
90
+ font-size: 11px;
91
+ }
92
+ .foot {
93
+ margin-top: 26px;
94
+ color: #73839a;
95
+ font-family: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
96
+ font-size: 12px;
97
+ line-height: 1.55;
98
+ letter-spacing: 0.03em;
99
+ }
100
+ .progress {
101
+ position: absolute;
102
+ left: 142px;
103
+ bottom: 92px;
104
+ width: 820px;
105
+ height: 3px;
106
+ overflow: hidden;
107
+ border-radius: 99px;
108
+ background: rgba(220, 235, 255, 0.16);
109
+ }
110
+ .progress::after {
111
+ content: "";
112
+ display: block;
113
+ width: calc(var(--fy-progress) * 100%);
114
+ height: 100%;
115
+ border-radius: inherit;
116
+ background: linear-gradient(90deg, #7fd6ff, #ffb347);
117
+ }
118
+ </style>
119
+ <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
120
+ <div class="scene">
121
+ <div class="panel">
122
+ <div class="kicker">Frameyard / Blend lab</div>
123
+ <h1>12 modes.<br /><span>One timeline.</span></h1>
124
+ <p class="subtitle">
125
+ A composition stays on screen while the video clip cycles through every supported CSS blend mode, one second
126
+ each.
127
+ </p>
128
+ <div class="modes">
129
+ <div><b>normal</b><span>0–1s</span></div>
130
+ <div><b>multiply</b><span>1–2s</span></div>
131
+ <div><b>screen</b><span>2–3s</span></div>
132
+ <div><b>overlay</b><span>3–4s</span></div>
133
+ <div><b>darken</b><span>4–5s</span></div>
134
+ <div><b>lighten</b><span>5–6s</span></div>
135
+ <div><b>color-dodge</b><span>6–7s</span></div>
136
+ <div><b>color-burn</b><span>7–8s</span></div>
137
+ <div><b>hard-light</b><span>8–9s</span></div>
138
+ <div><b>soft-light</b><span>9–10s</span></div>
139
+ <div><b>difference</b><span>10–11s</span></div>
140
+ <div><b>exclusion</b><span>11–12s</span></div>
141
+ </div>
142
+ <div class="foot">
143
+ v2 · composition · screen · 0–12s<br />
144
+ v3 · video · 12 modes · 1s each · Preview uses CSS; render uses FFmpeg + alpha mask
145
+ </div>
146
+ </div>
147
+ <div class="progress"></div>
148
+ </div>
149
+ </fy-composition>
@@ -0,0 +1,11 @@
1
+ export default {
2
+ format: { width: 1920, height: 1080, fps: 30 },
3
+ compositions: {
4
+ backdrop: "./compositions/backdrop.html",
5
+ aurora: "./compositions/aurora.html",
6
+ labels: "./compositions/labels.html",
7
+ },
8
+ timelines: {
9
+ "blend-timeline": "./timelines/blend.timeline.json",
10
+ },
11
+ };
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ out="${1:-$root/assets/ink.mp4}"
6
+ mkdir -p "$(dirname "$out")"
7
+
8
+ ffmpeg -y -v error \
9
+ -f lavfi \
10
+ -i "gradients=size=1280x720:rate=30:c0=0x0b1020:c1=0x1b4dff:c2=0xff2d7a:c3=0xffb347:n=4:type=spiral:speed=0.08:duration=12" \
11
+ -f lavfi \
12
+ -i "anullsrc=channel_layout=stereo:sample_rate=48000" \
13
+ -t 12 \
14
+ -map 0:v:0 \
15
+ -map 1:a:0 \
16
+ -c:v libx264 \
17
+ -preset medium \
18
+ -crf 20 \
19
+ -pix_fmt yuv420p \
20
+ -g 30 \
21
+ -c:a aac \
22
+ -b:a 96k \
23
+ -shortest \
24
+ "$out"
25
+
26
+ printf 'Wrote %s\n' "$out"
@@ -0,0 +1,155 @@
1
+ {
2
+ "version": 1,
3
+ "name": "Blend lab: every supported mode on a video clip",
4
+ "background": "#05070d",
5
+ "tracks": [
6
+ { "id": "v1", "kind": "video", "name": "Backdrop" },
7
+ { "id": "v2", "kind": "video", "name": "Aurora · screen" },
8
+ { "id": "v3", "kind": "video", "name": "Video · 12 modes, one per second" },
9
+ { "id": "v4", "kind": "video", "name": "Labels" }
10
+ ],
11
+ "clips": [
12
+ {
13
+ "id": "backdrop",
14
+ "track": "v1",
15
+ "source": { "kind": "composition", "id": "backdrop" },
16
+ "start": 0,
17
+ "duration": 360
18
+ },
19
+ {
20
+ "id": "aurora",
21
+ "track": "v2",
22
+ "source": { "kind": "composition", "id": "aurora" },
23
+ "start": 0,
24
+ "duration": 360,
25
+ "blend": "screen"
26
+ },
27
+ {
28
+ "id": "ink-normal",
29
+ "track": "v3",
30
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
31
+ "start": 0,
32
+ "duration": 30,
33
+ "sourceIn": 0,
34
+ "blend": "normal",
35
+ "audio": { "muted": true }
36
+ },
37
+ {
38
+ "id": "ink-multiply",
39
+ "track": "v3",
40
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
41
+ "start": 30,
42
+ "duration": 30,
43
+ "sourceIn": 30,
44
+ "blend": "multiply",
45
+ "audio": { "muted": true }
46
+ },
47
+ {
48
+ "id": "ink-screen",
49
+ "track": "v3",
50
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
51
+ "start": 60,
52
+ "duration": 30,
53
+ "sourceIn": 60,
54
+ "blend": "screen",
55
+ "audio": { "muted": true }
56
+ },
57
+ {
58
+ "id": "ink-overlay",
59
+ "track": "v3",
60
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
61
+ "start": 90,
62
+ "duration": 30,
63
+ "sourceIn": 90,
64
+ "blend": "overlay",
65
+ "audio": { "muted": true }
66
+ },
67
+ {
68
+ "id": "ink-darken",
69
+ "track": "v3",
70
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
71
+ "start": 120,
72
+ "duration": 30,
73
+ "sourceIn": 120,
74
+ "blend": "darken",
75
+ "audio": { "muted": true }
76
+ },
77
+ {
78
+ "id": "ink-lighten",
79
+ "track": "v3",
80
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
81
+ "start": 150,
82
+ "duration": 30,
83
+ "sourceIn": 150,
84
+ "blend": "lighten",
85
+ "audio": { "muted": true }
86
+ },
87
+ {
88
+ "id": "ink-color-dodge",
89
+ "track": "v3",
90
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
91
+ "start": 180,
92
+ "duration": 30,
93
+ "sourceIn": 180,
94
+ "blend": "color-dodge",
95
+ "audio": { "muted": true }
96
+ },
97
+ {
98
+ "id": "ink-color-burn",
99
+ "track": "v3",
100
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
101
+ "start": 210,
102
+ "duration": 30,
103
+ "sourceIn": 210,
104
+ "blend": "color-burn",
105
+ "audio": { "muted": true }
106
+ },
107
+ {
108
+ "id": "ink-hard-light",
109
+ "track": "v3",
110
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
111
+ "start": 240,
112
+ "duration": 30,
113
+ "sourceIn": 240,
114
+ "blend": "hard-light",
115
+ "audio": { "muted": true }
116
+ },
117
+ {
118
+ "id": "ink-soft-light",
119
+ "track": "v3",
120
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
121
+ "start": 270,
122
+ "duration": 30,
123
+ "sourceIn": 270,
124
+ "blend": "soft-light",
125
+ "audio": { "muted": true }
126
+ },
127
+ {
128
+ "id": "ink-difference",
129
+ "track": "v3",
130
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
131
+ "start": 300,
132
+ "duration": 30,
133
+ "sourceIn": 300,
134
+ "blend": "difference",
135
+ "audio": { "muted": true }
136
+ },
137
+ {
138
+ "id": "ink-exclusion",
139
+ "track": "v3",
140
+ "source": { "kind": "video", "src": "assets/ink.mp4" },
141
+ "start": 330,
142
+ "duration": 30,
143
+ "sourceIn": 330,
144
+ "blend": "exclusion",
145
+ "audio": { "muted": true }
146
+ },
147
+ {
148
+ "id": "labels",
149
+ "track": "v4",
150
+ "source": { "kind": "composition", "id": "labels" },
151
+ "start": 0,
152
+ "duration": 360
153
+ }
154
+ ]
155
+ }
@@ -19,13 +19,13 @@ From the repository root:
19
19
  pnpm build
20
20
  node dist/cli.js check demo-timeline --project examples/demo
21
21
  node dist/cli.js still demo-timeline --frame 240 --project examples/demo --out demo-still.png
22
- node dist/cli.js render demo-timeline --project examples/demo --props examples/demo/props/demo.json
22
+ FRAMEYARD_POLICY=full node dist/cli.js render demo-timeline --project examples/demo --props examples/demo/props/demo.json
23
23
  ```
24
24
 
25
25
  The composition remains renderable on its own:
26
26
 
27
27
  ```sh
28
- node dist/cli.js render demo --project examples/demo --props examples/demo/props/demo.json
28
+ FRAMEYARD_POLICY=full node dist/cli.js render demo --project examples/demo --props examples/demo/props/demo.json
29
29
  ```
30
30
 
31
31
  Start the interactive preview with: