@autoaiai/aicut 0.1.7 → 0.1.13

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 (306) hide show
  1. package/MANUAL.md +94 -84
  2. package/package.json +1 -1
  3. package/product.json +2 -1
  4. package/skills/aicut/SKILL.md +4 -4
  5. package/skills/aicut/references/commands.md +15 -15
  6. package/skills/aicut/references/film-format.md +1 -1
  7. package/skills/aicut/references/recipes.md +4 -4
  8. package/skills/aicut/references/troubleshooting.md +2 -2
  9. package/skills/aicut-motion/SKILL.md +75 -9
  10. package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
  11. package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
  12. package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
  13. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
  14. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
  15. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
  16. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
  17. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
  18. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
  19. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
  20. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
  21. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
  22. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
  23. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
  24. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
  25. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
  26. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
  27. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
  28. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
  29. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
  30. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
  31. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
  32. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
  33. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
  34. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
  35. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
  36. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
  37. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
  38. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
  39. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
  40. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
  41. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
  42. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
  43. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
  44. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
  45. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
  46. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
  47. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
  48. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
  49. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
  50. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
  51. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
  52. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
  53. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
  54. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
  55. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
  56. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
  57. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
  58. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
  59. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
  60. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
  61. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
  62. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
  63. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
  64. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
  65. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
  66. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
  67. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
  68. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
  69. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
  70. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
  71. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
  72. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
  73. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
  74. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
  75. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
  76. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
  77. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
  78. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
  79. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
  80. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
  81. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
  82. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
  83. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
  84. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
  85. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
  86. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
  87. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
  88. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
  89. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
  90. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
  91. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
  92. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
  93. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
  94. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
  95. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
  96. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
  97. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
  98. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
  99. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
  100. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
  101. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
  102. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
  103. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
  104. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
  105. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
  106. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
  107. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
  108. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
  109. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
  110. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
  111. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
  112. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
  113. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
  114. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
  115. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
  116. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
  117. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
  118. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
  119. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
  120. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
  121. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
  122. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
  123. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
  124. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
  125. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
  126. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
  127. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
  128. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
  129. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
  130. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
  131. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
  132. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
  133. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
  134. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
  135. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
  136. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
  137. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
  138. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
  139. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
  140. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
  141. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
  142. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
  143. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
  144. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
  145. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
  146. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
  147. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
  148. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
  149. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
  150. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
  151. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
  152. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
  153. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
  154. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
  155. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
  156. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
  157. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
  158. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
  159. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
  160. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
  161. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
  162. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
  163. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
  164. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
  165. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
  166. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
  167. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
  168. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
  169. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
  170. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
  171. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
  172. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
  173. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
  174. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
  175. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
  176. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
  177. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
  178. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
  179. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
  180. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
  181. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
  182. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
  183. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
  184. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
  185. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
  186. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
  187. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
  188. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
  189. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
  190. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
  191. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
  192. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
  193. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
  194. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
  195. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
  196. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
  197. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
  198. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
  199. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
  200. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
  201. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
  202. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
  203. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
  204. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
  205. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
  206. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
  207. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
  208. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
  209. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
  210. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
  211. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
  212. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
  213. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
  214. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
  215. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
  216. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
  217. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
  218. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
  219. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
  220. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
  221. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
  222. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
  223. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
  224. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
  225. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
  226. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
  227. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
  228. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
  229. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
  230. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
  231. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
  232. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
  233. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
  234. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
  235. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
  236. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
  237. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
  238. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
  239. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
  240. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
  241. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
  242. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
  243. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
  244. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
  245. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
  246. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
  247. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
  248. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
  249. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
  250. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
  251. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
  252. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
  253. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
  254. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
  255. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
  256. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
  257. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
  258. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
  259. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
  260. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
  261. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
  262. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
  263. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
  264. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
  265. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
  266. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
  267. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
  268. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
  269. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
  270. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
  271. package/skills/aicut-motion/references/design-systems/README.md +1 -1
  272. package/skills/aicut-motion/references/motion-delivery.md +55 -0
  273. package/skills/aicut-motion/references/pages.md +1 -1
  274. package/skills/aicut-motion/references/style-index.json +690 -0
  275. package/skills/aicut-motion/references/style-research.md +73 -0
  276. package/skills/aicut-motion/references/style-selection.md +71 -0
  277. package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
  278. package/skills/aicut-motion/scripts/atlas.mjs +109 -0
  279. package/studio/client.mjs +3 -3
  280. package/studio/server/fetch-github.mjs +14 -3
  281. package/studio/server/server.mjs +71 -6
  282. package/studio/server/skill-summaries.mjs +60 -0
  283. package/studio/server/skills.mjs +121 -7
  284. package/studio/server/styles-store.mjs +235 -0
  285. package/studio/server/template-posters.mjs +2 -2
  286. package/studio/server/templates-page.mjs +93 -93
  287. package/studio/server/templates.mjs +32 -7
  288. package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
  289. package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
  290. package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
  291. package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
  292. package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
  293. package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
  294. package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
  295. package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
  296. package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
  297. package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
  298. package/studio/ui/dist/index.html +4 -3
  299. package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
  300. package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
  301. package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
  302. package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
  303. package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
  304. package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
  305. package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
  306. package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
