@amg555/frameforge-mcp 2.7.3 → 2.7.7

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 (565) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/AGENTS.md +33 -0
  3. package/CHANGELOG.md +49 -0
  4. package/CLAUDE.md +17 -6
  5. package/GEMINI.md +63 -0
  6. package/README.md +216 -426
  7. package/START_HERE.md +4 -2
  8. package/package.json +5 -3
  9. package/plugins/ai-video-editor/README.md +32 -0
  10. package/plugins/ai-video-editor/SKILL.md +152 -0
  11. package/plugins/ai-video-editor/assets/fonts/fonts.css +2409 -0
  12. package/plugins/ai-video-editor/assets/fonts/ttf/anton-400.ttf +0 -0
  13. package/plugins/ai-video-editor/assets/fonts/ttf/archivo-800.ttf +0 -0
  14. package/plugins/ai-video-editor/assets/fonts/ttf/cabin-sketch-700.ttf +0 -0
  15. package/plugins/ai-video-editor/assets/fonts/ttf/dm-sans-700.ttf +0 -0
  16. package/plugins/ai-video-editor/assets/fonts/ttf/fraunces-600.ttf +0 -0
  17. package/plugins/ai-video-editor/assets/fonts/ttf/fraunces-700.ttf +0 -0
  18. package/plugins/ai-video-editor/assets/fonts/ttf/fredoka-700.ttf +0 -0
  19. package/plugins/ai-video-editor/assets/fonts/ttf/inter-700.ttf +0 -0
  20. package/plugins/ai-video-editor/assets/fonts/ttf/inter-800.ttf +0 -0
  21. package/plugins/ai-video-editor/assets/fonts/ttf/inter-tight-800.ttf +0 -0
  22. package/plugins/ai-video-editor/assets/fonts/ttf/kalam-700.ttf +0 -0
  23. package/plugins/ai-video-editor/assets/fonts/ttf/noto-sans-devanagari-700.ttf +0 -0
  24. package/plugins/ai-video-editor/assets/fonts/web/noto-sans-devanagari-devanagari-400-normal.woff2 +0 -0
  25. package/plugins/ai-video-editor/assets/fonts/web/noto-sans-devanagari-devanagari-500-normal.woff2 +0 -0
  26. package/plugins/ai-video-editor/assets/fonts/web/noto-sans-devanagari-devanagari-600-normal.woff2 +0 -0
  27. package/plugins/ai-video-editor/assets/fonts/web/noto-sans-devanagari-devanagari-700-normal.woff2 +0 -0
  28. package/plugins/ai-video-editor/assets/fonts/web/noto-sans-devanagari-devanagari-800-normal.woff2 +0 -0
  29. package/plugins/ai-video-editor/engine/hidpi.js +117 -0
  30. package/plugins/ai-video-editor/engine/page.html +43 -0
  31. package/plugins/ai-video-editor/engine/player.js +145 -0
  32. package/plugins/ai-video-editor/references/example-story-warm-canvas.js +95 -0
  33. package/plugins/ai-video-editor/references/story-guide.md +112 -0
  34. package/plugins/ai-video-editor/requirements.txt +4 -0
  35. package/plugins/ai-video-editor/styles/chalk-talk/base.js +490 -0
  36. package/plugins/ai-video-editor/styles/chalk-talk/style.json +14 -0
  37. package/plugins/ai-video-editor/styles/chalk-talk/style.md +41 -0
  38. package/plugins/ai-video-editor/styles/clean-tech/base.js +447 -0
  39. package/plugins/ai-video-editor/styles/clean-tech/style.json +15 -0
  40. package/plugins/ai-video-editor/styles/clean-tech/style.md +38 -0
  41. package/plugins/ai-video-editor/styles/creator-motion-graphics/base.js +698 -0
  42. package/plugins/ai-video-editor/styles/creator-motion-graphics/style.json +16 -0
  43. package/plugins/ai-video-editor/styles/creator-motion-graphics/style.md +39 -0
  44. package/plugins/ai-video-editor/styles/data-story/base.js +432 -0
  45. package/plugins/ai-video-editor/styles/data-story/style.json +14 -0
  46. package/plugins/ai-video-editor/styles/data-story/style.md +38 -0
  47. package/plugins/ai-video-editor/styles/index.json +103 -0
  48. package/plugins/ai-video-editor/styles/pop-info/base.js +570 -0
  49. package/plugins/ai-video-editor/styles/pop-info/style.json +13 -0
  50. package/plugins/ai-video-editor/styles/pop-info/style.md +37 -0
  51. package/plugins/ai-video-editor/styles/software-walkthrough/base.js +544 -0
  52. package/plugins/ai-video-editor/styles/software-walkthrough/style.json +15 -0
  53. package/plugins/ai-video-editor/styles/software-walkthrough/style.md +38 -0
  54. package/plugins/ai-video-editor/styles/warm-canvas/base.js +610 -0
  55. package/plugins/ai-video-editor/styles/warm-canvas/style.json +14 -0
  56. package/plugins/ai-video-editor/styles/warm-canvas/style.md +37 -0
  57. package/plugins/aiinsight-shorts/CHANGELOG.md +6 -0
  58. package/plugins/aiinsight-shorts/README.md +43 -0
  59. package/plugins/aiinsight-shorts/SKILL.md +138 -0
  60. package/plugins/aiinsight-shorts/styles/deep-dive/STYLE.md +58 -0
  61. package/plugins/aiinsight-shorts/styles/deep-dive/example/data/cues.json +140 -0
  62. package/plugins/aiinsight-shorts/styles/deep-dive/example/data/lines.json +67 -0
  63. package/plugins/aiinsight-shorts/styles/deep-dive/example/data/words.json +614 -0
  64. package/plugins/aiinsight-shorts/styles/deep-dive/example/data/words.txt +102 -0
  65. package/plugins/aiinsight-shorts/styles/deep-dive/example/fonts/Inter-Black.otf +0 -0
  66. package/plugins/aiinsight-shorts/styles/deep-dive/example/fonts/Inter-ExtraBold.otf +0 -0
  67. package/plugins/aiinsight-shorts/styles/deep-dive/example/index.html +17 -0
  68. package/plugins/aiinsight-shorts/styles/deep-dive/example/script.json +27 -0
  69. package/plugins/aiinsight-shorts/styles/deep-dive/example/sheet.html +11 -0
  70. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/board.js +62 -0
  71. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/captions.js +68 -0
  72. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/engine.js +65 -0
  73. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/human.js +128 -0
  74. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/lib.js +44 -0
  75. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/look2d.js +33 -0
  76. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/sdf.js +79 -0
  77. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/sets.js +107 -0
  78. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/core/timing.js +23 -0
  79. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/main.js +56 -0
  80. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/sheet.js +79 -0
  81. package/plugins/aiinsight-shorts/styles/deep-dive/example/src/shots.js +136 -0
  82. package/plugins/aiinsight-shorts/styles/deep-dive/example/tools/capture.mjs +122 -0
  83. package/plugins/aiinsight-shorts/styles/deep-dive/example/tools/mix.py +172 -0
  84. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/LICENSE-three.txt +21 -0
  85. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  86. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  87. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  88. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  89. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  90. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/Pass.js +95 -0
  91. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  92. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  93. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  94. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/shaders/BokehShader.js +145 -0
  95. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/shaders/CopyShader.js +45 -0
  96. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  97. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/addons/shaders/OutputShader.js +85 -0
  98. package/plugins/aiinsight-shorts/styles/deep-dive/example/vendor/three.module.js +6 -0
  99. package/plugins/aiinsight-shorts/styles/deep-dive/style-sheet.png +0 -0
  100. package/plugins/aiinsight-shorts/styles/inside-out/STYLE.md +97 -0
  101. package/plugins/aiinsight-shorts/styles/inside-out/example/data/cues.json +178 -0
  102. package/plugins/aiinsight-shorts/styles/inside-out/example/data/lines.json +73 -0
  103. package/plugins/aiinsight-shorts/styles/inside-out/example/data/words.json +662 -0
  104. package/plugins/aiinsight-shorts/styles/inside-out/example/fonts/Inter-Black.otf +0 -0
  105. package/plugins/aiinsight-shorts/styles/inside-out/example/index.html +17 -0
  106. package/plugins/aiinsight-shorts/styles/inside-out/example/script.json +26 -0
  107. package/plugins/aiinsight-shorts/styles/inside-out/example/src/core/captions.js +68 -0
  108. package/plugins/aiinsight-shorts/styles/inside-out/example/src/core/kit.js +345 -0
  109. package/plugins/aiinsight-shorts/styles/inside-out/example/src/core/lib.js +44 -0
  110. package/plugins/aiinsight-shorts/styles/inside-out/example/src/core/timing.js +23 -0
  111. package/plugins/aiinsight-shorts/styles/inside-out/example/src/main.js +62 -0
  112. package/plugins/aiinsight-shorts/styles/inside-out/example/src/shots.js +195 -0
  113. package/plugins/aiinsight-shorts/styles/inside-out/example/tools/capture.mjs +122 -0
  114. package/plugins/aiinsight-shorts/styles/inside-out/example/tools/mix.py +172 -0
  115. package/plugins/aiinsight-shorts/styles/inside-out/style-sheet.png +0 -0
  116. package/plugins/aiinsight-shorts/styles/night-file/STYLE.md +36 -0
  117. package/plugins/aiinsight-shorts/styles/night-file/example/data/cues.json +146 -0
  118. package/plugins/aiinsight-shorts/styles/night-file/example/data/lines.json +73 -0
  119. package/plugins/aiinsight-shorts/styles/night-file/example/data/words.json +572 -0
  120. package/plugins/aiinsight-shorts/styles/night-file/example/data/words.txt +95 -0
  121. package/plugins/aiinsight-shorts/styles/night-file/example/fonts/FreeMonoBold.ttf +0 -0
  122. package/plugins/aiinsight-shorts/styles/night-file/example/fonts/Inter-Black.otf +0 -0
  123. package/plugins/aiinsight-shorts/styles/night-file/example/index.html +18 -0
  124. package/plugins/aiinsight-shorts/styles/night-file/example/script.json +26 -0
  125. package/plugins/aiinsight-shorts/styles/night-file/example/sheet.html +13 -0
  126. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/board.js +62 -0
  127. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/captions.js +49 -0
  128. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/engine.js +65 -0
  129. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/human.js +128 -0
  130. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/kit.js +151 -0
  131. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/lib.js +44 -0
  132. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/sdf.js +79 -0
  133. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/sets3d.js +106 -0
  134. package/plugins/aiinsight-shorts/styles/night-file/example/src/core/timing.js +23 -0
  135. package/plugins/aiinsight-shorts/styles/night-file/example/src/main.js +56 -0
  136. package/plugins/aiinsight-shorts/styles/night-file/example/src/sheet.js +86 -0
  137. package/plugins/aiinsight-shorts/styles/night-file/example/src/shots.js +130 -0
  138. package/plugins/aiinsight-shorts/styles/night-file/example/tools/capture.mjs +122 -0
  139. package/plugins/aiinsight-shorts/styles/night-file/example/tools/mix.py +172 -0
  140. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/LICENSE-three.txt +21 -0
  141. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  142. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  143. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  144. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  145. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  146. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/Pass.js +95 -0
  147. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  148. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  149. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  150. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/shaders/BokehShader.js +145 -0
  151. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/shaders/CopyShader.js +45 -0
  152. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  153. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/addons/shaders/OutputShader.js +85 -0
  154. package/plugins/aiinsight-shorts/styles/night-file/example/vendor/three.module.js +6 -0
  155. package/plugins/aiinsight-shorts/styles/night-file/style-sheet.png +0 -0
  156. package/plugins/aiinsight-shorts/styles/paper-atlas/STYLE.md +30 -0
  157. package/plugins/aiinsight-shorts/styles/paper-atlas/example/data/cues.json +167 -0
  158. package/plugins/aiinsight-shorts/styles/paper-atlas/example/data/lines.json +67 -0
  159. package/plugins/aiinsight-shorts/styles/paper-atlas/example/data/words.json +614 -0
  160. package/plugins/aiinsight-shorts/styles/paper-atlas/example/data/words.txt +102 -0
  161. package/plugins/aiinsight-shorts/styles/paper-atlas/example/fonts/Caladea-Bold.ttf +0 -0
  162. package/plugins/aiinsight-shorts/styles/paper-atlas/example/fonts/Inter-Black.otf +0 -0
  163. package/plugins/aiinsight-shorts/styles/paper-atlas/example/index.html +15 -0
  164. package/plugins/aiinsight-shorts/styles/paper-atlas/example/script.json +26 -0
  165. package/plugins/aiinsight-shorts/styles/paper-atlas/example/sheet.html +12 -0
  166. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/core/board.js +62 -0
  167. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/core/captions.js +47 -0
  168. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/core/kit.js +152 -0
  169. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/core/lib.js +44 -0
  170. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/core/timing.js +23 -0
  171. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/main.js +62 -0
  172. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/sheet.js +61 -0
  173. package/plugins/aiinsight-shorts/styles/paper-atlas/example/src/shots.js +139 -0
  174. package/plugins/aiinsight-shorts/styles/paper-atlas/example/tools/capture.mjs +122 -0
  175. package/plugins/aiinsight-shorts/styles/paper-atlas/example/tools/mix.py +172 -0
  176. package/plugins/aiinsight-shorts/styles/paper-atlas/style-sheet.png +0 -0
  177. package/plugins/aiinsight-shorts/styles/stick-story/STYLE.md +32 -0
  178. package/plugins/aiinsight-shorts/styles/stick-story/example/data/cues.json +182 -0
  179. package/plugins/aiinsight-shorts/styles/stick-story/example/data/lines.json +79 -0
  180. package/plugins/aiinsight-shorts/styles/stick-story/example/data/words.json +662 -0
  181. package/plugins/aiinsight-shorts/styles/stick-story/example/data/words.txt +110 -0
  182. package/plugins/aiinsight-shorts/styles/stick-story/example/fonts/Inter-Black.otf +0 -0
  183. package/plugins/aiinsight-shorts/styles/stick-story/example/fonts/Inter-ExtraBold.otf +0 -0
  184. package/plugins/aiinsight-shorts/styles/stick-story/example/index.html +13 -0
  185. package/plugins/aiinsight-shorts/styles/stick-story/example/script.json +27 -0
  186. package/plugins/aiinsight-shorts/styles/stick-story/example/sheet.html +10 -0
  187. package/plugins/aiinsight-shorts/styles/stick-story/example/src/core/board.js +62 -0
  188. package/plugins/aiinsight-shorts/styles/stick-story/example/src/core/captions.js +47 -0
  189. package/plugins/aiinsight-shorts/styles/stick-story/example/src/core/kit.js +166 -0
  190. package/plugins/aiinsight-shorts/styles/stick-story/example/src/core/lib.js +44 -0
  191. package/plugins/aiinsight-shorts/styles/stick-story/example/src/core/timing.js +23 -0
  192. package/plugins/aiinsight-shorts/styles/stick-story/example/src/main.js +62 -0
  193. package/plugins/aiinsight-shorts/styles/stick-story/example/src/sheet.js +64 -0
  194. package/plugins/aiinsight-shorts/styles/stick-story/example/src/shots.js +148 -0
  195. package/plugins/aiinsight-shorts/styles/stick-story/example/tools/capture.mjs +122 -0
  196. package/plugins/aiinsight-shorts/styles/stick-story/example/tools/mix.py +172 -0
  197. package/plugins/aiinsight-shorts/styles/stick-story/style-sheet.png +0 -0
  198. package/plugins/aiinsight-shorts/styles/tall-tale/STYLE.md +51 -0
  199. package/plugins/aiinsight-shorts/styles/tall-tale/example/data/cues.json +271 -0
  200. package/plugins/aiinsight-shorts/styles/tall-tale/example/data/lines.json +127 -0
  201. package/plugins/aiinsight-shorts/styles/tall-tale/example/data/words.json +908 -0
  202. package/plugins/aiinsight-shorts/styles/tall-tale/example/data/words.txt +151 -0
  203. package/plugins/aiinsight-shorts/styles/tall-tale/example/fonts/Caladea-Bold.ttf +0 -0
  204. package/plugins/aiinsight-shorts/styles/tall-tale/example/fonts/Inter-Black.otf +0 -0
  205. package/plugins/aiinsight-shorts/styles/tall-tale/example/fonts/Inter-ExtraBold.otf +0 -0
  206. package/plugins/aiinsight-shorts/styles/tall-tale/example/index.html +19 -0
  207. package/plugins/aiinsight-shorts/styles/tall-tale/example/script.json +37 -0
  208. package/plugins/aiinsight-shorts/styles/tall-tale/example/sheet.html +13 -0
  209. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/board.js +62 -0
  210. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/captions.js +56 -0
  211. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/engine.js +65 -0
  212. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/lib.js +44 -0
  213. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/props.js +166 -0
  214. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/timing.js +23 -0
  215. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/toon.js +352 -0
  216. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/core/world.js +340 -0
  217. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/main.js +56 -0
  218. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/sheet.js +99 -0
  219. package/plugins/aiinsight-shorts/styles/tall-tale/example/src/shots.js +377 -0
  220. package/plugins/aiinsight-shorts/styles/tall-tale/example/tools/capture.mjs +122 -0
  221. package/plugins/aiinsight-shorts/styles/tall-tale/example/tools/mix.py +204 -0
  222. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/LICENSE-three.txt +21 -0
  223. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  224. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  225. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  226. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  227. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  228. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/Pass.js +95 -0
  229. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  230. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  231. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  232. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/shaders/BokehShader.js +145 -0
  233. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/shaders/CopyShader.js +45 -0
  234. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  235. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/addons/shaders/OutputShader.js +85 -0
  236. package/plugins/aiinsight-shorts/styles/tall-tale/example/vendor/three.module.js +6 -0
  237. package/plugins/aiinsight-shorts/styles/tall-tale/style-sheet.png +0 -0
  238. package/plugins/faceless-shorts/README.md +34 -0
  239. package/plugins/faceless-shorts/SKILL.md +124 -0
  240. package/plugins/faceless-shorts/styles/deep-dive/STYLE.md +58 -0
  241. package/plugins/faceless-shorts/styles/deep-dive/example/data/cues.json +140 -0
  242. package/plugins/faceless-shorts/styles/deep-dive/example/data/lines.json +67 -0
  243. package/plugins/faceless-shorts/styles/deep-dive/example/data/words.json +614 -0
  244. package/plugins/faceless-shorts/styles/deep-dive/example/data/words.txt +102 -0
  245. package/plugins/faceless-shorts/styles/deep-dive/example/fonts/Inter-Black.otf +0 -0
  246. package/plugins/faceless-shorts/styles/deep-dive/example/fonts/Inter-ExtraBold.otf +0 -0
  247. package/plugins/faceless-shorts/styles/deep-dive/example/index.html +16 -0
  248. package/plugins/faceless-shorts/styles/deep-dive/example/script.json +27 -0
  249. package/plugins/faceless-shorts/styles/deep-dive/example/sheet.html +10 -0
  250. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/board.js +62 -0
  251. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/captions.js +68 -0
  252. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/engine.js +65 -0
  253. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/human.js +128 -0
  254. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/lib.js +44 -0
  255. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/look2d.js +33 -0
  256. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/sdf.js +79 -0
  257. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/sets.js +107 -0
  258. package/plugins/faceless-shorts/styles/deep-dive/example/src/core/timing.js +23 -0
  259. package/plugins/faceless-shorts/styles/deep-dive/example/src/main.js +56 -0
  260. package/plugins/faceless-shorts/styles/deep-dive/example/src/sheet.js +79 -0
  261. package/plugins/faceless-shorts/styles/deep-dive/example/src/shots.js +136 -0
  262. package/plugins/faceless-shorts/styles/deep-dive/example/tools/capture.mjs +122 -0
  263. package/plugins/faceless-shorts/styles/deep-dive/example/tools/mix.py +172 -0
  264. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/LICENSE-three.txt +21 -0
  265. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  266. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  267. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  268. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  269. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  270. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/Pass.js +95 -0
  271. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  272. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  273. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  274. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/shaders/BokehShader.js +145 -0
  275. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/shaders/CopyShader.js +45 -0
  276. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  277. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/addons/shaders/OutputShader.js +85 -0
  278. package/plugins/faceless-shorts/styles/deep-dive/example/vendor/three.module.js +6 -0
  279. package/plugins/faceless-shorts/styles/deep-dive/style-sheet.png +0 -0
  280. package/plugins/faceless-shorts/styles/inside-out/STYLE.md +97 -0
  281. package/plugins/faceless-shorts/styles/inside-out/example/data/cues.json +178 -0
  282. package/plugins/faceless-shorts/styles/inside-out/example/data/lines.json +73 -0
  283. package/plugins/faceless-shorts/styles/inside-out/example/data/words.json +662 -0
  284. package/plugins/faceless-shorts/styles/inside-out/example/fonts/Inter-Black.otf +0 -0
  285. package/plugins/faceless-shorts/styles/inside-out/example/index.html +16 -0
  286. package/plugins/faceless-shorts/styles/inside-out/example/script.json +26 -0
  287. package/plugins/faceless-shorts/styles/inside-out/example/src/core/captions.js +68 -0
  288. package/plugins/faceless-shorts/styles/inside-out/example/src/core/kit.js +345 -0
  289. package/plugins/faceless-shorts/styles/inside-out/example/src/core/lib.js +44 -0
  290. package/plugins/faceless-shorts/styles/inside-out/example/src/core/timing.js +23 -0
  291. package/plugins/faceless-shorts/styles/inside-out/example/src/main.js +62 -0
  292. package/plugins/faceless-shorts/styles/inside-out/example/src/shots.js +195 -0
  293. package/plugins/faceless-shorts/styles/inside-out/example/tools/capture.mjs +122 -0
  294. package/plugins/faceless-shorts/styles/inside-out/example/tools/mix.py +172 -0
  295. package/plugins/faceless-shorts/styles/inside-out/style-sheet.png +0 -0
  296. package/plugins/faceless-shorts/styles/night-file/STYLE.md +36 -0
  297. package/plugins/faceless-shorts/styles/night-file/example/data/cues.json +146 -0
  298. package/plugins/faceless-shorts/styles/night-file/example/data/lines.json +73 -0
  299. package/plugins/faceless-shorts/styles/night-file/example/data/words.json +572 -0
  300. package/plugins/faceless-shorts/styles/night-file/example/data/words.txt +95 -0
  301. package/plugins/faceless-shorts/styles/night-file/example/fonts/FreeMonoBold.ttf +0 -0
  302. package/plugins/faceless-shorts/styles/night-file/example/fonts/Inter-Black.otf +0 -0
  303. package/plugins/faceless-shorts/styles/night-file/example/index.html +16 -0
  304. package/plugins/faceless-shorts/styles/night-file/example/script.json +26 -0
  305. package/plugins/faceless-shorts/styles/night-file/example/sheet.html +11 -0
  306. package/plugins/faceless-shorts/styles/night-file/example/src/core/board.js +62 -0
  307. package/plugins/faceless-shorts/styles/night-file/example/src/core/captions.js +49 -0
  308. package/plugins/faceless-shorts/styles/night-file/example/src/core/engine.js +65 -0
  309. package/plugins/faceless-shorts/styles/night-file/example/src/core/human.js +128 -0
  310. package/plugins/faceless-shorts/styles/night-file/example/src/core/kit.js +151 -0
  311. package/plugins/faceless-shorts/styles/night-file/example/src/core/lib.js +44 -0
  312. package/plugins/faceless-shorts/styles/night-file/example/src/core/sdf.js +79 -0
  313. package/plugins/faceless-shorts/styles/night-file/example/src/core/sets3d.js +106 -0
  314. package/plugins/faceless-shorts/styles/night-file/example/src/core/timing.js +23 -0
  315. package/plugins/faceless-shorts/styles/night-file/example/src/main.js +56 -0
  316. package/plugins/faceless-shorts/styles/night-file/example/src/sheet.js +86 -0
  317. package/plugins/faceless-shorts/styles/night-file/example/src/shots.js +130 -0
  318. package/plugins/faceless-shorts/styles/night-file/example/tools/capture.mjs +122 -0
  319. package/plugins/faceless-shorts/styles/night-file/example/tools/mix.py +172 -0
  320. package/plugins/faceless-shorts/styles/night-file/example/vendor/LICENSE-three.txt +21 -0
  321. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  322. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  323. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  324. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  325. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  326. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/Pass.js +95 -0
  327. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  328. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  329. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  330. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/shaders/BokehShader.js +145 -0
  331. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/shaders/CopyShader.js +45 -0
  332. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  333. package/plugins/faceless-shorts/styles/night-file/example/vendor/addons/shaders/OutputShader.js +85 -0
  334. package/plugins/faceless-shorts/styles/night-file/example/vendor/three.module.js +6 -0
  335. package/plugins/faceless-shorts/styles/night-file/style-sheet.png +0 -0
  336. package/plugins/faceless-shorts/styles/paper-atlas/STYLE.md +30 -0
  337. package/plugins/faceless-shorts/styles/paper-atlas/example/data/cues.json +167 -0
  338. package/plugins/faceless-shorts/styles/paper-atlas/example/data/lines.json +67 -0
  339. package/plugins/faceless-shorts/styles/paper-atlas/example/data/words.json +614 -0
  340. package/plugins/faceless-shorts/styles/paper-atlas/example/data/words.txt +102 -0
  341. package/plugins/faceless-shorts/styles/paper-atlas/example/fonts/Caladea-Bold.ttf +0 -0
  342. package/plugins/faceless-shorts/styles/paper-atlas/example/fonts/Inter-Black.otf +0 -0
  343. package/plugins/faceless-shorts/styles/paper-atlas/example/index.html +13 -0
  344. package/plugins/faceless-shorts/styles/paper-atlas/example/script.json +26 -0
  345. package/plugins/faceless-shorts/styles/paper-atlas/example/sheet.html +10 -0
  346. package/plugins/faceless-shorts/styles/paper-atlas/example/src/core/board.js +62 -0
  347. package/plugins/faceless-shorts/styles/paper-atlas/example/src/core/captions.js +47 -0
  348. package/plugins/faceless-shorts/styles/paper-atlas/example/src/core/kit.js +152 -0
  349. package/plugins/faceless-shorts/styles/paper-atlas/example/src/core/lib.js +44 -0
  350. package/plugins/faceless-shorts/styles/paper-atlas/example/src/core/timing.js +23 -0
  351. package/plugins/faceless-shorts/styles/paper-atlas/example/src/main.js +62 -0
  352. package/plugins/faceless-shorts/styles/paper-atlas/example/src/sheet.js +61 -0
  353. package/plugins/faceless-shorts/styles/paper-atlas/example/src/shots.js +139 -0
  354. package/plugins/faceless-shorts/styles/paper-atlas/example/tools/capture.mjs +122 -0
  355. package/plugins/faceless-shorts/styles/paper-atlas/example/tools/mix.py +172 -0
  356. package/plugins/faceless-shorts/styles/paper-atlas/style-sheet.png +0 -0
  357. package/plugins/faceless-shorts/styles/stick-story/STYLE.md +32 -0
  358. package/plugins/faceless-shorts/styles/stick-story/example/data/cues.json +182 -0
  359. package/plugins/faceless-shorts/styles/stick-story/example/data/lines.json +79 -0
  360. package/plugins/faceless-shorts/styles/stick-story/example/data/words.json +662 -0
  361. package/plugins/faceless-shorts/styles/stick-story/example/data/words.txt +110 -0
  362. package/plugins/faceless-shorts/styles/stick-story/example/fonts/Inter-Black.otf +0 -0
  363. package/plugins/faceless-shorts/styles/stick-story/example/fonts/Inter-ExtraBold.otf +0 -0
  364. package/plugins/faceless-shorts/styles/stick-story/example/index.html +12 -0
  365. package/plugins/faceless-shorts/styles/stick-story/example/script.json +27 -0
  366. package/plugins/faceless-shorts/styles/stick-story/example/sheet.html +9 -0
  367. package/plugins/faceless-shorts/styles/stick-story/example/src/core/board.js +62 -0
  368. package/plugins/faceless-shorts/styles/stick-story/example/src/core/captions.js +47 -0
  369. package/plugins/faceless-shorts/styles/stick-story/example/src/core/kit.js +166 -0
  370. package/plugins/faceless-shorts/styles/stick-story/example/src/core/lib.js +44 -0
  371. package/plugins/faceless-shorts/styles/stick-story/example/src/core/timing.js +23 -0
  372. package/plugins/faceless-shorts/styles/stick-story/example/src/main.js +62 -0
  373. package/plugins/faceless-shorts/styles/stick-story/example/src/sheet.js +64 -0
  374. package/plugins/faceless-shorts/styles/stick-story/example/src/shots.js +148 -0
  375. package/plugins/faceless-shorts/styles/stick-story/example/tools/capture.mjs +122 -0
  376. package/plugins/faceless-shorts/styles/stick-story/example/tools/mix.py +172 -0
  377. package/plugins/faceless-shorts/styles/stick-story/style-sheet.png +0 -0
  378. package/plugins/faceless-shorts/styles/tall-tale/STYLE.md +51 -0
  379. package/plugins/faceless-shorts/styles/tall-tale/example/data/cues.json +271 -0
  380. package/plugins/faceless-shorts/styles/tall-tale/example/data/lines.json +127 -0
  381. package/plugins/faceless-shorts/styles/tall-tale/example/data/words.json +908 -0
  382. package/plugins/faceless-shorts/styles/tall-tale/example/data/words.txt +151 -0
  383. package/plugins/faceless-shorts/styles/tall-tale/example/fonts/Caladea-Bold.ttf +0 -0
  384. package/plugins/faceless-shorts/styles/tall-tale/example/fonts/Inter-Black.otf +0 -0
  385. package/plugins/faceless-shorts/styles/tall-tale/example/fonts/Inter-ExtraBold.otf +0 -0
  386. package/plugins/faceless-shorts/styles/tall-tale/example/index.html +17 -0
  387. package/plugins/faceless-shorts/styles/tall-tale/example/script.json +37 -0
  388. package/plugins/faceless-shorts/styles/tall-tale/example/sheet.html +11 -0
  389. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/board.js +62 -0
  390. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/captions.js +56 -0
  391. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/engine.js +65 -0
  392. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/lib.js +44 -0
  393. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/props.js +166 -0
  394. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/timing.js +23 -0
  395. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/toon.js +352 -0
  396. package/plugins/faceless-shorts/styles/tall-tale/example/src/core/world.js +340 -0
  397. package/plugins/faceless-shorts/styles/tall-tale/example/src/main.js +56 -0
  398. package/plugins/faceless-shorts/styles/tall-tale/example/src/sheet.js +99 -0
  399. package/plugins/faceless-shorts/styles/tall-tale/example/src/shots.js +377 -0
  400. package/plugins/faceless-shorts/styles/tall-tale/example/tools/capture.mjs +122 -0
  401. package/plugins/faceless-shorts/styles/tall-tale/example/tools/mix.py +204 -0
  402. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/LICENSE-three.txt +21 -0
  403. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/environments/RoomEnvironment.js +144 -0
  404. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/BokehPass.js +141 -0
  405. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/EffectComposer.js +231 -0
  406. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/MaskPass.js +104 -0
  407. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/OutputPass.js +97 -0
  408. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/Pass.js +95 -0
  409. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/RenderPass.js +99 -0
  410. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/ShaderPass.js +77 -0
  411. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/postprocessing/UnrealBloomPass.js +415 -0
  412. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/shaders/BokehShader.js +145 -0
  413. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/shaders/CopyShader.js +45 -0
  414. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/shaders/LuminosityHighPassShader.js +64 -0
  415. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/addons/shaders/OutputShader.js +85 -0
  416. package/plugins/faceless-shorts/styles/tall-tale/example/vendor/three.module.js +6 -0
  417. package/plugins/faceless-shorts/styles/tall-tale/style-sheet.png +0 -0
  418. package/plugins/motion-styles/LIBRARY.md +896 -0
  419. package/plugins/motion-styles/README.md +29 -0
  420. package/plugins/motion-styles/SKILL.md +70 -0
  421. package/plugins/motion-styles/catalog.json +2253 -0
  422. package/plugins/motion-styles/prompts/bio-glow.md +149 -0
  423. package/plugins/motion-styles/prompts/blueprint.md +145 -0
  424. package/plugins/motion-styles/prompts/breaking-desk.md +147 -0
  425. package/plugins/motion-styles/prompts/callout-kit.md +147 -0
  426. package/plugins/motion-styles/prompts/chalk-talk.md +153 -0
  427. package/plugins/motion-styles/prompts/chat-story.md +143 -0
  428. package/plugins/motion-styles/prompts/clay-diorama.md +149 -0
  429. package/plugins/motion-styles/prompts/clean-tech.md +147 -0
  430. package/plugins/motion-styles/prompts/comic-panels.md +145 -0
  431. package/plugins/motion-styles/prompts/cosmic-scale.md +147 -0
  432. package/plugins/motion-styles/prompts/creator-motion.md +149 -0
  433. package/plugins/motion-styles/prompts/data-city.md +149 -0
  434. package/plugins/motion-styles/prompts/data-story.md +147 -0
  435. package/plugins/motion-styles/prompts/deep-layers.md +147 -0
  436. package/plugins/motion-styles/prompts/doc-cinema.md +145 -0
  437. package/plugins/motion-styles/prompts/evidence-board.md +145 -0
  438. package/plugins/motion-styles/prompts/exploded-view.md +149 -0
  439. package/plugins/motion-styles/prompts/glass-icons.md +147 -0
  440. package/plugins/motion-styles/prompts/glitch-pack.md +145 -0
  441. package/plugins/motion-styles/prompts/globe-arcs.md +147 -0
  442. package/plugins/motion-styles/prompts/headline-backdrop.md +145 -0
  443. package/plugins/motion-styles/prompts/iso-cutaway.md +147 -0
  444. package/plugins/motion-styles/prompts/isotype.md +145 -0
  445. package/plugins/motion-styles/prompts/karaoke-subs.md +145 -0
  446. package/plugins/motion-styles/prompts/low-poly-atlas.md +149 -0
  447. package/plugins/motion-styles/prompts/lower-thirds.md +147 -0
  448. package/plugins/motion-styles/prompts/marker-board.md +145 -0
  449. package/plugins/motion-styles/prompts/market-tape.md +145 -0
  450. package/plugins/motion-styles/prompts/math-canvas.md +145 -0
  451. package/plugins/motion-styles/prompts/myth-or-fact.md +145 -0
  452. package/plugins/motion-styles/prompts/neon-line.md +145 -0
  453. package/plugins/motion-styles/prompts/paper-craft.md +145 -0
  454. package/plugins/motion-styles/prompts/parchment-map.md +147 -0
  455. package/plugins/motion-styles/prompts/physics-toybox.md +147 -0
  456. package/plugins/motion-styles/prompts/pixel-quest.md +145 -0
  457. package/plugins/motion-styles/prompts/podcast-clip.md +147 -0
  458. package/plugins/motion-styles/prompts/pop-info.md +145 -0
  459. package/plugins/motion-styles/prompts/product-turntable.md +149 -0
  460. package/plugins/motion-styles/prompts/punch-captions.md +145 -0
  461. package/plugins/motion-styles/prompts/quiz-countdown.md +145 -0
  462. package/plugins/motion-styles/prompts/quote-cinema.md +145 -0
  463. package/plugins/motion-styles/prompts/recipe-steps.md +145 -0
  464. package/plugins/motion-styles/prompts/rubber-hose-reel.md +145 -0
  465. package/plugins/motion-styles/prompts/shadow-puppets.md +145 -0
  466. package/plugins/motion-styles/prompts/size-lineup.md +149 -0
  467. package/plugins/motion-styles/prompts/software-walkthrough.md +147 -0
  468. package/plugins/motion-styles/prompts/stack-split.md +145 -0
  469. package/plugins/motion-styles/prompts/sticker-pop.md +145 -0
  470. package/plugins/motion-styles/prompts/storybook.md +145 -0
  471. package/plugins/motion-styles/prompts/swiss-grid.md +145 -0
  472. package/plugins/motion-styles/prompts/synth-grid.md +147 -0
  473. package/plugins/motion-styles/prompts/tactics-board.md +145 -0
  474. package/plugins/motion-styles/prompts/terminal.md +145 -0
  475. package/plugins/motion-styles/prompts/tier-list.md +143 -0
  476. package/plugins/motion-styles/prompts/timeline-rail.md +145 -0
  477. package/plugins/motion-styles/prompts/top-5-countdown.md +145 -0
  478. package/plugins/motion-styles/prompts/versus-split.md +145 -0
  479. package/plugins/motion-styles/prompts/vhs-camcorder.md +145 -0
  480. package/plugins/motion-styles/prompts/visual-novel.md +145 -0
  481. package/plugins/motion-styles/prompts/warm-canvas.md +145 -0
  482. package/plugins/motion-styles/prompts/woodcut-tale.md +145 -0
  483. package/plugins/motion-styles/prompts/zine-collage.md +145 -0
  484. package/plugins/motion-styles/sources/bio-glow.html +639 -0
  485. package/plugins/motion-styles/sources/blueprint.html +617 -0
  486. package/plugins/motion-styles/sources/breaking-desk.html +585 -0
  487. package/plugins/motion-styles/sources/callout-kit.html +586 -0
  488. package/plugins/motion-styles/sources/chalk-talk.html +597 -0
  489. package/plugins/motion-styles/sources/chat-story.html +597 -0
  490. package/plugins/motion-styles/sources/clay-diorama.html +681 -0
  491. package/plugins/motion-styles/sources/clean-tech.html +554 -0
  492. package/plugins/motion-styles/sources/comic-panels.html +608 -0
  493. package/plugins/motion-styles/sources/cosmic-scale.html +628 -0
  494. package/plugins/motion-styles/sources/creator-motion.html +805 -0
  495. package/plugins/motion-styles/sources/data-city.html +613 -0
  496. package/plugins/motion-styles/sources/data-story.html +539 -0
  497. package/plugins/motion-styles/sources/deep-layers.html +624 -0
  498. package/plugins/motion-styles/sources/doc-cinema.html +590 -0
  499. package/plugins/motion-styles/sources/evidence-board.html +610 -0
  500. package/plugins/motion-styles/sources/exploded-view.html +618 -0
  501. package/plugins/motion-styles/sources/glass-icons.html +612 -0
  502. package/plugins/motion-styles/sources/glitch-pack.html +583 -0
  503. package/plugins/motion-styles/sources/globe-arcs.html +621 -0
  504. package/plugins/motion-styles/sources/headline-backdrop.html +584 -0
  505. package/plugins/motion-styles/sources/iso-cutaway.html +621 -0
  506. package/plugins/motion-styles/sources/isotype.html +588 -0
  507. package/plugins/motion-styles/sources/karaoke-subs.html +577 -0
  508. package/plugins/motion-styles/sources/low-poly-atlas.html +621 -0
  509. package/plugins/motion-styles/sources/lower-thirds.html +582 -0
  510. package/plugins/motion-styles/sources/marker-board.html +598 -0
  511. package/plugins/motion-styles/sources/market-tape.html +598 -0
  512. package/plugins/motion-styles/sources/math-canvas.html +592 -0
  513. package/plugins/motion-styles/sources/myth-or-fact.html +587 -0
  514. package/plugins/motion-styles/sources/neon-line.html +597 -0
  515. package/plugins/motion-styles/sources/paper-craft.html +612 -0
  516. package/plugins/motion-styles/sources/parchment-map.html +624 -0
  517. package/plugins/motion-styles/sources/physics-toybox.html +617 -0
  518. package/plugins/motion-styles/sources/pixel-quest.html +609 -0
  519. package/plugins/motion-styles/sources/podcast-clip.html +585 -0
  520. package/plugins/motion-styles/sources/pop-info.html +677 -0
  521. package/plugins/motion-styles/sources/product-turntable.html +611 -0
  522. package/plugins/motion-styles/sources/punch-captions.html +583 -0
  523. package/plugins/motion-styles/sources/quiz-countdown.html +593 -0
  524. package/plugins/motion-styles/sources/quote-cinema.html +581 -0
  525. package/plugins/motion-styles/sources/recipe-steps.html +599 -0
  526. package/plugins/motion-styles/sources/rubber-hose-reel.html +627 -0
  527. package/plugins/motion-styles/sources/shadow-puppets.html +609 -0
  528. package/plugins/motion-styles/sources/size-lineup.html +620 -0
  529. package/plugins/motion-styles/sources/software-walkthrough.html +651 -0
  530. package/plugins/motion-styles/sources/stack-split.html +588 -0
  531. package/plugins/motion-styles/sources/sticker-pop.html +587 -0
  532. package/plugins/motion-styles/sources/storybook.html +600 -0
  533. package/plugins/motion-styles/sources/swiss-grid.html +590 -0
  534. package/plugins/motion-styles/sources/synth-grid.html +614 -0
  535. package/plugins/motion-styles/sources/tactics-board.html +608 -0
  536. package/plugins/motion-styles/sources/terminal.html +593 -0
  537. package/plugins/motion-styles/sources/tier-list.html +600 -0
  538. package/plugins/motion-styles/sources/timeline-rail.html +598 -0
  539. package/plugins/motion-styles/sources/top-5-countdown.html +589 -0
  540. package/plugins/motion-styles/sources/versus-split.html +592 -0
  541. package/plugins/motion-styles/sources/vhs-camcorder.html +587 -0
  542. package/plugins/motion-styles/sources/visual-novel.html +623 -0
  543. package/plugins/motion-styles/sources/warm-canvas.html +742 -0
  544. package/plugins/motion-styles/sources/woodcut-tale.html +598 -0
  545. package/plugins/motion-styles/sources/zine-collage.html +594 -0
  546. package/plugins/video-pipeline/.claude-plugin/plugin.json +2 -2
  547. package/plugins/video-pipeline/skills/.upstream-skills.json +156 -143
  548. package/plugins/video-pipeline/skills/video-pipeline/references/mcp-tools.md +3 -3
  549. package/scripts/deep-validate.js +3 -2
  550. package/src/audio-mixer/mixer.js +69 -0
  551. package/src/cli/make.js +19 -0
  552. package/src/harness/lib/browser.mjs +22 -1
  553. package/src/harness/lib/capture.mjs +33 -11
  554. package/src/harness/lib/jobs.mjs +16 -1
  555. package/src/mcp-server/server.js +726 -10
  556. package/src/pipeline/providers.js +77 -17
  557. package/src/skills/sync-upstream.js +15 -3
  558. package/src/utils/media-downloader.js +7 -0
  559. package/src/utils/tool-installer.js +212 -0
  560. package/src/utils/transcribe.js +9 -1
  561. package/src/video-renderer/renderer.js +68 -38
  562. package/src/video-renderer/scene-animator.js +50 -15
  563. package/src/workflows/assets.js +49 -5
  564. package/src/workflows/render.js +2 -0
  565. package/src/workflows/script.js +14 -3
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "creator-motion-graphics",
3
+ "title": "Creator Motion Graphics",
4
+ "family": "Kinetic Type",
5
+ "fonts": [
6
+ "60px \"Anton\"",
7
+ "italic 60px \"Instrument Serif\"",
8
+ "600 30px \"Inter\"",
9
+ "700 30px \"Inter\"",
10
+ "800 30px \"Inter\"",
11
+ "500 20px \"Inter\""
12
+ ],
13
+ "demo_duration": 21.5,
14
+ "demo_still": 1.95,
15
+ "has_aroll": false
16
+ }
@@ -0,0 +1,39 @@
1
+ # Creator Motion Graphics (Kinetic Type)
2
+
3
+ ## Style card
4
+ Name: Creator Motion Graphics
5
+ Family: Kinetic Type
6
+ Look: The high-energy business-creator edit: huge condensed caps slamming in word by word, a yellow serif accent on the one word that matters, highlighter swipes, hand-drawn circles and arrows, tilted browser and phone mockups, rolling green counters, film grain and light leaks on near-black.
7
+ How it explains: Every idea gets its own beat: words land on the beat with punch zooms and shake, the wrong idea gets a red strike and a stamp, proof appears as code-drawn mockups, the key number rolls up and locks with a boom, and whip pans, RGB glitches and flash cuts carry you between scenes.
8
+
9
+ ## The look
10
+ - Surface: a near-black editing-room background with slow warm and cool glows, a faint drifting grid, a vignette and film grain stepped at 24 fps. Run background() and finishing() on every frame at the reference’s strength.
11
+ - Type: Anton in caps for statements, set big (80 to 220 px). Instrument Serif italic in yellow for the one soft or emotional phrase. Inter for labels and small supporting lines. At most one yellow word per line; green only for money and growth numbers; red only for the wrong idea.
12
+ - How things appear: words slam in one at a time (large and blurred, settling with an expo ease). Cards, chips and pills pop with overshoot. Mockups swing in with a 3D tilt (drawYaw) or slide up from below.
13
+ - Emphasis: punch zoom plus shake on the hardest words; a yellow highlighter swipe that turns the word dark; a hand-drawn circle and arrow onto the thing that matters; a red strike-through for the wrong idea; a stamp for a flat rejection.
14
+ - Numbers: a rolling counter in green with a glow. When it locks: a boom, a white flash, a light leak and spark streaks.
15
+ - Proof shots: stylized mockups drawn in code (browser pages, phone chats, dashboards, cards) with invented, generic names. No real logos, brand names or real people, and nothing that would pass as a real receipt or a real message. Label figures as examples when they are illustrative.
16
+ - Scene changes: every new idea gets a cut, rotating between whip pans with motion blur, RGB glitches and flash cuts. Inside a scene keep things continuous: lines stack, shift up, get struck out, and new lines arrive.
17
+ - Chrome: a small topic label at the top left, plus a STEP pill only when the content is a real sequence.
18
+ - Pace: the hook lands in under 2 seconds, something new happens every 0.5 to 1.5 seconds, and the takeaway holds about 1.5 seconds at the end.
19
+
20
+ ## Rules that keep it in style
21
+ - Change the story, the words and the props; do not change the medium. Draw every subject accurately for my topic and double-check any facts and numbers you show.
22
+ - Every frame is a pure function of t. Use CM.rng(seed) and CM.noise1(seed) for all randomness, never Math.random. Caching static layers is fine.
23
+ - It will be watched on phones: at 1280x720 keep statement text at 80 px or more, any label the story depends on at 28 px or more, and key numbers at 120 px or more. Fill the frame.
24
+ - Keep each frame fast, well under 50 ms.
25
+ - Give my topic its own demo content; do not reuse the reference's story ("How to land your first client") unless my content asks for it.
26
+
27
+ ## Sound
28
+ - Music: a low drone and a riser into the first big word, the half-time beat (k.music) under the body, a tape stop on the wrong idea, and the beat back for the takeaway. Booms duck the music on their own.
29
+ - Map events to the kit: word slams get "slam" (bigger word, more gain); pills, chips and cards get "pop"; cuts get "whoosh", "glitch" or a "slam" on the flash; highlighter and hand-drawn marks get "marker"; strike-throughs get "strike"; typing gets "keys"; a sent message "send", an incoming one "ping"; clicks "click"; counters "tick" only when the shown number changes and never more than 12 per second; the big number lands on "boom" + "slam" + "shimmer"; a rejection gets "stamp".
30
+ - Full mix (no voiceover): keep the music, have at most about 3 sounds at once, and leave a moment of near-silence before the biggest hit.
31
+ - Sync is everything: the sound score reads the same TL times as the picture.
32
+
33
+ ## Helpers to reuse (from the original prompt)
34
+ One self-contained HTML file (an artifact if you can), built on the reference file below:
35
+ - Keep the page, the player (play/pause, scrub, time, sound toggle, loop, Load voiceover) and the CM and SFX blocks unchanged.
36
+ - Replace the demo's story: TL, CUTS, PUNCH, SHAKE, FLASH, LEAK, PUSH, the value curves, the scene functions, SCENES, the chrome() label text and scoreSound(). Keep and reuse the style's helpers (slam, row, swipe, slamMarked, strike, scribble, handArrow, drawYaw, bubble, background, finishing and the compositor).
37
+ - Aim for 15 to 40 seconds unless my content needs more, and set the duration in TL.D.
38
+ - Keep window.seek(t), window.DURATION, window.scoreSound and window.SFX exposed, so the video can be rendered frame by frame.
39
+ - Load the style's fonts before the first draw, as the reference does: Anton, Instrument Serif (italic), Inter.
@@ -0,0 +1,432 @@
1
+ // Data Story (Data) style engine for the ai-video-editor skill.
2
+ // Unchanged from the Motion Graphics Style Library reference, except: the player is removed (the skill's player.js replaces it)
3
+ // and TL, CUTS, PUNCH, SCENES are declared with var so your story.js can redefine them.
4
+ // Everything after the 'STYLE:' banner that draws the demo story (scene functions, explainer, scoreSound) is a worked example.
5
+ // =====================================================================================================
6
+ // CREATOR MOTION GRAPHICS · core helpers (CM), sound kit (SFX), style, player
7
+ // Every frame is a pure function of t. All randomness is seeded. Times are seconds.
8
+ // =====================================================================================================
9
+ const CM = (() => {
10
+ const clamp = (v, a = 0, b = 1) => (v < a ? a : v > b ? b : v);
11
+ const lerp = (a, b, u) => a + (b - a) * u;
12
+ const span = (t, a, b) => clamp((t - a) / (b - a));
13
+ const rotl = (x, k) => (x << k) | (x >>> (32 - k));
14
+ const hash = (s) => { s = String(s); let h = 0x9e3779b1; for (let i = 0; i < s.length; i++) { h = Math.imul(h ^ s.charCodeAt(i), 0x85ebca6b); h ^= h >>> 13; } return h >>> 0; };
15
+ // xoshiro128** style generator, seeded from a number or string
16
+ const rng = (seed) => {
17
+ const s = typeof seed === 'number' ? seed >>> 0 : hash(seed);
18
+ let a = (s ^ 0xa5a5a5a5) >>> 0, b = (Math.imul(s, 0x27d4eb2d) >>> 0) || 1, c = (0x165667b1 ^ s) >>> 0, d = (rotl(s, 7) >>> 0) || 7;
19
+ const r = () => { const res = Math.imul(rotl(Math.imul(b, 5), 7), 9) >>> 0; const t = b << 9; c ^= a; d ^= b; b ^= c; a ^= d; c ^= t; d = rotl(d, 11); return res / 4294967296; };
20
+ for (let i = 0; i < 8; i++) r();
21
+ r.in = (lo, hi) => lo + r() * (hi - lo);
22
+ r.pick = (arr) => arr[(r() * arr.length) | 0];
23
+ return r;
24
+ };
25
+ // smooth 1D value noise, roughly -1..1
26
+ const noise1 = (seed) => { const r = rng(seed), v = Float32Array.from({ length: 256 }, () => r() * 2 - 1); return (x) => { const i = Math.floor(x), f = x - i, u = f * f * (3 - 2 * f); return lerp(v[i & 255], v[(i + 1) & 255], u); }; };
27
+ const ez = {
28
+ out3: (u) => 1 - Math.pow(1 - u, 3),
29
+ in3: (u) => u * u * u,
30
+ inout3: (u) => (u < 0.5 ? 4 * u * u * u : 1 - Math.pow(-2 * u + 2, 3) / 2),
31
+ outExpo: (u) => (u >= 1 ? 1 : 1 - Math.pow(2, -10 * u)),
32
+ back: (u, s = 1.7) => (u <= 0 ? 0 : u >= 1 ? 1 : 1 + (s + 1) * Math.pow(u - 1, 3) + s * Math.pow(u - 1, 2)),
33
+ spring: (u) => (u <= 0 ? 0 : u >= 1 ? 1 : 1 - Math.exp(-6 * u) * Math.cos(13 * u)),
34
+ };
35
+ const layer = (w, h) => { const c = document.createElement('canvas'); c.width = w; c.height = h; return c; };
36
+ return { clamp, lerp, span, hash, rng, noise1, ez, layer };
37
+ })();
38
+
39
+ // =====================================================================================================
40
+ // SFX: synthesized creator-edit sound kit. Plain Web Audio, no files.
41
+ // voiceover mode keeps the music low under narration and plays sounds marked `soft` at 55%.
42
+ // =====================================================================================================
43
+ const SFX = (() => {
44
+ const SEMI = { C: -9, D: -7, E: -5, F: -4, G: -2, A: 0, B: 2 };
45
+ const hz = (n) => { if (typeof n === 'number') return n; const m = /^([A-G])(#|b)?(-?\d)$/.exec(n); return 440 * Math.pow(2, (SEMI[m[1]] + (m[2] === '#' ? 1 : m[2] === 'b' ? -1 : 0) + (+m[3] - 4) * 12) / 12); };
46
+
47
+ function kit(ac, o = {}) {
48
+ const VO = !!o.voiceover, SR = ac.sampleRate, T0 = o.from || 0, base = ac.currentTime + (o.lead || 0);
49
+ // snap every start a little past the sample grid (avoids level spikes on some browsers)
50
+ const at = (t) => (Math.round((base + (t - T0)) * SR) + 0.3) / SR;
51
+ const skip = (t, dur) => at(t) + dur < ac.currentTime || (t < T0 - 0.005 && dur < 1.2);
52
+
53
+ const comp = ac.createDynamicsCompressor();
54
+ comp.threshold.value = -14; comp.knee.value = 6; comp.ratio.value = 4; comp.attack.value = 0.003; comp.release.value = 0.15;
55
+ const master = ac.createGain(); master.gain.value = o.master == null ? 0.85 : o.master;
56
+ comp.connect(master); master.connect(o.dest || ac.destination);
57
+ const mk = (g) => { const n = ac.createGain(); n.gain.value = g; n.connect(comp); return n; };
58
+ const bus = { fx: mk(VO ? 0.78 : 0.95), music: mk(VO ? 0.3 : 0.62), amb: mk(0.45) };
59
+ const duckIn = ac.createGain(); duckIn.connect(bus.music);
60
+ const DUCKS = [];
61
+
62
+ // generated plate-style reverb
63
+ const verb = ac.createConvolver();
64
+ { const n = Math.floor(1.5 * SR), ir = ac.createBuffer(2, n, SR);
65
+ for (let ch = 0; ch < 2; ch++) { const r = CM.rng('plate' + ch), d = ir.getChannelData(ch); let lp = 0; for (let i = 0; i < n; i++) { const u = i / n; lp += (0.7 - 0.5 * u) * ((r() * 2 - 1) - lp); d[i] = lp * Math.pow(1 - u, 2.6) * 0.9; } }
66
+ verb.buffer = ir; }
67
+ const verbIn = ac.createGain(); verbIn.gain.value = 0.8; verbIn.connect(verb); verb.connect(comp);
68
+
69
+ // seeded noise buffers
70
+ const NB = {};
71
+ { const n = Math.floor(2 * SR);
72
+ for (const kind of ['white', 'pink', 'brown']) {
73
+ const b = ac.createBuffer(1, n, SR), d = b.getChannelData(0), r = CM.rng('nz' + kind); let b0 = 0, b1 = 0, b2 = 0, br = 0;
74
+ for (let i = 0; i < n; i++) { const w = r() * 2 - 1;
75
+ if (kind === 'white') d[i] = w;
76
+ else if (kind === 'pink') { b0 = 0.99765 * b0 + w * 0.099; b1 = 0.963 * b1 + w * 0.2965; b2 = 0.57 * b2 + w * 1.0527; d[i] = (b0 + b1 + b2 + w * 0.1848) * 0.18; }
77
+ else { br = (br + 0.02 * w) / 1.02; d[i] = br * 3.4; } }
78
+ NB[kind] = b; } }
79
+
80
+ const ramp = (param, v, t) => { if (!Array.isArray(v)) { param.setValueAtTime(v, t); return; } param.setValueAtTime(v[0][1], t + v[0][0]); for (let i = 1; i < v.length; i++) param.exponentialRampToValueAtTime(Math.max(1e-3, v[i][1]), t + v[i][0]); };
81
+ const env = (g, t, peak, dur, a = 0.004, hold = 0) => {
82
+ a = Math.min(a, dur * 0.9); hold = Math.min(hold, dur - a);
83
+ g.gain.setValueAtTime(0, t); g.gain.linearRampToValueAtTime(peak, t + a);
84
+ if (hold > 0) g.gain.setValueAtTime(peak, t + a + hold);
85
+ g.gain.exponentialRampToValueAtTime(1e-4, t + dur); g.gain.setValueAtTime(0, t + dur + 0.002);
86
+ };
87
+ const level = (p, d) => (p.gain == null ? d : p.gain) * (VO && p.soft ? 0.55 : 1);
88
+ const route = (node, p, t) => {
89
+ const pan = ac.createStereoPanner();
90
+ if (Array.isArray(p.pan)) { pan.pan.setValueAtTime(p.pan[0], t); pan.pan.linearRampToValueAtTime(p.pan[1], t + (p.dur || 0.2)); } else pan.pan.value = CM.clamp(p.pan || 0, -1, 1);
91
+ node.connect(pan); pan.connect(p.bus === 'music' ? duckIn : bus[p.bus || 'fx']);
92
+ if (p.verb) { const s = ac.createGain(); s.gain.value = p.verb; pan.connect(s); s.connect(verbIn); }
93
+ };
94
+ const filt = (p, t) => { if (!p.filter) return null; const f = ac.createBiquadFilter(); f.type = p.filter; f.Q.value = p.q == null ? 0.9 : p.q; ramp(f.frequency, p.ff == null ? 1200 : p.ff, t); return f; };
95
+
96
+ // primitive: oscillator voice. f may be a value or [[dt, hz], ...]
97
+ function voice(time, p) {
98
+ const dur = p.dur || 0.2; if (skip(time, dur)) return; const t = at(time);
99
+ const osc = ac.createOscillator(); osc.type = p.type || 'sine';
100
+ ramp(osc.frequency, Array.isArray(p.f) ? p.f.map(([d, v]) => [d, hz(v)]) : hz(p.f || 440), t);
101
+ const g = ac.createGain(); env(g, t, level(p, 0.3), dur, p.a, p.hold);
102
+ const f = filt(p, t); if (f) { osc.connect(f); f.connect(g); } else osc.connect(g);
103
+ route(g, p, t); osc.start(t); osc.stop(t + dur + 0.03);
104
+ }
105
+ // primitive: noise voice
106
+ function hiss(time, p) {
107
+ const dur = p.dur || 0.2; if (skip(time, dur)) return; const t = at(time);
108
+ const src = ac.createBufferSource(); src.buffer = NB[p.color || 'white']; src.loop = true; if (p.rate) src.playbackRate.value = p.rate;
109
+ const g = ac.createGain(); env(g, t, level(p, 0.3), dur, p.a, p.hold);
110
+ const f = filt(p, t); if (f) { src.connect(f); f.connect(g); } else src.connect(g);
111
+ route(g, p, t); src.start(t, CM.rng('off' + time)() * 1.5); src.stop(t + dur + 0.03);
112
+ }
113
+ function duck(t, depth = 0.5, hold = 0.25) { DUCKS.push({ t, depth, hold }); }
114
+
115
+ // ---------------- named sounds (creator-edit palette) ----------------
116
+ const S = {
117
+ // text slam: a tight low thump with a short papery crack
118
+ slam: (t, o) => { voice(t, { f: [[0, 150], [0.12, 52]], dur: 0.2, gain: 0.42 * o.g, pan: o.pan, soft: o.soft }); hiss(t, { dur: 0.07, filter: 'lowpass', ff: 2600, gain: 0.22 * o.g, pan: o.pan, soft: o.soft }); hiss(t, { dur: 0.012, filter: 'highpass', ff: 5000, gain: 0.12 * o.g, pan: o.pan, soft: o.soft }); },
119
+ // big drop: sub boom with a dark tail
120
+ boom: (t, o) => { voice(t, { f: [[0, 90], [0.08, 52], [1.6, 34]], dur: 1.7, gain: 0.7 * o.g, a: 0.006, hold: 0.12 }); hiss(t, { color: 'brown', dur: 0.6, filter: 'lowpass', ff: [[0, 600], [0.5, 90]], gain: 0.35 * o.g, verb: 0.3 }); duck(t, 0.7, 0.35); },
121
+ pop: (t, o) => { const f = o.f || 760; voice(t, { f: [[0, f * 1.7], [0.05, f]], dur: 0.08, gain: 0.22 * o.g, pan: o.pan, soft: o.soft }); hiss(t, { dur: 0.01, filter: 'highpass', ff: 4500, gain: 0.08 * o.g, pan: o.pan, soft: o.soft }); },
122
+ tick: (t, o) => hiss(t, { dur: 0.016, filter: 'bandpass', ff: o.f || 3800, q: 3, gain: 0.32 * o.g, pan: o.pan, soft: o.soft }),
123
+ click: (t, o) => { hiss(t, { dur: 0.012, filter: 'bandpass', ff: 3200, q: 2, gain: 0.35 * o.g, pan: o.pan }); hiss(t + 0.045, { dur: 0.01, filter: 'bandpass', ff: 2400, q: 2, gain: 0.22 * o.g, pan: o.pan }); },
124
+ whoosh: (t, o) => { const d = o.dur || 0.35, dir = o.dir || 1; hiss(t, { color: 'pink', dur: d, filter: 'bandpass', ff: [[0, 260], [d * 0.55, o.top || 3200], [d, 520]], q: 0.75, gain: 0.5 * o.g, a: d * 0.55, pan: [-0.7 * dir, 0.7 * dir], verb: 0.12, soft: o.soft }); hiss(t + d * 0.2, { dur: d * 0.6, filter: 'highpass', ff: [[0, 3000], [d * 0.6, 7000]], gain: 0.08 * o.g, a: d * 0.3, pan: [-0.4 * dir, 0.5 * dir], soft: o.soft }); },
125
+ riser: (t, o) => { const d = o.dur || 0.8; hiss(t, { color: 'pink', dur: d, filter: 'bandpass', ff: [[0, 300], [d, 5200]], q: 1.1, gain: 0.32 * o.g, a: d * 0.96, verb: 0.2, soft: o.soft }); voice(t, { type: 'sawtooth', f: [[0, 110], [d, 440]], dur: d, gain: 0.06 * o.g, a: d * 0.95, filter: 'lowpass', ff: [[0, 400], [d, 2400]], soft: o.soft }); },
126
+ glitch: (t, o) => { const r = CM.rng('gl' + t); for (let i = 0; i < 9; i++) voice(t + i * 0.021, { type: 'square', f: 180 + r() * 2200, dur: 0.018, gain: 0.07 * o.g, a: 0.001, hold: 0.012, pan: r() * 1.4 - 0.7 }); hiss(t, { dur: 0.2, filter: 'bandpass', ff: [[0, 4000], [0.2, 900]], q: 0.7, gain: 0.18 * o.g }); hiss(t + 0.1, { dur: 0.05, color: 'brown', gain: 0.25 * o.g }); },
127
+ ping: (t, o) => { voice(t, { f: 'E6', dur: 0.5, gain: 0.16 * o.g, a: 0.003, verb: 0.3, pan: o.pan }); voice(t + 0.11, { f: 'B6', dur: 0.7, gain: 0.14 * o.g, a: 0.003, verb: 0.35, pan: o.pan }); },
128
+ send: (t, o) => { voice(t, { f: [[0, 520], [0.11, 1500]], dur: 0.13, gain: 0.14 * o.g, pan: o.pan }); hiss(t, { dur: 0.1, filter: 'highpass', ff: [[0, 2000], [0.1, 6000]], gain: 0.07 * o.g, pan: o.pan }); },
129
+ keys: (t, o) => { const r = CM.rng('k' + t), end = t + (o.dur || 0.4); for (let x = t; x < end; x += 0.055 + r() * 0.035) { hiss(x, { dur: 0.014, filter: 'bandpass', ff: 3200 + r() * 900, q: 3, gain: 0.2 * o.g, pan: o.pan, soft: true }); voice(x, { f: 1900 + r() * 300, dur: 0.012, gain: 0.025 * o.g, pan: o.pan, soft: true }); } },
130
+ // highlighter / marker swipe: grainy felt drag
131
+ marker: (t, o) => { const d = o.dur || 0.25, r = CM.rng('m' + t); for (let x = 0; x < d; x += 0.022 + r() * 0.012) hiss(t + x, { dur: 0.03, filter: 'bandpass', ff: 1700 + r() * 900, q: 1.4, gain: 0.16 * o.g * (0.6 + r() * 0.5), pan: o.pan, soft: o.soft }); },
132
+ strike: (t, o) => { hiss(t, { dur: 0.15, filter: 'bandpass', ff: [[0, 1200], [0.15, 3200]], q: 1.2, gain: 0.3 * o.g, a: 0.02, pan: o.pan }); },
133
+ tapeStop: (t, o) => { voice(t, { type: 'sawtooth', f: [[0, 196], [0.62, 18]], dur: 0.66, gain: 0.22 * o.g, a: 0.004, hold: 0.3, filter: 'lowpass', ff: [[0, 1800], [0.6, 160]] }); voice(t, { f: [[0, 98], [0.62, 12]], dur: 0.66, gain: 0.3 * o.g, a: 0.004, hold: 0.3 }); hiss(t, { color: 'brown', dur: 0.5, filter: 'lowpass', ff: [[0, 900], [0.5, 120]], gain: 0.18 * o.g }); },
134
+ stamp: (t, o) => { voice(t, { f: [[0, 160], [0.18, 48]], dur: 0.28, gain: 0.6 * o.g }); hiss(t, { dur: 0.06, filter: 'bandpass', ff: 1300, q: 1.1, gain: 0.4 * o.g }); hiss(t + 0.004, { dur: 0.014, filter: 'highpass', ff: 4200, gain: 0.25 * o.g }); },
135
+ shimmer: (t, o) => { ['C6', 'E6', 'G6', 'B6', 'D7', 'E7'].forEach((n, i) => voice(t + i * 0.035, { f: n, dur: 0.75, gain: 0.045 * o.g, a: 0.003, verb: 0.45, pan: (i % 2 ? 0.35 : -0.35) })); },
136
+ sparkle: (t, o) => { const r = CM.rng('sp' + t); for (let i = 0; i < 5; i++) voice(t + i * 0.05, { f: 2400 + r() * 2400, dur: 0.25, gain: 0.03 * o.g, a: 0.002, verb: 0.4, pan: r() * 1.2 - 0.6 }); },
137
+ };
138
+ function play(name, t, opts = {}) { const fn = S[name]; if (!fn) throw new Error('unknown sound ' + name); fn(t, Object.assign({}, opts, { g: opts.gain == null ? 1 : opts.gain })); }
139
+
140
+ // ---------------- music: half-time beat with bass and soft pad ----------------
141
+ function music(t0, t1, p = {}) {
142
+ const bpm = p.bpm || 98, step = 60 / bpm / 4, bar = step * 16, g = p.gain == null ? 1 : p.gain;
143
+ const roots = p.roots || ['A1', 'F1', 'C2', 'G1'];
144
+ const pads = p.pads || [['A3', 'C4', 'E4'], ['F3', 'A3', 'C4'], ['C4', 'E4', 'G4'], ['G3', 'B3', 'D4']];
145
+ let bi = 0;
146
+ for (let tb = t0; tb < t1 - 0.05; tb += bar, bi++) {
147
+ const root = roots[bi % roots.length], pad = pads[bi % pads.length], blen = Math.min(bar, t1 - tb);
148
+ pad.forEach((n, k) => voice(tb, { type: 'triangle', f: n, dur: blen + 0.2, gain: 0.03 * g, a: 0.3, hold: blen - 0.5, filter: 'lowpass', ff: 1100, pan: (k - 1) * 0.35, verb: 0.3, bus: 'music' }));
149
+ for (let s = 0; s < 16; s++) {
150
+ const ts = tb + s * step; if (ts >= t1 - 0.02) break;
151
+ if (s === 0 || s === 10) { voice(ts, { f: [[0, 150], [0.09, 46]], dur: 0.32, gain: 0.5 * g, bus: 'music' }); hiss(ts, { dur: 0.012, filter: 'highpass', ff: 3000, gain: 0.05 * g, bus: 'music' }); }
152
+ if (s === 4 || s === 12) { hiss(ts, { dur: 0.16, filter: 'bandpass', ff: 1700, q: 0.8, gain: 0.26 * g, verb: 0.25, bus: 'music' }); hiss(ts + 0.008, { dur: 0.05, filter: 'highpass', ff: 4500, gain: 0.1 * g, bus: 'music' }); }
153
+ if (s % 2 === 0) hiss(ts, { dur: s % 4 === 2 ? 0.05 : 0.028, filter: 'highpass', ff: 7500, gain: (s % 4 === 2 ? 0.06 : 0.035) * g, bus: 'music' });
154
+ if ([0, 3, 6, 10, 14].includes(s)) voice(ts, { type: 'triangle', f: root, dur: step * 2.6, gain: 0.2 * g, a: 0.006, hold: step, filter: 'lowpass', ff: 380, bus: 'music' });
155
+ }
156
+ }
157
+ }
158
+ function drone(t0, t1, p = {}) { const d = t1 - t0, g = p.gain == null ? 1 : p.gain; voice(t0, { f: p.note || 'A1', dur: d, gain: 0.12 * g, a: d * 0.4, hold: d * 0.4, bus: 'music' }); hiss(t0, { color: 'pink', dur: d, filter: 'bandpass', ff: 900, q: 0.5, gain: 0.03 * g, a: d * 0.5, hold: d * 0.3, bus: 'amb' }); }
159
+ function finish() {
160
+ DUCKS.sort((a, b) => a.t - b.t);
161
+ for (const e of DUCKS) { const s = at(e.t); if (s + e.hold < ac.currentTime) continue; duckIn.gain.setTargetAtTime(1 - e.depth, Math.max(s, ac.currentTime), 0.008); duckIn.gain.setTargetAtTime(1, s + e.hold, 0.14); }
162
+ }
163
+ return { play, voice, hiss, music, drone, duck, finish, hz, vo: VO };
164
+ }
165
+ function live(score, from, opts = {}) {
166
+ const ac = new (window.AudioContext || window.webkitAudioContext)();
167
+ const k = kit(ac, Object.assign({}, opts, { from: from || 0, lead: 0.04 })); score(k); k.finish();
168
+ return () => { try { ac.close(); } catch (e) {} };
169
+ }
170
+ async function render(score, dur, sr = 48000, opts = {}) {
171
+ const len = Math.ceil(dur * sr), ac = new OfflineAudioContext(2, len, sr);
172
+ const k = kit(ac, Object.assign({}, opts, { from: 0 })); score(k); k.finish();
173
+ const buf = await ac.startRendering(), L = buf.getChannelData(0), R = buf.getChannelData(1), inter = new Float32Array(len * 2);
174
+ let peak = 0; for (let i = 0; i < len; i++) { inter[2 * i] = L[i]; inter[2 * i + 1] = R[i]; peak = Math.max(peak, Math.abs(L[i]), Math.abs(R[i])); }
175
+ return { inter, peak, sr, frames: len };
176
+ }
177
+ return { kit, live, render, hz };
178
+ })();
179
+
180
+ // ---------- shared UI-style helpers (Clean Tech, Software Walkthrough, Data Story) ----------
181
+ const W = 1280, HH = 720;
182
+ const { clamp, lerp, span, rng, noise1, ez } = CM;
183
+ const rr = (c, x, y, w, h, r) => { c.beginPath(); c.roundRect(x, y, w, h, r); };
184
+ // keyframe track: K = [[t, v1, v2, ...], ...], eased in-out between keys
185
+ function track(K, t) {
186
+ if (t <= K[0][0]) return K[0].slice(1);
187
+ for (let i = 0; i < K.length - 1; i++) { const a = K[i], b = K[i + 1]; if (t <= b[0]) { const e = ez.inout3((t - a[0]) / Math.max(1e-6, b[0] - a[0])); return a.slice(1).map((v, j) => lerp(v, b[j + 1], e)); } }
188
+ return K[K.length - 1].slice(1);
189
+ }
190
+ const textW = (c, s, font) => { c.save(); c.font = font; const w = c.measureText(s).width; c.restore(); return w; };
191
+ const pxOf = (font) => parseFloat(/(\d+)px/.exec(font)[1]);
192
+ // a word rises from behind a mask line into place; returns its width
193
+ function rise(c, s, x, y, font, color, t0, t, o = {}) {
194
+ const w = textW(c, s, font); if (t < t0) return w;
195
+ const px = pxOf(font), X = o.align === 'center' ? x - w / 2 : o.align === 'right' ? x - w : x, e = ez.outExpo(span(t, t0, t0 + (o.dur || 0.5)));
196
+ c.save(); c.beginPath(); c.rect(X - 12, y - px * 1.0, w + 24, px * 1.32); c.clip();
197
+ c.globalAlpha *= Math.min(1, e * 1.6) * (o.alpha == null ? 1 : o.alpha); c.font = font; c.fillStyle = color; c.textBaseline = 'alphabetic'; c.textAlign = 'left';
198
+ c.fillText(s, X, y + (1 - e) * px * 0.9); c.restore(); return w;
199
+ }
200
+ // a line of words, each rising on its own time. words: [[text, t0, color?]]
201
+ function riseLine(c, words, x, y, font, color, t, o = {}) {
202
+ const sp = textW(c, ' ', font), ws = words.map(([s]) => textW(c, s, font)), tot = ws.reduce((a, b) => a + b, 0) + sp * (words.length - 1);
203
+ let cx = o.align === 'center' ? x - tot / 2 : x; const boxes = [];
204
+ words.forEach(([s, t0, col], i) => { boxes.push({ x: cx, w: ws[i], y }); rise(c, s, cx, y, font, col || color, t0, t, { alpha: o.alpha }); cx += ws[i] + sp; });
205
+ return boxes;
206
+ }
207
+ // pill highlight behind a word box, growing from the left
208
+ function pillBehind(c, box, font, p, color, pad = 16) {
209
+ if (p <= 0) return; const px = pxOf(font); c.save(); c.fillStyle = color; rr(c, box.x - pad, box.y - px * 0.88, (box.w + pad * 2) * ez.outExpo(p), px * 1.12, px * 0.26); c.fill(); c.restore();
210
+ }
211
+ const typed = (full, t, a, b) => full.slice(0, Math.floor(full.length * span(t, a, b)));
212
+ function softCard(c, x, y, w, h, r, o = {}) {
213
+ c.save(); c.shadowColor = o.shadow || 'rgba(13,15,40,0.13)'; c.shadowBlur = o.blur == null ? 44 : o.blur; c.shadowOffsetY = o.offY == null ? 16 : o.offY;
214
+ c.fillStyle = o.fill || '#ffffff'; rr(c, x, y, w, h, r); c.fill(); c.restore();
215
+ if (o.border !== false) { c.save(); c.strokeStyle = o.border || 'rgba(13,15,20,0.08)'; c.lineWidth = 1.5; rr(c, x, y, w, h, r); c.stroke(); c.restore(); }
216
+ }
217
+ function cursorArrow(c, x, y, s = 1.3) {
218
+ c.save(); c.translate(x, y); c.scale(s, s); c.shadowColor = 'rgba(0,0,0,0.3)'; c.shadowBlur = 6; c.shadowOffsetY = 2;
219
+ c.fillStyle = '#111'; c.strokeStyle = '#fff'; c.lineWidth = 1.6; c.beginPath(); c.moveTo(0, 0); c.lineTo(0, 22); c.lineTo(5.5, 17); c.lineTo(9.5, 26); c.lineTo(13, 24.5); c.lineTo(9, 15.5); c.lineTo(16, 15.5); c.closePath(); c.fill(); c.shadowColor = 'transparent'; c.stroke(); c.restore();
220
+ }
221
+ function checkMark(c, x, y, r, p, fill = '#18b46b') {
222
+ if (p <= 0) return; const s = ez.back(clamp(p), 2.2); c.save(); c.translate(x, y); c.scale(s, s); c.fillStyle = fill; c.beginPath(); c.arc(0, 0, r, 0, 7); c.fill();
223
+ c.strokeStyle = '#fff'; c.lineWidth = r * 0.24; c.lineCap = 'round'; c.lineJoin = 'round'; c.beginPath(); c.moveTo(-r * 0.42, 0.02 * r); c.lineTo(-r * 0.1, r * 0.32); c.lineTo(r * 0.45, -r * 0.3); c.stroke(); c.restore();
224
+ }
225
+ // scenes on transparent layers, with cuts between them
226
+ const LA = CM.layer(W, HH), LB = CM.layer(W, HH);
227
+ function sceneLayer(L, draw, t) { const x = L.getContext('2d'); x.setTransform(1, 0, 0, 1, 0, 0); x.globalAlpha = 1; x.globalCompositeOperation = 'source-over'; x.filter = 'none'; x.clearRect(0, 0, W, HH); draw(x, t); return L; }
228
+ function composite(ctx, t, SCENES, CUTS, background) {
229
+ ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.globalAlpha = 1; ctx.globalCompositeOperation = 'source-over'; ctx.filter = 'none';
230
+ background(ctx, t);
231
+ let idx = 0; for (const c of CUTS) if (t >= c.t) idx++;
232
+ const cut = CUTS.find((c) => t >= c.t && t < c.t + c.d);
233
+ if (!cut) { ctx.drawImage(sceneLayer(LA, SCENES[idx], t), 0, 0); return; }
234
+ const u = (t - cut.t) / cut.d, e = ez.inout3(u), A = sceneLayer(LA, SCENES[idx - 1], t), B = sceneLayer(LB, SCENES[idx], t);
235
+ if (cut.type === 'push') {
236
+ const dir = cut.dir || 1, s = 1 - 0.05 * Math.sin(Math.PI * u);
237
+ for (const [L, off] of [[A, -e * W * dir], [B, (1 - e) * W * dir]]) { ctx.save(); ctx.translate(640 + off, 360); ctx.scale(s, s); ctx.drawImage(L, -640, -360); ctx.restore(); }
238
+ } else if (cut.type === 'zoom') {
239
+ const z = lerp(1, 3.2, ez.in3(clamp(u / 0.65))); ctx.save(); ctx.globalAlpha = 1 - span(u, 0.38, 0.65); ctx.translate(cut.x, cut.y); ctx.scale(z, z); ctx.drawImage(A, -cut.x, -cut.y); ctx.restore();
240
+ const zb = lerp(1.1, 1, ez.out3(span(u, 0.35, 1))); ctx.save(); ctx.globalAlpha = span(u, 0.35, 0.72); ctx.translate(640, 360); ctx.scale(zb, zb); ctx.drawImage(B, -640, -360); ctx.restore();
241
+ } else if (cut.type === 'up') {
242
+ ctx.save(); ctx.globalAlpha = 1 - e; ctx.drawImage(A, 0, -60 * e); ctx.restore(); ctx.save(); ctx.globalAlpha = e; ctx.drawImage(B, 0, 70 * (1 - e)); ctx.restore();
243
+ } else { ctx.save(); ctx.globalAlpha = 1 - e; ctx.drawImage(A, 0, 0); ctx.globalAlpha = e; ctx.drawImage(B, 0, 0); ctx.restore(); }
244
+ }
245
+ // a soft synth pad and light percussion on the music bus (for scores)
246
+ function padBed(k, t0, t1, o = {}) {
247
+ const bpm = o.bpm || 90, beat = 60 / bpm, bar = beat * 4, chords = o.chords, g = o.gain == null ? 1 : o.gain;
248
+ let i = 0;
249
+ for (let tb = t0; tb < t1 - 0.1; tb += bar, i++) {
250
+ const ch = chords[i % chords.length], len = Math.min(bar, t1 - tb);
251
+ ch.forEach((n, j) => { k.voice(tb, { type: 'triangle', f: n, dur: len + 0.4, gain: 0.026 * g, a: 0.5, hold: Math.max(0, len - 0.9), filter: 'lowpass', ff: o.cut || 1300, pan: (j - (ch.length - 1) / 2) * 0.3, verb: 0.3, bus: 'music' }); });
252
+ k.voice(tb, { f: k.hz(ch[0]) / 2, dur: len + 0.2, gain: 0.05 * g, a: 0.3, hold: Math.max(0, len - 0.6), bus: 'music' });
253
+ for (let b = 0; b < 8; b++) {
254
+ const ts = tb + b * beat / 2; if (ts >= t1 - 0.05) break;
255
+ if (o.hats && ts >= o.hats[0] && ts < o.hats[1]) k.hiss(ts, { dur: b % 2 ? 0.03 : 0.045, filter: 'highpass', ff: 8000, gain: (b % 2 ? 0.014 : 0.022) * g, bus: 'music' });
256
+ if (o.kick && ts >= o.kick[0] && ts < o.kick[1] && (b === 0 || b === 4)) k.voice(ts, { f: [[0, 120], [0.09, 46]], dur: 0.26, gain: 0.16 * g, bus: 'music' });
257
+ }
258
+ }
259
+ }
260
+ // =====================================================================================================
261
+ // STYLE: Data Story
262
+ // A calm, editorial data explainer: deep ink background, one warm accent per idea, numbers that count,
263
+ // a line chart that draws itself through real milestones, bars that grow and get sonified, and a plain-language
264
+ // takeaway with the source always on screen. Demo: "How we got to 8 billion" (UN World Population Prospects).
265
+ // =====================================================================================================
266
+ const C = { bg: '#0f141c', ink: '#eef1f5', mute: '#7d8796', faint: '#667183', grid: 'rgba(238,241,245,0.07)', coral: '#ff6b5b', teal: '#3cc6b8', sand: '#f2c14e' };
267
+ const HEAD = (px, w = 800) => `${w} ${px}px "Archivo", system-ui, sans-serif`, MONO = (px, w = 500) => `${w} ${px}px "IBM Plex Mono", ui-monospace, monospace`;
268
+
269
+ // ---------- the data (UN World Population Prospects; 1800 to 1900 values are historical estimates) ----------
270
+ const POP = [[1800, 0.98], [1804, 1.0], [1850, 1.26], [1900, 1.6], [1927, 2], [1950, 2.5], [1960, 3], [1974, 4], [1987, 5], [1999, 6], [2011, 7], [2022, 8]];
271
+ const BILLIONS = [1804, 1927, 1960, 1974, 1987, 1999, 2011, 2022];
272
+ const YEARS_PER = [123, 33, 14, 13, 12, 12, 11];
273
+
274
+ // ---------- the timeline: picture and sound both read these ----------
275
+ var TL = {
276
+ D: 21,
277
+ hook: { tag: 0.1, count: [0.3, 2.4], sub: 2.6, shrink: [3.6, 4.3] },
278
+ line: { title: [3.9, 4.05], axes: [3.9, 4.5], draw: [4.5, 7.9], span: [8.1, 8.9], note: 8.5 },
279
+ bars: { title: 10.55, base: [10.6, 11.0], bar0: 11.0, gap: 0.26, ring: 13.0, ringNote: 13.4 },
280
+ cmp: { a: 15.15, b: 15.55, x: [16.1, 16.9], sub: 17.0 },
281
+ take: { w1: [18.55, 18.7, 18.85], w2: [19.1, 19.25, 19.4], pill: 19.65, note: 20.1 },
282
+ };
283
+ var CUTS = [{ t: 10.0, d: 0.6, type: 'up' }, { t: 14.6, d: 0.5, type: 'push', dir: 1 }, { t: 18.0, d: 0.55, type: 'up' }];
284
+ const X = (yr) => 150 + (yr - 1800) / (2025 - 1800) * 1000, Y = (b) => 600 - (b / 8.5) * 440;
285
+ // monotone cubic through the data so the curve never overshoots a real value
286
+ const SLOPES = (() => { const n = POP.length, d = [], m = []; for (let i = 0; i < n - 1; i++) d.push((POP[i + 1][1] - POP[i][1]) / (POP[i + 1][0] - POP[i][0])); m[0] = d[0]; m[n - 1] = d[n - 2]; for (let i = 1; i < n - 1; i++) m[i] = d[i - 1] * d[i] <= 0 ? 0 : (2 * d[i - 1] * d[i]) / (d[i - 1] + d[i]); return m; })();
287
+ function popAt(yr) {
288
+ if (yr <= POP[0][0]) return POP[0][1]; for (let i = 0; i < POP.length - 1; i++) { const [x0, y0] = POP[i], [x1, y1] = POP[i + 1]; if (yr <= x1) { const h = x1 - x0, s = (yr - x0) / h, s2 = s * s, s3 = s2 * s; return (2 * s3 - 3 * s2 + 1) * y0 + (s3 - 2 * s2 + s) * h * SLOPES[i] + (-2 * s3 + 3 * s2) * y1 + (s3 - s2) * h * SLOPES[i + 1]; } }
289
+ return POP[POP.length - 1][1];
290
+ }
291
+ const yearAt = (t) => lerp(1800, 2022, ez.inout3(span(t, TL.line.draw[0], TL.line.draw[1])));
292
+ const countAt = (t) => Math.round(8e9 * ez.outExpo(span(t, TL.hook.count[0], TL.hook.count[1])) / 1e6) * 1e6;
293
+ const fmtN = (n) => n.toLocaleString('en-US');
294
+
295
+ // ---------- background ----------
296
+ function background(c, t) {
297
+ c.fillStyle = C.bg; c.fillRect(0, 0, W, HH);
298
+ const g = c.createRadialGradient(280 + Math.sin(t * 0.2) * 50, 110, 0, 280, 110, 900); g.addColorStop(0, 'rgba(255,107,91,0.075)'); g.addColorStop(1, 'rgba(255,107,91,0)'); c.fillStyle = g; c.fillRect(0, 0, W, HH);
299
+ const g2 = c.createRadialGradient(1100, 650, 0, 1100, 650, 700); g2.addColorStop(0, 'rgba(60,198,184,0.05)'); g2.addColorStop(1, 'rgba(60,198,184,0)'); c.fillStyle = g2; c.fillRect(0, 0, W, HH);
300
+ }
301
+ function source(c, t) { c.save(); c.globalAlpha = 0.9 * span(t, 0.4, 1.0); c.font = MONO(16); c.fillStyle = '#5f6a7a'; c.fillText('Source: UN World Population Prospects', 150, 690); c.restore(); }
302
+ function tag(c, s, x, y, t0, t, col = C.coral, align = 'left') { c.save(); c.globalAlpha *= span(t, t0, t0 + 0.3); c.font = MONO(22, 600); c.fillStyle = col; c.letterSpacing = '4px'; c.textAlign = align; c.fillText(s, x, y); c.restore(); }
303
+ function cam(c, t, a, b) { const z = 1 + 0.022 * ez.inout3(span(t, a, b)); c.translate(640, 360); c.scale(z, z); c.translate(-640, -360); }
304
+
305
+ // ---------- scene 0+1: the counter that becomes a chart ----------
306
+ function sceneLine(c, t) {
307
+ const H = TL.hook, L = TL.line;
308
+ // counter: big at center, then shrinks into a label at the top right
309
+ const sh = ez.inout3(span(t, H.shrink[0], H.shrink[1])), F = HEAD(128), str = fmtN(countAt(t)), fw = textW(c, fmtN(8e9), F);
310
+ c.save(); c.globalAlpha = 1 - span(t, H.shrink[0] - 0.3, H.shrink[0]); tag(c, 'WORLD POPULATION', 640, 250, H.tag, t, C.coral, 'center'); c.restore();
311
+ { const s = lerp(1, 0.25, sh), cx = lerp(640, 1150 - fw * 0.25 / 2, sh), cy = lerp(390, 118, sh);
312
+ c.save(); c.translate(cx, cy); c.scale(s, s); c.font = F; c.textAlign = 'center'; c.fillStyle = C.ink; c.shadowColor = 'rgba(255,107,91,0.25)'; c.shadowBlur = 40 * (1 - sh); c.fillText(str, 0, 0); c.restore(); }
313
+ if (t > H.sub) { c.save(); c.globalAlpha = span(t, H.sub, H.sub + 0.4) * (1 - span(t, H.shrink[0] - 0.2, H.shrink[0] + 0.1)); rise(c, 'people. We crossed it in November 2022.', 640, 470, MONO(28), C.mute, H.sub, t, { align: 'center' }); c.restore(); }
314
+ if (t < L.axes[0]) return;
315
+ c.save(); cam(c, t, L.axes[0], 10);
316
+ // title + gridlines + axis labels
317
+ tag(c, 'WORLD POPULATION', 150, 70, L.title[0], t);
318
+ riseLine(c, [['How', L.title[0]], ['we', L.title[0] + 0.06], ['got', L.title[0] + 0.12], ['to', L.title[0] + 0.18], ['8', L.title[1] + 0.2], ['billion', L.title[1] + 0.26]], 150, 128, HEAD(50), C.ink, t);
319
+ const ax = ez.outExpo(span(t, L.axes[0], L.axes[1]));
320
+ [2, 4, 6, 8].forEach((b, i) => { const a = span(t, L.axes[0] + i * 0.06, L.axes[0] + 0.3 + i * 0.06); c.strokeStyle = C.grid; c.lineWidth = 1; c.beginPath(); c.moveTo(150, Y(b)); c.lineTo(150 + 1000 * ax, Y(b)); c.stroke(); c.save(); c.globalAlpha = a; c.font = MONO(16); c.fillStyle = C.faint; c.textAlign = 'right'; c.fillText(b + 'B', 136, Y(b) + 5); c.restore(); });
321
+ c.strokeStyle = 'rgba(238,241,245,0.3)'; c.lineWidth = 2; c.beginPath(); c.moveTo(150, 600); c.lineTo(150 + 1000 * ax, 600); c.stroke();
322
+ [1800, 1850, 1900, 1950, 2000].forEach((yr, i) => { c.save(); c.globalAlpha = span(t, L.axes[0] + 0.2 + i * 0.05, L.axes[0] + 0.5 + i * 0.05); c.font = MONO(16); c.fillStyle = C.faint; c.textAlign = 'center'; c.fillText(String(yr), X(yr), 628); c.restore(); });
323
+ // the line, area and head
324
+ const yr = yearAt(t);
325
+ if (t > L.draw[0]) {
326
+ const pts = []; for (let y = 1800; y < yr; y += 1) pts.push([X(y), Y(popAt(y))]); pts.push([X(yr), Y(popAt(yr))]);
327
+ const ag = c.createLinearGradient(0, 180, 0, 600); ag.addColorStop(0, 'rgba(255,107,91,0.28)'); ag.addColorStop(1, 'rgba(255,107,91,0)');
328
+ c.fillStyle = ag; c.beginPath(); c.moveTo(X(1800), 600); pts.forEach(([x, y]) => c.lineTo(x, y)); c.lineTo(pts[pts.length - 1][0], 600); c.closePath(); c.fill();
329
+ c.strokeStyle = C.coral; c.lineWidth = 4; c.lineJoin = 'round'; c.lineCap = 'round'; c.beginPath(); pts.forEach(([x, y], i) => (i ? c.lineTo(x, y) : c.moveTo(x, y))); c.stroke();
330
+ // the last 95 years glow sand once the span lands
331
+ const sp = span(t, L.span[0], L.span[1]);
332
+ if (sp > 0) { const yEnd = lerp(1927, 2022, ez.inout3(sp)); c.strokeStyle = C.sand; c.lineWidth = 6; c.shadowColor = 'rgba(242,193,78,0.6)'; c.shadowBlur = 18; c.beginPath(); for (let y = 1927; y <= yEnd; y += 1) { const P = [X(y), Y(popAt(y))]; y === 1927 ? c.moveTo(...P) : c.lineTo(...P); } c.stroke(); c.shadowBlur = 0; c.shadowColor = 'transparent'; }
333
+ // milestone dots
334
+ BILLIONS.forEach((by, i) => { if (yr < by) return; const passT = TL.line.draw[0] + inverseYear(by), s = ez.back(span(t, passT, passT + 0.3), 2.4); c.save(); c.translate(X(by), Y(i + 1)); c.scale(s, s); c.fillStyle = C.bg; c.strokeStyle = sp > 0 && by >= 1927 ? C.sand : C.coral; c.lineWidth = 3; c.beginPath(); c.arc(0, 0, 7, 0, 7); c.fill(); c.stroke(); c.restore(); });
335
+ // labels on the key milestones
336
+ [[0, '1B · 1804', -1], [1, '2B · 1927', 1], [7, '8B · 2022', 1]].forEach(([i, s, side]) => { const by = BILLIONS[i]; if (yr < by) return; const passT = TL.line.draw[0] + inverseYear(by); c.save(); c.globalAlpha = span(t, passT + 0.1, passT + 0.4); c.font = MONO(18, 600); c.fillStyle = C.ink; c.textAlign = side > 0 ? 'right' : 'left'; c.fillText(s, X(by) + (side > 0 ? -16 : 8), Y(i + 1) - 18); c.restore(); });
337
+ if (yr < 2022) { const hx = X(yr), hy = Y(popAt(yr)), g = c.createRadialGradient(hx, hy, 0, hx, hy, 30); g.addColorStop(0, 'rgba(255,107,91,0.6)'); g.addColorStop(1, 'rgba(255,107,91,0)'); c.fillStyle = g; c.beginPath(); c.arc(hx, hy, 30, 0, 7); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(hx, hy, 5, 0, 7); c.fill(); }
338
+ if (t > L.note) { const e = ez.outExpo(span(t, L.note, L.note + 0.5)); c.save(); c.globalAlpha = e; c.translate(0, (1 - e) * 14); c.font = HEAD(40, 800); c.fillStyle = C.sand; c.fillText('+6 billion', 560, 300); c.font = MONO(22); c.fillStyle = C.ink; c.fillText('in just 95 years', 560, 336); c.restore(); }
339
+ }
340
+ c.restore();
341
+ }
342
+ // time (seconds into the draw) at which the line reaches a given year
343
+ function inverseYear(by) { let lo = 0, hi = TL.line.draw[1] - TL.line.draw[0]; for (let k = 0; k < 30; k++) { const m = (lo + hi) / 2; if (yearAt(TL.line.draw[0] + m) < by) lo = m; else hi = m; } return hi; }
344
+
345
+ // ---------- scene 2: years to add each billion ----------
346
+ const BAR = { base: 600, scale: 380 / 123, x: (i) => 190 + i * 140, w: 92 };
347
+ const barT = (i) => TL.bars.bar0 + i * TL.bars.gap;
348
+ function sceneBars(c, t) {
349
+ const B = TL.bars; cam(c, t, 10, 14.6);
350
+ tag(c, 'EACH NEW BILLION', 150, 92, B.title - 0.1, t);
351
+ riseLine(c, [['Years', B.title], ['to', B.title + 0.06], ['add', B.title + 0.12], ['each', B.title + 0.18], ['billion', B.title + 0.24]], 150, 150, HEAD(52), C.ink, t);
352
+ c.strokeStyle = 'rgba(238,241,245,0.35)'; c.lineWidth = 2; c.beginPath(); c.moveTo(150, BAR.base); c.lineTo(150 + 1030 * ez.outExpo(span(t, B.base[0], B.base[1])), BAR.base); c.stroke();
353
+ YEARS_PER.forEach((v, i) => {
354
+ const t0 = barT(i), p = ez.outExpo(span(t, t0, t0 + 0.55)), x = BAR.x(i), h = v * BAR.scale * p; if (p <= 0) return;
355
+ const col = i === 0 ? C.coral : i === 6 ? C.sand : C.teal; c.fillStyle = col; rr(c, x, BAR.base - h, BAR.w, h, [8, 8, 0, 0]); c.fill();
356
+ c.save(); c.globalAlpha = span(t, t0 + 0.2, t0 + 0.45); c.font = MONO(30, 600); c.fillStyle = C.ink; c.textAlign = 'center'; c.fillText(String(Math.round(v * p)), x + BAR.w / 2, BAR.base - h - 14);
357
+ c.font = MONO(26); c.fillStyle = C.mute; c.fillText(`${i + 2}B`, x + BAR.w / 2, BAR.base + 40); c.restore();
358
+ });
359
+ const r = span(t, B.ring, B.ring + 0.45); if (r > 0) { const x = BAR.x(6) + BAR.w / 2, y = BAR.base - 11 * BAR.scale - 30; c.strokeStyle = C.sand; c.lineWidth = 4; c.lineCap = 'round'; c.beginPath(); c.arc(x, y, 60, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * ez.outExpo(r)); c.stroke(); }
360
+ if (t > B.ringNote) { const e = ez.outExpo(span(t, B.ringNote, B.ringNote + 0.5)); c.save(); c.globalAlpha = e; c.font = MONO(22); c.fillStyle = C.sand; c.textAlign = 'right'; c.fillText('the 8th billion took 11 years', BAR.x(6) - 18 + (1 - e) * 20, BAR.base - 11 * BAR.scale - 104); c.restore(); }
361
+ }
362
+ // ---------- scene 3: the comparison ----------
363
+ function cmpCard(c, x, label, num, col, t0, t) {
364
+ const e = ez.outExpo(span(t, t0, t0 + 0.55)); if (e <= 0) return; c.save(); c.globalAlpha = e; c.translate(0, (1 - e) * 40);
365
+ c.fillStyle = 'rgba(238,241,245,0.04)'; rr(c, x, 190, 380, 340, 24); c.fill(); c.strokeStyle = 'rgba(238,241,245,0.10)'; c.lineWidth = 1.5; rr(c, x, 190, 380, 340, 24); c.stroke();
366
+ c.font = MONO(22, 600); c.fillStyle = C.mute; c.letterSpacing = '3px'; c.fillText(label, x + 36, 246); c.letterSpacing = '0px';
367
+ const n = Math.round(num * ez.out3(span(t, t0 + 0.1, t0 + 0.8))); c.font = HEAD(150); c.fillStyle = col; c.fillText(String(n), x + 30, 420);
368
+ c.font = MONO(28); c.fillStyle = C.ink; c.fillText('years', x + 38, 478); c.restore();
369
+ }
370
+ function sceneCmp(c, t) {
371
+ const K = TL.cmp; cam(c, t, 14.6, 18);
372
+ cmpCard(c, 100, '1B → 2B', 123, C.coral, K.a, t); cmpCard(c, 800, '7B → 8B', 11, C.sand, K.b, t);
373
+ const k = span(t, K.x[0], K.x[1]); if (k > 0) {
374
+ const n = Math.max(1, Math.round(11 * ez.out3(k))), pop = t > K.x[1] ? 1 + 0.12 * Math.sin(Math.PI * span(t, K.x[1], K.x[1] + 0.25)) : 1;
375
+ c.save(); c.translate(640, 380); c.scale(pop, pop); c.font = HEAD(104); c.textAlign = 'center'; c.shadowColor = 'rgba(242,193,78,0.45)'; c.shadowBlur = 36; c.fillStyle = C.sand; c.globalAlpha = Math.min(1, k * 3); c.fillText(n + '×', 0, 0); c.restore();
376
+ }
377
+ if (t > K.sub) { c.save(); c.globalAlpha = ez.outExpo(span(t, K.sub, K.sub + 0.4)); c.font = MONO(24); c.fillStyle = C.ink; c.textAlign = 'center'; c.fillText('faster', 640, 428); c.restore(); }
378
+ }
379
+ // ---------- scene 4: takeaway ----------
380
+ function sceneTake(c, t) {
381
+ const K = TL.take; cam(c, t, 18, 21); const F = HEAD(88);
382
+ riseLine(c, [['The', K.w1[0]], ['fastest', K.w1[1]], ['growth', K.w1[2]]], 640, 320, F, C.ink, t, { align: 'center' });
383
+ const p = span(t, K.pill, K.pill + 0.4), on = p > 0.6, b = riseLine(c, [['is', K.w2[0]], ['behind', K.w2[1], on ? C.bg : C.ink], ['us.', K.w2[2], on ? C.bg : C.ink]], 640, 430, F, C.ink, t, { align: 'center' });
384
+ if (p > 0) { const box = { x: b[1].x, w: b[2].x + b[2].w - b[1].x, y: 430 }; pillBehind(c, box, F, p, C.sand, 18); rise(c, 'behind', b[1].x, 430, F, C.bg, K.w2[1], t); rise(c, 'us.', b[2].x, 430, F, C.bg, K.w2[2], t); }
385
+ if (t > K.note) { const e = ez.outExpo(span(t, K.note, K.note + 0.5)); c.save(); c.globalAlpha = e; c.font = MONO(24); c.fillStyle = C.mute; c.textAlign = 'center'; c.fillText('Growth peaked in the 1960s at about 2% a year.', 640, 540 + (1 - e) * 14); c.restore(); }
386
+ }
387
+ var SCENES = [sceneLine, sceneBars, sceneCmp, sceneTake];
388
+ function explainer(ctx, t) {
389
+ composite(ctx, t, SCENES, CUTS, background);
390
+ source(ctx, t);
391
+ const v = ctx.createRadialGradient(640, 360, 400, 640, 360, 860); v.addColorStop(0, 'rgba(0,0,0,0)'); v.addColorStop(1, 'rgba(0,0,0,0.3)'); ctx.fillStyle = v; ctx.fillRect(0, 0, W, HH);
392
+ }
393
+
394
+ // =================================== SOUND SCORE ===================================
395
+ // Thoughtful and warm: a low pad with a soft pulse, a ticking counter, a pluck for every billion the line passes
396
+ // (pitch climbs with the population), bars that sound their height, a bright swipe for the ring and the pill,
397
+ // a deep hit for the 11x, and an open chord to land the takeaway.
398
+ function scoreSound(k) {
399
+ { const k0 = k, G = 2.0; k = Object.assign({}, k0, { voice: (t, p) => k0.voice(t, Object.assign({}, p, { gain: (p.gain == null ? 0.3 : p.gain) * G })), hiss: (t, p) => k0.hiss(t, Object.assign({}, p, { gain: (p.gain == null ? 0.3 : p.gain) * G })), play: (n, t, o = {}) => k0.play(n, t, Object.assign({}, o, { gain: (o.gain == null ? 1 : o.gain) * G })) }); }
400
+ const H = TL.hook, L = TL.line, B = TL.bars, K = TL.cmp, T = TL.take;
401
+ const rise1 = (t, g = 1, pan = 0) => { k.voice(t, { f: [[0, 600], [0.05, 900]], dur: 0.08, gain: 0.06 * g, pan, soft: true }); k.hiss(t, { dur: 0.012, filter: 'highpass', ff: 5500, gain: 0.04 * g, pan, soft: true }); };
402
+ const pluck = (t, f, g = 1, pan = 0) => { k.voice(t, { type: 'triangle', f, dur: 0.7, gain: 0.09 * g, a: 0.003, verb: 0.35, pan }); k.voice(t, { f: f * 2, dur: 0.3, gain: 0.03 * g, a: 0.002, pan }); };
403
+ const swipe = (t, pan = 0) => { k.hiss(t, { dur: 0.26, filter: 'bandpass', ff: [[0, 2000], [0.24, 5600]], q: 1.2, gain: 0.12, a: 0.08, pan }); k.voice(t + 0.12, { f: 'B5', dur: 0.6, gain: 0.05, verb: 0.35, pan }); };
404
+ const scale = [196, 220, 247, 294, 330, 392, 440, 494, 587, 659];
405
+ padBed(k, 0.1, TL.D - 0.2, { bpm: 84, gain: 1, cut: 1100, chords: [['A2', 'E3', 'G3', 'C4'], ['F2', 'C3', 'E3', 'A3'], ['C3', 'G3', 'B3', 'E4'], ['G2', 'D3', 'F#3', 'B3']], hats: [4.5, 18.0], kick: [10.0, 18.0] });
406
+ // counter
407
+ k.play('tick', H.tag, { f: 3400, gain: 0.3, soft: true });
408
+ { let last = -1; for (let t = H.count[0]; t <= H.count[1]; t += 1 / 30) { const v = Math.floor(countAt(t) / 2e8); if (v !== last) { k.play('tick', t, { f: 1800 + v * 70, gain: 0.32, soft: true }); last = v; } } }
409
+ k.play('pop', H.count[1], { f: 620, gain: 0.5 }); k.voice(H.count[1] + 0.02, { f: 'A5', dur: 1.2, gain: 0.05, a: 0.003, verb: 0.45 });
410
+ rise1(H.sub, 0.7); k.play('whoosh', H.shrink[0], { dur: 0.6, gain: 0.35, dir: 1, top: 1800 });
411
+ // chart
412
+ [0, 0.06, 0.12, 0.18, 0.46, 0.52].forEach((d) => rise1(L.title[0] + d, 0.8, -0.4));
413
+ k.hiss(L.axes[0], { dur: 0.6, filter: 'bandpass', ff: [[0, 900], [0.5, 2600]], q: 1, gain: 0.05, a: 0.2, soft: true });
414
+ k.hiss(L.draw[0], { color: 'pink', dur: L.draw[1] - L.draw[0], filter: 'bandpass', ff: [[0, 300], [3.4, 1400]], q: 1.2, gain: 0.04, a: 1.2, pan: [-0.5, 0.6], soft: true });
415
+ BILLIONS.forEach((by, i) => pluck(L.draw[0] + inverseYear(by), scale[i + 1], 1, -0.6 + i * 0.17));
416
+ swipe(L.span[0], 0.2); k.play('pop', L.note, { f: 700, gain: 0.4 });
417
+ // bars
418
+ k.play('whoosh', CUTS[0].t, { dur: 0.55, gain: 0.45, dir: 1, top: 2000 });
419
+ [0, 0.06, 0.12, 0.18, 0.24].forEach((d) => rise1(B.title + d, 0.8));
420
+ YEARS_PER.forEach((v, i) => pluck(barT(i) + 0.03, 196 * Math.pow(2, (1 - v / 123) * 1.6), 1.1, -0.6 + i * 0.2));
421
+ swipe(B.ring, 0.6); k.play('tick', B.ringNote, { f: 3000, gain: 0.3, soft: true });
422
+ // comparison
423
+ k.play('whoosh', CUTS[1].t, { dur: 0.5, gain: 0.5, dir: 1, top: 2400 });
424
+ k.play('pop', K.a + 0.05, { f: 520, gain: 0.45, pan: -0.5 }); k.play('pop', K.b + 0.05, { f: 780, gain: 0.45, pan: 0.5 });
425
+ for (let i = 1; i <= 11; i++) { const tt = K.x[0] + (i / 11) * (K.x[1] - K.x[0]) * 0.8; k.play('tick', tt, { f: 2000 + i * 160, gain: 0.35, soft: true }); }
426
+ k.play('boom', K.x[1], { gain: 0.5 }); k.voice(K.x[1] + 0.02, { f: 'E6', dur: 1.2, gain: 0.05, a: 0.003, verb: 0.45 });
427
+ // takeaway
428
+ k.play('whoosh', CUTS[2].t, { dur: 0.5, gain: 0.4, dir: -1, top: 1800 });
429
+ T.w1.concat(T.w2).forEach((t) => rise1(t)); swipe(T.pill, 0);
430
+ ['A3', 'E4', 'G4', 'C5', 'E5'].forEach((n, i) => k.voice(T.pill + 0.18 + i * 0.07, { f: n, dur: 2.0, gain: 0.045, a: 0.01, verb: 0.45, pan: (i - 2) * 0.22 }));
431
+ rise1(T.note, 0.6);
432
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "data-story",
3
+ "title": "Data Story",
4
+ "family": "Data",
5
+ "fonts": [
6
+ "800 60px \"Archivo\"",
7
+ "600 30px \"Archivo\"",
8
+ "500 30px \"IBM Plex Mono\"",
9
+ "600 30px \"IBM Plex Mono\""
10
+ ],
11
+ "demo_duration": 21.0,
12
+ "demo_still": 7.0,
13
+ "has_aroll": false
14
+ }
@@ -0,0 +1,38 @@
1
+ # Data Story (Data)
2
+
3
+ ## Style card
4
+ Name: Data Story
5
+ Family: Data
6
+ Look: A calm, editorial data explainer: deep ink background, one warm accent per idea (coral, teal, sand), Archivo headlines, IBM Plex Mono numbers and labels, charts that draw themselves to scale, and the source always on screen.
7
+ How it explains: Numbers count up, then shrink into a label as the chart arrives. A line draws itself through the real data with dots popping at each milestone, the part that matters is re-traced in sand, bars grow one by one and get ringed, two cards set the key comparison side by side, and a plain-language takeaway lands with a sand pill.
8
+
9
+ ## The look
10
+ - Data first: use real, sourced numbers for my topic and plot them to scale (no invented values, no cropped axes that exaggerate). Put a short "Source: ..." line at the bottom left of every frame. If a figure is an estimate, say so in the narration or a label. If you cannot verify a number, ask me instead of guessing.
11
+ - Surface: deep ink (#0f141c) with a faint coral glow top left and a faint teal glow bottom right, very subtle gridlines, and a vignette.
12
+ - Type: Archivo 800 for headlines and big numbers, IBM Plex Mono for every label, axis value, unit and the source line, and a small coral mono tag in caps above each headline. Ink #eef1f5, muted #7d8796.
13
+ - Color has meaning: one accent per idea. Coral for the main series, teal for supporting bars, sand (#f2c14e) for the one thing the viewer should remember. Never more than three colors on screen.
14
+ - Charts: lines draw left to right with a glowing head and a soft area fill, using a monotone curve through the real points so it never overshoots; milestone dots pop as the line passes them; bars grow from the baseline one by one with their value counting up on top; the key bar or span gets a sand ring or a sand re-trace with a glow.
15
+ - Numbers: big counters with thousands separators count up with an expo ease and lock; a comparison card pair shows two numbers side by side with a sand multiplier counting between them.
16
+ - Scene changes: soft slide-ups and clean pushes (composite with CUTS). The first number can shrink into a chart label so the opening flows into the chart without a cut.
17
+ - Takeaway: one plain sentence in Archivo with a sand pill behind the key phrase, then a single mono context line. Hold about 1.5 seconds.
18
+
19
+ ## Rules that keep it in style
20
+ - Change the story, the words and the props; do not change the medium. Draw every subject accurately for my topic and double-check any facts and numbers you show.
21
+ - Every frame is a pure function of t. Use CM.rng(seed) and CM.noise1(seed) for all randomness, never Math.random. Caching static layers is fine.
22
+ - It will be watched on phones: at 1280x720 keep headlines at 50 px or more, the takeaway at 80 px or more, key numbers at 100 px or more, and axis labels at 16 px or more. Fill the frame.
23
+ - Keep each frame fast, well under 50 ms.
24
+ - Give my topic its own demo content; do not reuse the reference's story ("How we got to 8 billion people (UN World Population Prospects)") unless my content asks for it.
25
+
26
+ ## Sound
27
+ - Bed: a low, thoughtful pad (padBed) with a soft pulse once the charts start; no big drums.
28
+ - Map events to the kit: counters "tick" only when the shown number changes, rising in pitch; each data milestone the line passes gets a soft pluck that climbs a pentatonic scale; each bar gets a pluck whose pitch follows its height; rings and pills get a bright swipe; scene changes get "whoosh"; cards get "pop"; the single biggest number lands on "boom" with a bell note; the takeaway gets an open chord.
29
+ - Full mix (no voiceover): keep the low pad with a soft pulse, at most about 3 sounds at once, and a short gap before the big comparison lands.
30
+ - Sync is everything: the sound score reads the same TL times as the picture.
31
+
32
+ ## Helpers to reuse (from the original prompt)
33
+ One self-contained HTML file (an artifact if you can), built on the reference file below:
34
+ - Keep the page, the player (play/pause, scrub, time, sound toggle, loop, Load voiceover) and the CM and SFX blocks unchanged.
35
+ - Replace the demo's story: the data (POP, BILLIONS, YEARS_PER), TL, CUTS, the X and Y scales, the scene functions, SCENES, the source line text and scoreSound(). Keep and reuse the style's helpers (the ui-kit helpers (rr, track, rise, riseLine, pillBehind, composite, padBed), background(), tag(), cam(), popAt() (monotone curve), inverseYear(), the counter and the bar, card and pill drawing patterns).
36
+ - Aim for 15 to 40 seconds unless my content needs more, and set the duration in TL.D.
37
+ - Keep window.seek(t), window.DURATION, window.scoreSound and window.SFX exposed, so the video can be rendered frame by frame.
38
+ - Load the style's fonts before the first draw, as the reference does: Archivo (500 to 800), IBM Plex Mono (500 and 600).