@@ -0,0 +1,531 @@
1
+ {
2
+ // Generative streamline art: particles traced through a divergence-free (curl) field.
3
+ const PAL = ['#123A4A', '#1C6F78', '#5FB3A6', '#CDE3CF', '#F3E6C8', '#EDBB73', '#DE7A52', '#A8384E', '#5A1E44'];
4
+ const PALRGB = PAL.map((c) => H.hex(c));
5
+ function pal(u) {
6
+ u = H.clamp(u) * (PAL.length - 1);
7
+ const i = Math.min(PAL.length - 2, Math.floor(u)), f = u - i;
8
+ return H.mix(PALRGB[i], PALRGB[i + 1], f);
9
+ }
10
+ const FX = {}; // cached offscreen layers (keyed by size)
11
+ // The core: a lensing void, not a sticker. Warm halo, dark contact ring, disc lit from the upper-left
12
+ // with an inner rim-light, then the ivory hairline rim (drawn by the caller).
13
+ function coreBody(ctx, cx, cy, Rc, a) {
14
+ ctx.save(); ctx.globalAlpha = a; ctx.globalCompositeOperation = 'lighter';
15
+ const halo = ctx.createRadialGradient(cx, cy, Rc * 0.9, cx, cy, Rc * 1.5);
16
+ halo.addColorStop(0, 'rgba(255,230,190,0.07)'); halo.addColorStop(1, 'rgba(255,230,190,0)');
17
+ ctx.fillStyle = halo; ctx.beginPath(); ctx.arc(cx, cy, Rc * 1.5, 0, 7); ctx.fill();
18
+ ctx.restore();
19
+ ctx.save();
20
+ // contact ring: the streams darken where they graze the void
21
+ const cr = ctx.createRadialGradient(cx, cy, Rc * 0.96, cx, cy, Rc * 0.97 + 7);
22
+ cr.addColorStop(0, 'rgba(4,4,8,0.75)'); cr.addColorStop(0.45, 'rgba(4,4,8,0.4)'); cr.addColorStop(1, 'rgba(4,4,8,0)');
23
+ ctx.fillStyle = cr; ctx.beginPath(); ctx.arc(cx, cy, Rc * 0.97 + 7, 0, 7); ctx.fill();
24
+ const core = ctx.createRadialGradient(cx - Rc * 0.35, cy - Rc * 0.4, 4, cx, cy, Rc);
25
+ core.addColorStop(0, '#1A1C28'); core.addColorStop(0.6, '#0D0E15'); core.addColorStop(1, '#06070A');
26
+ ctx.fillStyle = core; ctx.beginPath(); ctx.arc(cx, cy, Rc * 0.97, 0, 7); ctx.fill();
27
+ ctx.clip();
28
+ // inner rim light, strongest on the upper-left edge
29
+ const rim = ctx.createRadialGradient(cx + Rc * 0.16, cy + Rc * 0.2, Rc * 0.62, cx + Rc * 0.05, cy + Rc * 0.06, Rc * 1.04);
30
+ rim.addColorStop(0, 'rgba(243,230,200,0)'); rim.addColorStop(0.75, 'rgba(243,230,200,0.035)'); rim.addColorStop(1, 'rgba(243,230,200,0.13)');
31
+ ctx.globalAlpha = a; ctx.fillStyle = rim; ctx.fillRect(cx - Rc, cy - Rc, Rc * 2, Rc * 2);
32
+ // faint cool counter-light on the lower-right edge
33
+ const rim2 = ctx.createRadialGradient(cx - Rc * 0.2, cy - Rc * 0.22, Rc * 0.78, cx - Rc * 0.05, cy - Rc * 0.05, Rc * 1.02);
34
+ rim2.addColorStop(0, 'rgba(95,179,166,0)'); rim2.addColorStop(1, 'rgba(95,179,166,0.06)');
35
+ ctx.fillStyle = rim2; ctx.fillRect(cx - Rc, cy - Rc, Rc * 2, Rc * 2);
36
+ ctx.restore();
37
+ }
38
+
39
+ registerStyle({
40
+ id: 'flow-field',
41
+ name: 'Flow Field',
42
+ category: 'Data & Information',
43
+ description: 'Thousands of hairline streamlines traced through a curl-noise field that parts around an unseen core; additive colour from a refined gradient builds luminous braids on dark paper.',
44
+ motion: 'The dark core appears first, then particles are released in a wave from left to right and trace their streamlines outward until the braids glow; in the hold the field slowly breathes while bright sparks ride the currents.',
45
+ duration: 7,
46
+ fonts: ['400 11px "Space Mono"', '700 11px "Space Mono"', '700 34px "Space Mono"', '700 13px "Space Mono"', '400 13px "Space Mono"', '700 38px "Space Mono"', '700 30px "Space Mono"', '700 74px "Space Mono"', '700 88px "Space Mono"', '700 60px "Space Mono"', '400 24px "Space Mono"'],
47
+ frameT: 6.8,
48
+ readsPixels: true,
49
+ draw(ctx, W, H_, t) {
50
+ // ---- dark paper ----
51
+ const bg = ctx.createRadialGradient(W * 0.6, H_ * 0.48, 40, W * 0.55, H_ * 0.5, W * 0.75);
52
+ bg.addColorStop(0, '#16182A'); bg.addColorStop(1, '#08090E');
53
+ ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H_);
54
+
55
+ // ---- stream function on a grid: flow past a cylinder + curl noise ----
56
+ const nz = H.makeNoise(23), nz2 = H.makeNoise(57);
57
+ const G = 4, gw = Math.ceil(W / G) + 3, gh = Math.ceil(H_ / G) + 3;
58
+ const cx = W * 0.4, cy = H_ * 0.47, Rc = 112;
59
+ const VORT = [[cx + 240, cy + 70, 95], [cx + 420, cy - 70, -105], [cx + 610, cy + 66, 85], [cx + 780, cy - 52, -70]];
60
+ const psi = new Float32Array(gw * gh);
61
+ const z = t * 0.06;
62
+ for (let j = 0; j < gh; j++) for (let i = 0; i < gw; i++) {
63
+ const x = (i - 1) * G, y = (j - 1) * G;
64
+ const dx = x - cx, dy = y - cy, r2 = dx * dx + dy * dy, r = Math.sqrt(r2);
65
+ const pot = (dy) * (1 - (Rc * Rc) / Math.max(r2, 1)); // potential flow around a cylinder
66
+ const away = H.smooth(Rc * 1.05, Rc * 2.1, r);
67
+ const n = nz.fbm3(x * 0.0026, y * 0.0026, z, 3) * 190 + nz2.n3(x * 0.006, y * 0.006, z * 2) * 26;
68
+ let vort = 0;
69
+ for (const [vxp, vyp, g] of VORT) {
70
+ const ex = x - vxp, ey = y - vyp, d2 = ex * ex + ey * ey;
71
+ vort += g * Math.log(d2 + 900) * 0.5 * Math.exp(-d2 / (2 * 150 * 150));
72
+ }
73
+ psi[j * gw + i] = pot * 1.0 + (n + vort) * away + (y - H_ / 2) * 0.1;
74
+ }
75
+ // velocity = (dpsi/dy, -dpsi/dx)
76
+ const vx = new Float32Array(gw * gh), vy = new Float32Array(gw * gh);
77
+ for (let j = 1; j < gh - 1; j++) for (let i = 1; i < gw - 1; i++) {
78
+ const k = j * gw + i;
79
+ vx[k] = (psi[k + gw] - psi[k - gw]) / (2 * G);
80
+ vy[k] = -(psi[k + 1] - psi[k - 1]) / (2 * G);
81
+ }
82
+ const sample = (x, y, out) => {
83
+ const fx = x / G + 1, fy = y / G + 1;
84
+ const i = Math.floor(fx), j = Math.floor(fy);
85
+ if (i < 1 || j < 1 || i >= gw - 2 || j >= gh - 2) return false;
86
+ const u = fx - i, v = fy - j, k = j * gw + i;
87
+ const a = (1 - u) * (1 - v), b = u * (1 - v), c = (1 - u) * v, d = u * v;
88
+ out[0] = vx[k] * a + vx[k + 1] * b + vx[k + gw] * c + vx[k + gw + 1] * d;
89
+ out[1] = vy[k] * a + vy[k + 1] * b + vy[k + gw] * c + vy[k + gw + 1] * d;
90
+ out[2] = psi[k] * a + psi[k + 1] * b + psi[k + gw] * c + psi[k + gw + 1] * d;
91
+ return true;
92
+ };
93
+
94
+ // ---- trace particles; bucket polylines by colour ----
95
+ const NB = 28, buckets = Array.from({ length: NB }, () => []), allLines = [];
96
+ const rng = H.rng(91);
97
+ const N = 7000, STEPS = 170, STEP = 2.2;
98
+ const v = [0, 0, 0];
99
+ const inCore = (x, y) => (x - cx) * (x - cx) + (y - cy) * (y - cy) < Rc * Rc * 0.98;
100
+ for (let p = 0; p < N; p++) {
101
+ const sx = rng.range(-20, W + 20), sy = rng.range(-20, H_ + 20);
102
+ if (inCore(sx, sy)) continue;
103
+ // uneven seeding: braids of dense, luminous streams separated by quiet gaps
104
+ const dens = 0.5 + 0.5 * nz.n3(sx * 0.004, sy * 0.009, 7.3);
105
+ if (rng() > 0.18 + dens * dens * 1.4) continue;
106
+ if (!sample(sx, sy, v)) continue;
107
+ const birth = 0.35 + H.clamp(sx / W) * 1.5 + rng() * 0.8;
108
+ // colour by streamline identity (psi), slightly jittered
109
+ const u = H.clamp((v[2] + 320) / 640 + nz2.n2(sx * 0.0035, sy * 0.0035) * 0.22 + rng.gauss() * 0.02);
110
+ const line = []; let seedPt = 0;
111
+ for (const dir of [-1, 1]) {
112
+ let x = sx, y = sy;
113
+ const seg = [];
114
+ for (let s = 0; s < STEPS; s++) {
115
+ if (!sample(x, y, v)) break;
116
+ let mx = v[0], my = v[1];
117
+ const m = Math.hypot(mx, my) || 1;
118
+ // midpoint (RK2) step, normalised so strokes are even-length
119
+ const hx = x + dir * mx / m * STEP * 0.5, hy = y + dir * my / m * STEP * 0.5;
120
+ if (!sample(hx, hy, v)) break;
121
+ const m2 = Math.hypot(v[0], v[1]) || 1;
122
+ x += dir * v[0] / m2 * STEP; y += dir * v[1] / m2 * STEP;
123
+ if (inCore(x, y)) break;
124
+ seg.push(x, y);
125
+ }
126
+ if (dir === -1) { for (let q = seg.length - 2; q >= 0; q -= 2) line.push(seg[q], seg[q + 1]); line.push(sx, sy); seedPt = line.length / 2 - 1; }
127
+ else for (let q = 0; q < seg.length; q++) line.push(seg[q]);
128
+ }
129
+ if (line.length > 20) {
130
+ const grow = H.ease.out(H.clamp((t - birth) / 1.6));
131
+ if (grow > 0) {
132
+ const nPts = line.length / 2, si = Math.max(0, Math.min(nPts - 1, seedPt));
133
+ const reach = Math.ceil(grow * STEPS);
134
+ const a = Math.max(0, si - reach), b = Math.min(nPts - 1, si + reach);
135
+ const piece = line.slice(a * 2, b * 2 + 2);
136
+ if (piece.length >= 4) buckets[Math.min(NB - 1, Math.floor(u * NB))].push(piece);
137
+ }
138
+ if (allLines.length < 170 && rng() < 0.05) allLines.push({ L: line, u, ph: rng(), sp: rng.range(60, 120) });
139
+ }
140
+ }
141
+
142
+ // ---- draw: additive hairlines ----
143
+ const lay = H.layer(W, H_), lc = lay.getContext('2d');
144
+ lc.globalCompositeOperation = 'lighter';
145
+ lc.lineCap = 'round'; lc.lineJoin = 'round';
146
+ for (let b = 0; b < NB; b++) {
147
+ const col = pal((b + 0.5) / NB);
148
+ lc.strokeStyle = H.rgba(col, 0.1);
149
+ lc.lineWidth = 0.7;
150
+ lc.beginPath();
151
+ for (const L of buckets[b]) { lc.moveTo(L[0], L[1]); for (let q = 2; q < L.length; q += 2) lc.lineTo(L[q], L[q + 1]); }
152
+ lc.stroke();
153
+ }
154
+ ctx.save();
155
+ ctx.globalCompositeOperation = 'lighter';
156
+ // bloom: a soft, downsampled glow where streams pile up
157
+ const sm = H.layer(W / 4, H_ / 4), sc = sm.getContext('2d');
158
+ sc.filter = 'blur(3px)'; sc.drawImage(lay, 0, 0, W / 4, H_ / 4);
159
+ ctx.globalAlpha = 0.55; ctx.drawImage(sm, 0, 0, W, H_);
160
+ ctx.globalAlpha = 1; ctx.drawImage(lay, 0, 0);
161
+ ctx.restore();
162
+
163
+ // ---- sparks: bright heads riding the currents ----
164
+ const sparkA = H.clamp((t - 2.6) / 1.2);
165
+ if (sparkA > 0) {
166
+ ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.lineCap = 'round';
167
+ for (const S of allLines) {
168
+ const nPts = S.L.length / 2, len = nPts * STEP;
169
+ const head = ((t * S.sp + S.ph * len) % (len + 200)) / STEP; // pause off-path between passes
170
+ if (head >= nPts) continue;
171
+ const tail = Math.max(0, Math.floor(head) - 22), hI = Math.floor(head);
172
+ if (hI - tail < 2) continue;
173
+ const col = pal(S.u), g = ctx.createLinearGradient(S.L[tail * 2], S.L[tail * 2 + 1], S.L[hI * 2], S.L[hI * 2 + 1]);
174
+ g.addColorStop(0, H.rgba(col, 0)); g.addColorStop(1, H.rgba(H.mix(col, [255, 250, 235], 0.5), 0.6 * sparkA));
175
+ ctx.strokeStyle = g; ctx.lineWidth = 1.2;
176
+ ctx.beginPath(); ctx.moveTo(S.L[tail * 2], S.L[tail * 2 + 1]);
177
+ for (let q = tail + 1; q <= hI; q++) ctx.lineTo(S.L[q * 2], S.L[q * 2 + 1]);
178
+ ctx.stroke();
179
+ }
180
+ ctx.restore();
181
+ }
182
+
183
+ // ---- the core: a faint halo that defines the void ----
184
+ const coreA = H.ease.out(H.clamp(t / 0.9));
185
+ coreBody(ctx, cx, cy, Rc, coreA);
186
+ ctx.strokeStyle = `rgba(243,230,200,${0.4 * coreA})`; ctx.lineWidth = 0.8;
187
+ ctx.beginPath(); ctx.arc(cx, cy, Rc * 0.985, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * coreA); ctx.stroke();
188
+
189
+ // ---- caption ----
190
+ // typed on, monospaced, after the field has filled
191
+ const typed = (str, t0, cps) => str.slice(0, Math.max(0, Math.floor((t - t0) * cps)));
192
+ ctx.textBaseline = 'alphabetic'; ctx.textAlign = 'left';
193
+ ctx.fillStyle = 'rgba(236,228,210,0.85)'; ctx.font = '700 11px "Space Mono"';
194
+ let x0 = 48; for (const ch of typed('LATENT FLOW', 3.0, 22)) { ctx.fillText(ch, x0, H_ - 54); x0 += ctx.measureText(ch).width + 3.2; }
195
+ ctx.fillStyle = 'rgba(236,228,210,0.45)'; ctx.font = '400 11px "Space Mono"';
196
+ ctx.fillText(typed('streamlines of a learned vector field', 3.5, 40), 48, H_ - 36);
197
+ ctx.textAlign = 'right';
198
+ ctx.globalAlpha = H.clamp((t - 4.2) / 0.4); ctx.fillText('No. 04', W - 48, H_ - 36); ctx.globalAlpha = 1;
199
+
200
+ H.grain(ctx, W, H_, 0.035, 12 + Math.floor(t * 12));
201
+ },
202
+ // Sound: air, not objects. A soft pad underneath, the field itself as breath-like filtered noise that swirls
203
+ // while the core drinks the data and goes from gusty to smooth as turbulence combs into order; the scripted
204
+ // particles are airy swells panned along their paths; labels arrive as low soft sine notes. Times mirror explainer().
205
+ explainerSound(k) {
206
+ const D = 15, cxs = 1280 * 0.42;
207
+ const io = H.ease.inOut;
208
+ const pan = (px) => H.clamp((px / 1280) * 2 - 1, -1, 1) * 0.7;
209
+ const when = (t0, t1, ease, u) => { for (let i = 0; i <= 400; i++) { const t = t0 + ((t1 - t0) * i) / 400; if (ease(i / 400) >= u - 1e-6) return t; } return t1; };
210
+ const seg = (t, a, b) => H.clamp((t - a) / (b - a));
211
+ // a label surfacing: a low soft sine note with a little air
212
+ const note = (t, f, g = 1, p = 0, minor = true) => { k.tone(t, { f, dur: 0.9, gain: 0.09 * g, a: 0.03, verb: 0.35, pan: p, minor }); k.noise(t, { dur: 0.25, color: 'pink', filter: 'bandpass', f: 1200, q: 0.8, gain: 0.025 * g, a: 0.08, pan: p, minor }); };
213
+ // airy swell carried along a path: segments panned from p0 to p1, brightest in the middle (ease inOut speed)
214
+ const carry = (t0, t1, p0, p1, g = 1, f0 = 500, f1 = 1500) => {
215
+ const n = 6, d = (t1 - t0) / n;
216
+ for (let i = 0; i < n; i++) { const u = (i + 0.5) / n, sp = 1 - Math.abs(u - 0.5) * 1.6; k.noise(t0 + i * d, { dur: d * 1.7, color: 'pink', filter: 'bandpass', f: f0 + (f1 - f0) * sp, q: 1.1, gain: 0.07 * g * (0.4 + sp), a: d * 0.7, pan: p0 + (p1 - p0) * u, verb: 0.2 }); }
217
+ };
218
+ // soft grain tick for counters (filtered, dry, mid-low)
219
+ const grain = (t, g, p) => k.noise(t, { dur: 0.02, color: 'pink', filter: 'bandpass', f: 1400, q: 1.2, gain: 0.16 * g, a: 0.003, pan: p, minor: true });
220
+ const counter = (t0, t1, steps, p) => { let last = -1, lastT = -1; for (let t = t0; t <= t1 + 1e-6; t += 1 / 120) { const v = Math.floor(io(seg(t, t0, t1)) * steps); if (v !== last && t - lastT >= 1 / 12) { grain(t, 0.7 + 0.4 * v / steps, p); last = v; lastT = t; } } };
221
+
222
+ k.music('pad', 0, D, { bpm: 64, gain: 0.5, chords: [['D3', 'A3', 'E4', 'F#4'], ['B2', 'F#3', 'D4', 'A4'], ['G2', 'D3', 'B3', 'F#4'], ['D3', 'A3', 'F#4', 'A4']] });
223
+
224
+ // ---- the field: gusty air the whole way, rough early, combed smooth by 12.9 (turb 180 -> 26)
225
+ k.withMinor(() => {
226
+ for (let t = 0, i = 0; t < D; t += 0.5, i++) {
227
+ const turb = H.lerp(180, 26, io(seg(t, 10.8, 12.9))) / 180, grow = H.ease.out(seg(t, 0, 2.2));
228
+ const wob = Math.sin(i * 2.3) * 0.5 + Math.sin(i * 0.9) * 0.5;
229
+ k.noise(t, { dur: 0.95, color: 'pink', filter: 'bandpass', f: 700 + 500 * turb * (0.5 + 0.5 * wob), q: 0.5 + 1.6 * (1 - turb), gain: 0.05 * (0.35 + 0.65 * grow) * (1 + 0.6 * turb * wob), a: 0.45, pan: wob * 0.35 * turb });
230
+ }
231
+ });
232
+
233
+ // ---- hook: streamlines grow in, title surfaces, then docks top-left (1.6 -> 2.4)
234
+ k.noise(0, { dur: 2.2, color: 'pink', filter: 'bandpass', f: [[0, 300], [1.4, 1100], [2.2, 700]], q: 0.7, gain: 0.12, a: 1.2, verb: 0.3 });
235
+ note(0.12, 'A3', 1.3, 0, false);
236
+ k.noise(1.6, { dur: 0.8, color: 'brown', filter: 'bandpass', f: [[0, 260], [0.8, 420]], q: 0.6, gain: 0.08, a: 0.3, pan: -0.4, minor: true });
237
+
238
+ // ---- data: the core becomes a vortex drinking labelled particles (2.4 -> 6.6)
239
+ for (let t = 2.4, i = 0; t < 6.4; t += 0.4, i++) // swirl: air circling the core
240
+ k.noise(t, { dur: 0.7, color: 'pink', filter: 'bandpass', f: 500 + 120 * Math.sin(i * 1.3), q: 1.4, gain: 0.06 * (1 - seg(t, 5.6, 6.6) * 0.8), a: 0.3, pan: pan(cxs) + Math.sin(i * 1.57) * 0.45, minor: true });
241
+ note(2.85, 'D4', 1.1, pan(cxs + 140)); // "the model"
242
+ note(2.95, 'A3', 0.9, pan(360)); // "cat"
243
+ note(3.25, 'F#3', 0.9, pan(360)); // "dog"
244
+ counter(2.6, 5.9, 30, -0.6);
245
+ note(5.9, 'D3', 1.0, -0.6, false); // 1,000,000 settles
246
+ k.noise(6.0, { dur: 0.9, color: 'pink', filter: 'bandpass', f: [[0, 300], [0.45, 650], [0.9, 280]], q: 0.7, gain: 0.08, a: 0.4 }); // camera eases in
247
+
248
+ // ---- loop: one particle passes over the core, out the wrong exit
249
+ carry(6.8, 8.0, pan(0), pan(cxs + 300), 1.1);
250
+ note(6.95, 'E4', 0.9, pan(200)); // "new photo of a cat"
251
+ note(7.95, 'F3', 1.2, pan(900), false); // guess: "dog?"
252
+ k.tone(8.0, { f: 'D4', dur: 0.5, gain: 0.12, a: 0.01, verb: 0.3, pan: pan(900) }); // the wrong guess: a soft falling pair
253
+ k.tone(8.18, { f: 'Bb3', dur: 0.9, gain: 0.13, a: 0.01, verb: 0.35, pan: pan(900) });
254
+ k.noise(8.0, { dur: 0.5, color: 'pink', filter: 'bandpass', f: [[0, 400], [0.5, 900]], q: 1.2, gain: 0.08, a: 0.06, pan: pan(900) }); // coral ring opens
255
+ note(8.32, 'C4', 0.8, -0.7); // check
256
+ carry(8.4, 9.1, pan(cxs + 300), pan(cxs), 1.1, 900, 380); // error counter-current runs back
257
+ // adjust: the circulation flips (9.0 -> 9.9), a broad slow breath that turns over
258
+ k.noise(9.0, { dur: 1.0, color: 'pink', filter: 'bandpass', f: [[0, 300], [0.5, 800], [1.0, 350]], q: 0.8, gain: 0.13, a: 0.45, pan: pan(cxs), verb: 0.25 });
259
+ note(9.05, 'G3', 1.0, pan(cxs));
260
+ carry(9.7, 10.6, pan(0), pan(cxs + 300), 1.0, 450, 1200); // particle 2 slips under the core
261
+ ['D4', 'A4', 'F#4'].forEach((f, i) => k.tone(10.3 + i * 0.07, { f, dur: 1.4, gain: 0.11 - i * 0.015, a: 0.01, verb: 0.4, pan: pan(900) })); // "cat" ✓
262
+ k.noise(10.5, { dur: 0.9, color: 'pink', filter: 'bandpass', f: [[0, 280], [0.45, 600], [0.9, 260]], q: 0.7, gain: 0.08, a: 0.4 }); // camera eases out
263
+
264
+ // ---- progress: the field sorts itself; accuracy counts up
265
+ counter(11.0, 12.9, 36, 0.8);
266
+ // 97%: the core rim flashes, the underline draws: the big hit, a deep soft bloom
267
+ k.play('impact', 12.9, { gain: 0.45, pan: 0.2 });
268
+ k.tone(12.9, { f: 'D2', dur: 2.2, gain: 0.22, a: 0.02, verb: 0.4 });
269
+ ['D4', 'A4'].forEach((f, i) => k.play('ding', 13.0 + i * 0.07, { f, gain: 0.45, pan: 0.5 - i * 0.3 }));
270
+ k.noise(12.9, { dur: 0.4, color: 'pink', filter: 'bandpass', f: [[0, 600], [0.4, 1100]], q: 1, gain: 0.06, a: 0.1, pan: 0.85, minor: true }); // underline
271
+ // takeaway surfaces: the resolve
272
+ ['D3', 'A3', 'F#4', 'A4'].forEach((f, i) => k.tone(13.4 + i * 0.09, { f, dur: 1.5, gain: 0.1 - i * 0.012, a: 0.02, verb: 0.45, pan: (i - 1.5) * 0.2 }));
273
+ },
274
+ explainerDuration: 15,
275
+ explainerNote: 'The model is the dark core of a flow field: labelled particles spiral into it as data, the camera pushes in as a new particle is carried out the wrong exit ("dog?"), a coral counter-current carries the error back, the circulation re-combs the whole field so the next one exits "cat", and training is shown as the turbulent field combing into smooth order, all captioned in hairline Space Mono.',
276
+ explainer(ctx, W, H_, t) {
277
+ const S = (a, b) => H.clamp((t - a) / (b - a));
278
+ const cx = W * 0.42, cy = H_ * 0.48, Rc = 92;
279
+ const TEAL = [95, 179, 166], CORAL = [222, 122, 82], IVORY = [243, 230, 200];
280
+ // ---- field parameters over the story ----
281
+ const swirl = 120 * (1 - S(5.6, 6.6)); // data: the core is a vortex drinking examples
282
+ const circ = t < 6.6 ? 0 : H.lerp(-55, 55, H.ease.inOut(S(9.0, 9.9))); // adjust: circulation flips which side wins
283
+ const turb = H.lerp(180, 26, H.ease.inOut(S(10.8, 12.9))); // progress: turbulence combs into order
284
+ // ---- camera ----
285
+ const CK = [[0, 1, cx, cy], [6.0, 1, cx, cy], [6.8, 1.18, cx + 110, cy], [10.5, 1.18, cx + 110, cy], [11.3, 1, cx, cy], [15, 1, cx, cy]];
286
+ let cs = 1, ccx = cx, ccy = cy;
287
+ for (let i = 0; i < CK.length - 1; i++) if (t >= CK[i][0] && t <= CK[i + 1][0]) { const u = H.ease.inOut((t - CK[i][0]) / (CK[i + 1][0] - CK[i][0])); cs = H.lerp(CK[i][1], CK[i + 1][1], u); ccx = H.lerp(CK[i][2], CK[i + 1][2], u); ccy = H.lerp(CK[i][3], CK[i + 1][3], u); break; }
288
+ const bg = ctx.createRadialGradient(W * 0.5, H_ * 0.48, 40, W * 0.5, H_ * 0.5, W * 0.75);
289
+ bg.addColorStop(0, '#16182A'); bg.addColorStop(1, '#08090E');
290
+ ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H_);
291
+ ctx.save(); ctx.translate(W / 2 + (cx - W / 2), H_ / 2 + (cy - H_ / 2)); ctx.scale(cs, cs); ctx.translate(-ccx, -ccy);
292
+ // world -> screen helper for HUD-attached labels
293
+ const toS = (x, y) => [(x - ccx) * cs + cx, (y - ccy) * cs + cy];
294
+
295
+ // ---- stream function on a grid ----
296
+ const nz = H.makeNoise(23), nz2 = H.makeNoise(57);
297
+ const G = 5, gx0 = -40, gy0 = -40, gw = Math.ceil((W + 80) / G) + 2, gh = Math.ceil((H_ + 80) / G) + 2;
298
+ const psi = new Float32Array(gw * gh);
299
+ const z = t * 0.06;
300
+ for (let j = 0; j < gh; j++) for (let i = 0; i < gw; i++) {
301
+ const x = gx0 + i * G, y = gy0 + j * G, dx = x - cx, dy = y - cy, r2 = dx * dx + dy * dy, r = Math.sqrt(r2);
302
+ const pot = dy * (1 - (Rc * Rc) / Math.max(r2, 1));
303
+ const away = H.smooth(Rc * 1.05, Rc * 2.2, r);
304
+ const n = nz.fbm3(x * 0.0026, y * 0.0026, z, 3) * turb;
305
+ psi[j * gw + i] = pot + n * away + (swirl + circ) * Math.log(Math.max(r, 1) / Rc);
306
+ }
307
+ const sample = (x, y, out) => {
308
+ const fx = (x - gx0) / G, fy = (y - gy0) / G, i = Math.floor(fx), j = Math.floor(fy);
309
+ if (i < 1 || j < 1 || i >= gw - 2 || j >= gh - 2) return false;
310
+ const k = j * gw + i, u = fx - i, v = fy - j;
311
+ const p = (kk) => psi[kk];
312
+ const dpy = ((p(k + gw) - p(k - gw)) * (1 - u) + (p(k + gw + 1) - p(k - gw + 1)) * u) / (2 * G);
313
+ const dpx = ((p(k + 1) - p(k - 1)) * (1 - v) + (p(k + gw + 1) - p(k + gw - 1)) * v) / (2 * G);
314
+ out[0] = dpy; out[1] = -dpx; out[2] = p(k) * (1 - u) * (1 - v) + p(k + 1) * u * (1 - v) + p(k + gw) * (1 - u) * v + p(k + gw + 1) * u * v;
315
+ return true;
316
+ };
317
+ // ---- background streamlines (grow in during the hook) ----
318
+ const DY = new Float32Array(gw * gh), DX = new Float32Array(gw * gh);
319
+ for (let j = 1; j < gh - 1; j++) for (let i = 1, k = j * gw + 1; i < gw - 1; i++, k++) { DY[k] = (psi[k + gw] - psi[k - gw]) / (2 * G); DX[k] = (psi[k + 1] - psi[k - 1]) / (2 * G); }
320
+ const NB = 24, buckets = Array.from({ length: NB }, () => []), sparkLines = [];
321
+ const rng = H.rng(91), v = [0, 0, 0];
322
+ const grow = H.ease.out(S(0, 2.2));
323
+ const STEPS = Math.floor(10 + 120 * grow), STEP = 2.4;
324
+ const inCore = (x, y) => (x - cx) ** 2 + (y - cy) ** 2 < Rc * Rc * 0.97;
325
+ for (let pI = 0; pI < 4600; pI++) {
326
+ const sx = rng.range(-30, W + 30), sy = rng.range(-30, H_ + 30);
327
+ // uneven seeding as in the still: dense luminous braids separated by quiet gaps
328
+ const dens = 0.5 + 0.5 * nz.n3(sx * 0.004, sy * 0.009, 7.3);
329
+ if (rng() > 0.1 + dens * dens * 1.45) continue;
330
+ if (inCore(sx, sy) || !sample(sx, sy, v)) continue;
331
+ const u = H.clamp((v[2] + 320) / 640 + nz2.n2(sx * 0.0035, sy * 0.0035) * 0.2);
332
+ const line = [];
333
+ for (const dir of [-1, 1]) {
334
+ let x = sx, y = sy; const seg = [];
335
+ for (let s = 0; s < STEPS; s++) {
336
+ // same velocity as sample(), read from the precomputed difference grids
337
+ const fx = (x - gx0) / G, fy = (y - gy0) / G, i = Math.floor(fx), j = Math.floor(fy);
338
+ if (i < 1 || j < 1 || i >= gw - 2 || j >= gh - 2) break;
339
+ const k = j * gw + i, uu = fx - i, vv = fy - j;
340
+ const vxx = DY[k] * (1 - uu) + DY[k + 1] * uu, vyy = -(DX[k] * (1 - vv) + DX[k + gw] * vv);
341
+ const m = Math.sqrt(vxx * vxx + vyy * vyy) || 1;
342
+ x += dir * vxx / m * STEP; y += dir * vyy / m * STEP; if ((x - cx) * (x - cx) + (y - cy) * (y - cy) < Rc * Rc * 0.97) break; seg.push(x, y);
343
+ }
344
+ if (dir === -1) { for (let q = seg.length - 2; q >= 0; q -= 2) line.push(seg[q], seg[q + 1]); line.push(sx, sy); } else line.push(...seg);
345
+ }
346
+ if (line.length > 8) {
347
+ buckets[Math.min(NB - 1, Math.floor(u * NB))].push(line);
348
+ if (sparkLines.length < 90 && line.length > 60 && (pI * 7919) % 23 === 0) sparkLines.push({ L: line, u, ph: (pI * 0.6180339) % 1, sp: 60 + ((pI * 37) % 61) });
349
+ }
350
+ }
351
+ // hairlines into an offscreen layer (camera applied there), then the still's bloom + sharp composite
352
+ const order = H.ease.inOut(S(10.8, 12.9));
353
+ if (!FX.lay || FX.lay.width !== W || FX.lay.height !== H_) { FX.lay = H.layer(W, H_); FX.sm = H.layer(W / 4, H_ / 4); }
354
+ const lc = FX.lay.getContext('2d');
355
+ lc.setTransform(1, 0, 0, 1, 0, 0); lc.globalCompositeOperation = 'source-over'; lc.clearRect(0, 0, W, H_);
356
+ lc.translate(cx, cy); lc.scale(cs, cs); lc.translate(-ccx, -ccy);
357
+ lc.globalCompositeOperation = 'lighter'; lc.lineCap = 'butt'; lc.lineJoin = 'bevel'; lc.lineWidth = 0.7 / cs;
358
+ for (let b = 0; b < NB; b++) {
359
+ lc.strokeStyle = H.rgba(pal((b + 0.5) / NB), 0.12 + 0.04 * order);
360
+ lc.beginPath(); for (const L of buckets[b]) { lc.moveTo(L[0], L[1]); for (let q = 4; q < L.length; q += 4) lc.lineTo(L[q], L[q + 1]); if (L.length % 4 === 0) lc.lineTo(L[L.length - 2], L[L.length - 1]); } lc.stroke();
361
+ }
362
+ ctx.save(); ctx.setTransform(ctx.getTransform().multiply(new DOMMatrix().translate(ccx, ccy).scale(1 / cs, 1 / cs).translate(-cx, -cy)));
363
+ ctx.globalCompositeOperation = 'lighter';
364
+ const sc = FX.sm.getContext('2d'); sc.clearRect(0, 0, W / 4, H_ / 4); sc.filter = 'blur(3px)'; sc.drawImage(FX.lay, 0, 0, W / 4, H_ / 4); sc.filter = 'none';
365
+ ctx.globalAlpha = 0.6; ctx.drawImage(FX.sm, 0, 0, W, H_);
366
+ ctx.globalAlpha = 1; ctx.drawImage(FX.lay, 0, 0);
367
+ ctx.restore();
368
+ // sparks: bright heads riding the currents (the still's shimmer, at about half density)
369
+ const sparkA = S(1.4, 2.6) * 0.85;
370
+ if (sparkA > 0) {
371
+ ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.lineCap = 'round'; ctx.lineWidth = 1.2 / cs;
372
+ for (const Sp of sparkLines) {
373
+ const nPts = Sp.L.length / 2, len = nPts * STEP;
374
+ const head = ((t * Sp.sp + Sp.ph * len) % (len + 200)) / STEP;
375
+ if (head >= nPts) continue;
376
+ const tail = Math.max(0, Math.floor(head) - 22), hI = Math.floor(head);
377
+ if (hI - tail < 2) continue;
378
+ const col = pal(Sp.u), g = ctx.createLinearGradient(Sp.L[tail * 2], Sp.L[tail * 2 + 1], Sp.L[hI * 2], Sp.L[hI * 2 + 1]);
379
+ g.addColorStop(0, H.rgba(col, 0)); g.addColorStop(1, H.rgba(H.mix(col, [255, 250, 235], 0.5), 0.6 * sparkA));
380
+ ctx.strokeStyle = g;
381
+ ctx.beginPath(); ctx.moveTo(Sp.L[tail * 2], Sp.L[tail * 2 + 1]);
382
+ for (let q = tail + 1; q <= hI; q++) ctx.lineTo(Sp.L[q * 2], Sp.L[q * 2 + 1]);
383
+ ctx.stroke();
384
+ }
385
+ ctx.restore();
386
+ }
387
+
388
+ // ---- the core (the model) ----
389
+ const flash = S(12.9, 13.1) * (1 - S(13.3, 14.0));
390
+ coreBody(ctx, cx, cy, Rc, 1);
391
+ ctx.strokeStyle = `rgba(243,230,200,${0.4 + 0.5 * flash})`; ctx.lineWidth = (0.8 + 2 * flash) / cs; ctx.beginPath(); ctx.arc(cx, cy, Rc * 0.985, 0, 7); ctx.stroke();
392
+
393
+ // ---- particle helpers ----
394
+ const dot = (x, y, col, r, a = 1) => { const g = ctx.createRadialGradient(x, y, 0, x, y, r * 4); g.addColorStop(0, H.rgba(col, 0.9 * a)); g.addColorStop(1, H.rgba(col, 0)); ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = g; ctx.beginPath(); ctx.arc(x, y, r * 4, 0, 7); ctx.fill(); ctx.restore(); ctx.fillStyle = H.rgba(IVORY, a); ctx.beginPath(); ctx.arc(x, y, r * 0.6, 0, 7); ctx.fill(); };
395
+ const trail = (P, col, a = 1) => { if (P.length < 2) return; ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.lineCap = 'round'; for (let i = 1; i < P.length; i++) { ctx.strokeStyle = H.rgba(col, a * 0.8 * i / P.length); ctx.lineWidth = 1.6 / cs; ctx.beginPath(); ctx.moveTo(...P[i - 1]); ctx.lineTo(...P[i]); ctx.stroke(); } ctx.restore(); };
396
+ // spiral into the core (data beat)
397
+ const spiral = (r0, th0, u) => { const r = H.lerp(r0, Rc * 0.95, Math.pow(u, 0.8)), th = th0 + u * u * 2.6; return [cx + Math.cos(th) * r, cy + Math.sin(th) * r]; };
398
+ // scripted pass over (up=+1 → dog exit) or under (up=-1 → cat exit) the core
399
+ const pass = (up, u, y0 = cy) => {
400
+ const s = up > 0 ? -1 : 1;
401
+ const P = [[-30, y0], [cx - Rc - 160, y0 + s * 6], [cx - Rc * 0.9, cy + s * (Rc * 0.75)], [cx, cy + s * (Rc + 26)], [cx + Rc * 0.95, cy + s * (Rc * 0.8)], [cx + Rc + 150, cy + s * 120], [cx + Rc + 330, cy + s * 195]];
402
+ const f = H.clamp(u) * (P.length - 1), i = Math.min(P.length - 2, Math.floor(f)), w = f - i;
403
+ const p0 = P[Math.max(0, i - 1)], p1 = P[i], p2 = P[i + 1], p3 = P[Math.min(P.length - 1, i + 2)];
404
+ const cr = (a, b, c, d) => 0.5 * (2 * b + (-a + c) * w + (2 * a - 5 * b + 4 * c - d) * w * w + (-a + 3 * b - 3 * c + d) * w * w * w);
405
+ return [cr(p0[0], p1[0], p2[0], p3[0]), cr(p0[1], p1[1], p2[1], p3[1])];
406
+ };
407
+ const hist = (fn, u, n = 12, du = 0.012) => { const P = []; for (let k = n; k >= 0; k--) P.push(fn(u - k * du)); return P; };
408
+
409
+ // ================= DATA: labelled particles spiral into the core =================
410
+ const dA = S(2.4, 2.8) * (1 - S(6.2, 6.6));
411
+ let tagCat = null, tagDog = null;
412
+ if (dA > 0) {
413
+ const pr = H.rng(404);
414
+ for (let k = 0; k < 70; k++) {
415
+ const isCat = pr.chance(0.5), r0 = pr.range(560, 820), th0 = Math.PI + pr.range(-0.5, 0.5), t0 = 2.4 + pr.range(0, 3.4), dur = pr.range(1.6, 2.4);
416
+ const u = (t - t0) / dur; if (u <= 0 || u >= 1) continue;
417
+ const col = isCat ? TEAL : CORAL, P = hist((uu) => spiral(r0, th0, H.clamp(uu)), u, 10, 0.02);
418
+ trail(P, col, dA); const [x, y] = P[P.length - 1]; dot(x, y, col, 2.4, dA * (1 - S(0.85, 1) * 0));
419
+
420
+ if (!tagCat && isCat && u > 0.15 && u < 0.6 && y > 190 && x > 120) tagCat = [x, y];
421
+ if (!tagDog && !isCat && u > 0.15 && u < 0.6 && y > 190 && x > 120) tagDog = [x, y];
422
+ }
423
+ }
424
+
425
+ // ================= LOOP: one new particle, wrong exit, error back, re-comb, right exit =================
426
+ const loopA = S(6.6, 6.9) * (1 - S(10.6, 11.0));
427
+ let p1 = null, p2 = null;
428
+ if (loopA > 0) {
429
+ // splitter: two exits
430
+ // particle 1 (above → dog)
431
+ const u1 = H.ease.inOut(S(6.8, 8.0));
432
+ if (t >= 6.8 && t < 9.9) { const P = hist((uu) => pass(1, uu), u1, 14, 0.01); trail(P, IVORY, loopA); p1 = P[P.length - 1]; dot(...p1, IVORY, 3.2, loopA); }
433
+ // error counter-current: coral streak running back from the dog exit to the core
434
+ const e = S(8.4, 9.1);
435
+ if (e > 0 && t < 9.9) {
436
+ const P = []; for (let k = 0; k <= 30; k++) { const uu = 1 - e * (k / 30) * 0.62; P.push(pass(1, uu)); }
437
+ ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.strokeStyle = H.rgba(CORAL, 0.85 * (1 - S(9.5, 9.9))); ctx.lineWidth = 3 / cs; ctx.lineCap = 'round';
438
+ ctx.setLineDash([10 / cs, 6 / cs]); ctx.lineDashOffset = t * 60 / cs;
439
+ ctx.beginPath(); P.forEach(([x, y], i) => (i ? ctx.lineTo(x, y) : ctx.moveTo(x, y))); ctx.stroke(); ctx.restore();
440
+ }
441
+ // particle 2 (below → cat)
442
+ const u2 = H.ease.inOut(S(9.7, 10.6));
443
+ if (t >= 9.7) { const P = hist((uu) => pass(-1, uu), u2, 14, 0.01); trail(P, TEAL, loopA); p2 = P[P.length - 1]; dot(...p2, TEAL, 3.2, loopA); }
444
+ }
445
+
446
+ // ================= PROGRESS: many particles now sort themselves correctly =================
447
+ if (t > 10.9) {
448
+ const pr = H.rng(808), acc = 0.61 + 0.36 * H.ease.inOut(S(11.0, 12.9));
449
+ for (let k = 0; k < 46; k++) {
450
+ const isCat = pr.chance(0.5), t0 = 10.9 + pr.range(0, 3.8), dur = pr.range(1.4, 2.0), y0 = cy + pr.range(-60, 60), right = pr() < acc;
451
+ const u = (t - t0) / dur; if (u <= 0 || u >= 1) continue;
452
+ const up = isCat ? (right ? -1 : 1) : (right ? 1 : -1);
453
+ const P = hist((uu) => pass(up, uu, y0), u, 10, 0.012), col = isCat ? TEAL : CORAL;
454
+ trail(P, col, 0.9); dot(...P[P.length - 1], col, 2.2, 0.9);
455
+ }
456
+ }
457
+ ctx.restore(); // camera
458
+
459
+ // ================= labels (hairline Space Mono, attached to what they label) =================
460
+ const mono = (str, x, y, size, weight, a, align = 'left', col = 'rgba(236,228,210,0.88)') => { if (a <= 0) return; ctx.globalAlpha = a; ctx.font = `${weight} ${size}px "Space Mono"`; ctx.fillStyle = col; ctx.textAlign = align; ctx.textBaseline = 'alphabetic'; ctx.fillText(str, x, y); ctx.globalAlpha = 1; };
461
+ const lead = (x0, y0, x1, y1, a, col = 'rgba(236,228,210,0.6)') => { if (a <= 0) return; ctx.globalAlpha = a; ctx.strokeStyle = col; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); ctx.stroke(); ctx.beginPath(); ctx.arc(x1, y1, 2, 0, 7); ctx.stroke(); ctx.globalAlpha = 1; };
462
+ // hook title → docks top-left
463
+ {
464
+ const d = H.ease.inOut(S(1.6, 2.4));
465
+ const x = H.lerp(W / 2, 44, d), y = H.lerp(H_ * 0.48 + 14, 56, d), sz = H.lerp(60, 30, d);
466
+ ctx.globalAlpha = S(0.1, 0.5); ctx.font = `700 ${sz}px "Space Mono"`; ctx.fillStyle = 'rgba(236,228,210,0.95)'; ctx.textAlign = d > 0.5 ? 'left' : 'center'; ctx.textBaseline = 'alphabetic';
467
+ if (d <= 0.5) { ctx.fillStyle = 'rgba(8,9,14,0.65)'; ctx.fillRect(W / 2 - 430, y - 70, 860, 100); ctx.fillStyle = 'rgba(236,228,210,0.95)'; }
468
+ let w = 0; const str = 'HOW DOES AI LEARN?', tr = H.lerp(5, 3, d); for (const ch of str) w += ctx.measureText(ch).width + tr;
469
+ let xx = d > 0.5 ? x : x - w / 2; ctx.textAlign = 'left'; for (const ch of str) { ctx.fillText(ch, xx, y); xx += ctx.measureText(ch).width + tr; }
470
+ ctx.globalAlpha = 1;
471
+ }
472
+ // data labels
473
+ const coreS = toS(cx + Rc * 0.7, cy - Rc * 0.7);
474
+ const mA = S(2.8, 3.2) * (1 - S(10.6, 11)) * (1 - S(8.4, 8.6) + S(9.8, 10.0));
475
+ lead(coreS[0] + 60, coreS[1] - 60, coreS[0], coreS[1], mA);
476
+ mono('the model', coreS[0] + 64, coreS[1] - 66, 30, 700, mA);
477
+ if (dA > 0) {
478
+ if (tagCat) { const [x, y] = toS(...tagCat); lead(x - 34, y - 30, x, y, dA * S(2.9, 3.2)); mono('cat', x - 38, y - 34, 28, 700, dA * S(2.9, 3.2), 'right', 'rgb(95,179,166)'); }
479
+ if (tagDog) { const [x, y] = toS(...tagDog); lead(x + 34, y + 30, x, y, dA * S(3.2, 3.5)); mono('dog', x + 38, y + 52, 28, 700, dA * S(3.2, 3.5), 'left', 'rgb(222,122,82)'); }
480
+ const n = Math.round(1e6 * H.ease.inOut(S(2.6, 5.9)));
481
+ mono(n.toLocaleString('en-US'), 44, H_ - 84, 74, 700, dA);
482
+ mono('labelled examples flowing in', 48, H_ - 44, 28, 400, dA, 'left', 'rgba(236,228,210,0.7)');
483
+ }
484
+ // loop labels
485
+ if (loopA > 0) {
486
+ const steps = ['guess', 'check', 'adjust'], act = t < 8.3 ? 0 : t < 9.0 ? 1 : t < 9.8 ? 2 : 0;
487
+ let x = 48; steps.forEach((s, i) => { ctx.font = '700 30px "Space Mono"'; const w = ctx.measureText(s).width; mono(s, x, 110, 30, 700, loopA, 'left', i === act ? 'rgba(236,228,210,0.98)' : 'rgba(236,228,210,0.35)'); if (i === act) { ctx.globalAlpha = loopA; ctx.fillStyle = 'rgba(236,228,210,0.9)'; ctx.fillRect(x, 120, w, 3); ctx.globalAlpha = 1; } x += w + 18; if (i < 2) { mono('→', x, 110, 30, 400, loopA, 'left', 'rgba(236,228,210,0.4)'); x += 48; } });
488
+ const [ux, uy] = toS(...pass(1, 1)), [lx, ly] = toS(...pass(-1, 1));
489
+ // exits: hairline gates with their labels
490
+ [[ux, uy, 'dog', 'rgb(222,122,82)'], [lx, ly, 'cat', 'rgb(95,179,166)']].forEach(([x, y, s, c]) => {
491
+ ctx.globalAlpha = loopA; ctx.strokeStyle = c; ctx.lineWidth = 1.2; ctx.beginPath(); ctx.arc(x, y, 9, 0, 7); ctx.stroke(); ctx.globalAlpha = 1;
492
+ mono(s, x + 20, y + 11, 32, 700, loopA, 'left', c);
493
+ });
494
+ if (p1 && t < 8.3) { const [x, y] = toS(...p1); mono('new photo of a cat', Math.max(x + 24, 40), y - 26, 26, 700, S(6.9, 7.2) * (1 - S(7.6, 7.8))); }
495
+ if (t >= 7.9 && t < 9.9) {
496
+ const a = S(7.9, 8.1) * (1 - S(9.6, 9.9));
497
+ mono('guess: “dog?” ✗', ux - 60, uy - 34, 30, 700, a, 'left', 'rgba(236,228,210,0.98)');
498
+ const ring = S(8.0, 8.5); ctx.globalAlpha = a * (1 - ring * 0.6); ctx.strokeStyle = 'rgba(222,122,82,0.9)'; ctx.lineWidth = 1.4; ctx.beginPath(); ctx.arc(ux, uy, 12 + 46 * ring, 0, 7); ctx.stroke(); ctx.globalAlpha = 1;
499
+ mono('check: it was a cat', lx - 60, ly + 52, 26, 700, S(8.3, 8.6) * (1 - S(9.6, 9.9)), 'left');
500
+ const [mx, my] = toS(cx + Rc * 1.2, cy - Rc * 1.55);
501
+ mono('error flows back', mx, my, 28, 700, S(8.6, 8.9) * (1 - S(9.5, 9.8)), 'left', 'rgb(222,122,82)');
502
+ }
503
+ const [ax, ay] = toS(cx, cy + Rc + 40);
504
+ mono('adjust: the flow re-combs', ax, ay + 30, 28, 700, S(9.0, 9.3) * (1 - S(9.9, 10.2)), 'center');
505
+ if (t >= 10.3) mono('guess: “cat” ✓', lx - 60, ly + 52, 30, 700, S(10.3, 10.5) * loopA, 'left', 'rgb(95,179,166)');
506
+ }
507
+ // progress
508
+ if (t > 10.9) {
509
+ const a = S(11.0, 11.3), p = H.ease.inOut(S(11.0, 12.9));
510
+ mono('accuracy', W - 48, H_ - 150, 28, 400, a, 'right', 'rgba(236,228,210,0.7)');
511
+ mono(Math.round(61 + 36 * p) + '%', W - 48, H_ - 70, 88, 700, a, 'right');
512
+ mono('turbulence combs into order', 48, H_ - 84, 28, 700, S(11.2, 11.6) * (1 - S(13.1, 13.4)));
513
+ // sparkline
514
+ ctx.globalAlpha = a; ctx.strokeStyle = 'rgba(236,228,210,0.8)'; ctx.lineWidth = 1; ctx.beginPath();
515
+ for (let k = 0; k <= 50 * p; k++) { const u = k / 50, vv = 1 - Math.exp(-u * 4); const x = W - 440 + u * 150, y = H_ - 96 - vv * 44; k ? ctx.lineTo(x, y) : ctx.moveTo(x, y); }
516
+ ctx.stroke();
517
+ // baseline, ticks and a glowing end-dot so it reads as a chart
518
+ ctx.strokeStyle = 'rgba(236,228,210,0.28)'; ctx.beginPath(); ctx.moveTo(W - 444, H_ - 95.5); ctx.lineTo(W - 286, H_ - 95.5);
519
+ for (let k = 0; k <= 5; k++) { const x = Math.round(W - 440 + k * 30) + 0.5; ctx.moveTo(x, H_ - 95.5); ctx.lineTo(x, H_ - 91); } ctx.stroke();
520
+ { const vv = 1 - Math.exp(-p * 4), ex = W - 440 + p * 150, ey = H_ - 96 - vv * 44;
521
+ const g = ctx.createRadialGradient(ex, ey, 0, ex, ey, 10); g.addColorStop(0, 'rgba(255,240,215,0.7)'); g.addColorStop(1, 'rgba(255,240,215,0)');
522
+ ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = g; ctx.beginPath(); ctx.arc(ex, ey, 10, 0, 7); ctx.fill(); ctx.restore();
523
+ ctx.fillStyle = 'rgba(246,238,222,0.95)'; ctx.beginPath(); ctx.arc(ex, ey, 2.4, 0, 7); ctx.fill(); }
524
+ ctx.globalAlpha = 1;
525
+ const em = S(12.9, 13.3); if (em > 0) { ctx.fillStyle = 'rgba(236,228,210,0.95)'; ctx.fillRect(W - 48 - 170 * em, H_ - 56, 170 * em, 3); }
526
+ }
527
+ mono('MORE EXAMPLES → BETTER GUESSES', W / 2 - 40, H_ - 26, 44, 700, S(13.3, 13.8), 'center', 'rgba(236,228,210,0.98)');
528
+ H.grain(ctx, W, H_, 0.035, 12 + Math.floor(t * 12));
529
+ },
530
+ });
531
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 84,
3
+ "name": "Flow Field",
4
+ "nameZh": "流场",
5
+ "family": "Data & Information",
6
+ "familyZh": "数据与信息",
7
+ "description": "Thousands of hairline streamlines traced through a curl-noise field that parts around an unseen core; additive colour from a refined gradient builds luminous braids on dark paper.",
8
+ "motionNotes": "Motion: The dark core appears first, then particles are released in a wave from left to right and trace their streamlines outward until the braids glow; in the hold the field slowly breathes while bright sparks ride the currents.",
9
+ "explainerNotes": "Explainer: The model is the dark core of a flow field: labelled particles spiral into it as data, the camera pushes in as a new particle is carried out the wrong exit (\"dog?\"), a coral counter-current carries the error back, the circulation re-combs the whole field so the next one exits \"cat\", and training is shown as the turbulent field combing into smooth order, all captioned in hairline Space Mono.",
10
+ "code": "code.js",
11
+ "registeredAs": "flow-field",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }