@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,13 @@
1
+ {
2
+ "id": 79,
3
+ "name": "Teletext Page",
4
+ "nameZh": "图文电视页",
5
+ "family": "Retro UI & Neon",
6
+ "familyZh": "复古界面与霓虹",
7
+ "description": "A broadcast teletext page: strict 40x25 character grid, eight saturated colours, 2x3 sextant mosaic graphics with one colour per cell, double-height headlines and Fastext links, seen through a CRT.",
8
+ "motionNotes": "Motion: Only the header shows while the page counter rolls in green, then the page lands top-down two rows at a time; mosaic bars step up a sub-block at a time, LATEST and the robot eyes flash, the ticker hops whole characters and the clock ticks.",
9
+ "explainerNotes": "Explainer: Each beat is a new teletext page (P100-P104): the header searches in green while the old page lingers, then the new page overwrites it two rows at a time; arrows, pictures and bar charts are sextant mosaics, numbers count in double-height digits and the key moments use the Flash attribute.",
10
+ "code": "code.js",
11
+ "registeredAs": "teletext",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }
@@ -0,0 +1,524 @@
1
+ {
2
+ // ---------- Warhol-style silkscreen grid: flat hand-cut colour plates + misregistered black photo screen ----------
3
+ const PW = 300, PH = 331, M = 24, G = 10, COLS = 4, ROWS = 2;
4
+ const CANVAS = [236, 230, 218];
5
+ const PAL = [
6
+ { bg: '#f2559c', head: '#ffe14a', eye: '#22c3c8', mouth: '#e8242b', body: '#2b4fc3', tip: '#ff8a00' },
7
+ { bg: '#ff7a1f', head: '#9fdcff', eye: '#ffe600', mouth: '#e5007e', body: '#5b2a86', tip: '#2ad46b' },
8
+ { bg: '#2fbf63', head: '#ffa3cf', eye: '#1d3fbb', mouth: '#ff5a1f', body: '#fff04a', tip: '#e5007e' },
9
+ { bg: '#1e2b9c', head: '#ff6fae', eye: '#c8f53a', mouth: '#ffcf00', body: '#ff5a1f', tip: '#22c3c8' },
10
+ { bg: '#ffe100', head: '#ff6f3c', eye: '#2b4fc3', mouth: '#b8007a', body: '#22c3c8', tip: '#e8242b' },
11
+ { bg: '#9b4ee0', head: '#c9f53c', eye: '#ff4fa3', mouth: '#ff8a00', body: '#ffe14a', tip: '#ffffff' },
12
+ { bg: '#e8242b', head: '#fff1b0', eye: '#2fbf63', mouth: '#1e2b9c', body: '#ff9ec8', tip: '#ffe100' },
13
+ { bg: '#00b2c8', head: '#ffb000', eye: '#f2559c', mouth: '#e8242b', body: '#1e2b9c', tip: '#c9f53c' },
14
+ ];
15
+
16
+ const rrect = (x, a, b, w, h, r) => { x.beginPath(); x.roundRect(a, b, w, h, r); };
17
+
18
+ // the "photograph" the screen was burned from (grey tone, 0 = black, 255 = white)
19
+ const toneImage = () => {
20
+ const c = H.layer(PW, PH), x = c.getContext('2d');
21
+ x.fillStyle = '#fff'; x.fillRect(0, 0, PW, PH);
22
+ // soft cast shadow behind head on the right
23
+ x.fillStyle = 'rgba(0,0,0,0.12)'; rrect(x, 92, 82, 166, 156, 30); x.fill();
24
+ // shoulders / body
25
+ let g = x.createLinearGradient(40, 0, 260, 0);
26
+ g.addColorStop(0, '#e0e0e0'); g.addColorStop(0.4, '#b0b0b0'); g.addColorStop(0.7, '#6a6a6a'); g.addColorStop(1, '#303030');
27
+ x.fillStyle = g; x.beginPath(); x.moveTo(30, PH + 5); x.lineTo(42, 278); x.quadraticCurveTo(50, 252, 92, 248); x.lineTo(208, 248); x.quadraticCurveTo(250, 252, 258, 278); x.lineTo(270, PH + 5); x.closePath(); x.fill();
28
+ x.strokeStyle = '#222'; x.lineWidth = 3; x.stroke();
29
+ // chest panel with dials
30
+ x.fillStyle = '#5a5a5a'; rrect(x, 104, 272, 92, 50, 6); x.fill();
31
+ x.fillStyle = '#f0f0f0'; [[124, 292], [150, 292], [176, 292]].forEach(([a, b]) => { x.beginPath(); x.arc(a, b, 7, 0, 7); x.fill(); });
32
+ x.fillStyle = '#1a1a1a'; [[124, 292], [150, 292], [176, 292]].forEach(([a, b]) => { x.beginPath(); x.arc(a + 1.5, b + 1.5, 3, 0, 7); x.fill(); });
33
+ x.fillStyle = '#d0d0d0'; x.fillRect(112, 308, 76, 5);
34
+ // neck with rings
35
+ g = x.createLinearGradient(124, 0, 176, 0); g.addColorStop(0, '#9a9a9a'); g.addColorStop(0.4, '#cfcfcf'); g.addColorStop(1, '#262626');
36
+ x.fillStyle = g; x.fillRect(124, 214, 52, 38);
37
+ x.fillStyle = '#2a2a2a'; x.fillRect(122, 224, 56, 5); x.fillRect(122, 238, 56, 5);
38
+ // ears
39
+ for (const s of [-1, 1]) {
40
+ g = x.createLinearGradient(0, 118, 0, 176); g.addColorStop(0, '#d6d6d6'); g.addColorStop(1, '#3a3a3a');
41
+ x.fillStyle = s < 0 ? g : '#3c3c3c'; rrect(x, s < 0 ? 52 : 232, 118, 16, 56, 5); x.fill();
42
+ x.strokeStyle = '#1b1b1b'; x.lineWidth = 2.5; x.stroke();
43
+ }
44
+ // head
45
+ g = x.createLinearGradient(70, 70, 235, 225);
46
+ g.addColorStop(0, '#ffffff'); g.addColorStop(0.35, '#e2e2e2'); g.addColorStop(0.62, '#959595'); g.addColorStop(0.85, '#555555'); g.addColorStop(1, '#383838');
47
+ x.fillStyle = g; rrect(x, 68, 70, 164, 150, 30); x.fill();
48
+ // hard shadow side
49
+ x.save(); rrect(x, 68, 70, 164, 150, 30); x.clip();
50
+ x.fillStyle = 'rgba(0,0,0,0.55)'; x.beginPath(); x.moveTo(206, 60); x.quadraticCurveTo(196, 150, 214, 230); x.lineTo(260, 230); x.lineTo(260, 60); x.closePath(); x.fill();
51
+ x.fillStyle = 'rgba(0,0,0,0.35)'; x.fillRect(60, 206, 200, 20);
52
+ x.restore();
53
+ x.strokeStyle = '#141414'; x.lineWidth = 3.5; rrect(x, 68, 70, 164, 150, 30); x.stroke();
54
+ // antenna
55
+ x.strokeStyle = '#202020'; x.lineWidth = 4; x.beginPath(); x.moveTo(150, 70); x.lineTo(150, 40); x.stroke();
56
+ g = x.createRadialGradient(146, 31, 1, 150, 35, 11); g.addColorStop(0, '#fff'); g.addColorStop(1, '#333');
57
+ x.fillStyle = g; x.beginPath(); x.arc(150, 35, 10, 0, 7); x.fill();
58
+ // brows
59
+ x.fillStyle = '#1e1e1e'; x.fillRect(92, 102, 44, 6); x.fillRect(164, 102, 44, 6);
60
+ // eyes
61
+ for (const ex of [114, 186]) {
62
+ x.fillStyle = '#121212'; x.beginPath(); x.arc(ex, 138, 25, 0, 7); x.fill();
63
+ g = x.createRadialGradient(ex - 6, 132, 2, ex, 138, 19); g.addColorStop(0, '#9a9a9a'); g.addColorStop(1, '#2a2a2a');
64
+ x.fillStyle = g; x.beginPath(); x.arc(ex, 138, 18, 0, 7); x.fill();
65
+ x.fillStyle = '#050505'; x.beginPath(); x.arc(ex + 1, 139, 8, 0, 7); x.fill();
66
+ x.fillStyle = '#fff'; x.beginPath(); x.arc(ex - 7, 131, 5, 0, 7); x.fill();
67
+ }
68
+ // nose rivet
69
+ x.fillStyle = '#333'; x.beginPath(); x.arc(150, 162, 4, 0, 7); x.fill();
70
+ // mouth grille
71
+ x.fillStyle = '#1a1a1a'; rrect(x, 104, 180, 92, 24, 6); x.fill();
72
+ x.fillStyle = '#bdbdbd'; for (let i = 0; i < 6; i++) x.fillRect(112 + i * 14, 185, 5, 14);
73
+ // rivets along head
74
+ x.fillStyle = '#2a2a2a'; [[82, 84], [218, 84], [82, 206], [218, 206]].forEach(([a, b]) => { x.beginPath(); x.arc(a, b, 3.5, 0, 7); x.fill(); });
75
+ return x.getImageData(0, 0, PW, PH).data;
76
+ };
77
+
78
+ // crude hand-cut colour shapes (in panel coords)
79
+ const cutShape = (x, pts) => { x.beginPath(); x.moveTo(pts[0][0], pts[0][1]); for (let i = 1; i < pts.length; i++) x.lineTo(pts[i][0], pts[i][1]); x.closePath(); x.fill(); };
80
+ const blobAround = (r, cx, cy, rx, ry, n) => { const p = []; for (let i = 0; i < n; i++) { const a = (i / n) * Math.PI * 2; p.push([cx + Math.cos(a) * rx * r.range(0.9, 1.1), cy + Math.sin(a) * ry * r.range(0.9, 1.1)]); } return p; };
81
+
82
+ // ================= EXPLAINER: six silkscreened panels, one beat each, pulled in order =================
83
+ const SE = H.ease, scl = H.clamp, sseg = (t, a, b) => scl((t - a) / (b - a));
84
+ const QW = 402, QH = 330, QG = 12, QM = 25;
85
+ const SLOT = [[0, 0], [1, 0], [2, 0], [2, 1], [1, 1], [0, 1]]; // snake order
86
+ const qx = (i) => QM + SLOT[i][0] * (QW + QG), qy = (i) => 24 + SLOT[i][1] * (QH + QG);
87
+ const PRINT = [0.05, 2.55, 6.15, 8.45, 10.65, 13.05]; // when each panel is pulled
88
+ const QPAL = [
89
+ { bg: '#f2559c', a: '#ffe14a', b: '#22c3c8', c: '#e8242b' },
90
+ { bg: '#ff7a1f', a: '#fff04a', b: '#9fdcff', c: '#5b2a86' },
91
+ { bg: '#22c3c8', a: '#fff1b0', b: '#ffe14a', c: '#e8242b' },
92
+ { bg: '#ffe100', a: '#ff9ec8', b: '#2fbf63', c: '#e8242b' },
93
+ { bg: '#9b4ee0', a: '#c9f53c', b: '#ffe14a', c: '#ff4fa3' },
94
+ { bg: '#e8242b', a: '#fff1b0', b: '#ffe14a', c: '#1e2b9c' },
95
+ ];
96
+ const FLASH = ['#ff7a1f', '#2fbf63', '#1e2b9c', '#f2559c'];
97
+ let SX_CACHE = null;
98
+ const shash = (x, y, s) => { let h = (Math.imul(x, 374761393) + Math.imul(y, 668265263) + Math.imul(s, 1442695041)) | 0; h = Math.imul(h ^ (h >>> 13), 1274126177); h ^= h >>> 16; return (h >>> 0) / 4294967296; };
99
+ const anton = (x, s, size, px, py, align = 'center', ls = 1) => { x.font = `400 ${size}px "Anton"`; x.textAlign = align; x.textBaseline = 'middle'; x.letterSpacing = ls + 'px'; x.fillText(s, px, py); x.letterSpacing = '0px'; };
100
+ const cutPoly = (x, pts) => { x.beginPath(); pts.forEach((p, i) => (i ? x.lineTo(p[0], p[1]) : x.moveTo(p[0], p[1]))); x.closePath(); x.fill(); };
101
+ const loose = (cx, cy, w, h, seed) => { const r = H.rng(seed); return [[cx - w / 2 + r.range(-6, 6), cy - h / 2 + r.range(-6, 6)], [cx + w / 2 + r.range(-6, 6), cy - h / 2 + r.range(-6, 6)], [cx + w / 2 + r.range(-6, 6), cy + h / 2 + r.range(-6, 6)], [cx - w / 2 + r.range(-6, 6), cy + h / 2 + r.range(-6, 6)]]; };
102
+ // small robot portrait (colour shapes + black screen with soft shading as grey tone)
103
+ const miniBot = (c, k, cx, cy, s, pal, mood) => {
104
+ c.save(); c.translate(cx, cy); c.scale(s, s); k.save(); k.translate(cx, cy); k.scale(s, s);
105
+ c.fillStyle = pal.a; cutPoly(c, loose(6, 4, 168, 150, 3)); c.fillStyle = pal.b; cutPoly(c, loose(-36, -6, 60, 46, 4)); cutPoly(c, loose(42, -4, 60, 46, 5)); c.fillStyle = pal.c; cutPoly(c, loose(4, 50, 86, 30, 6));
106
+ k.fillStyle = '#000'; k.strokeStyle = '#000';
107
+ const g = k.createLinearGradient(-80, -70, 80, 70); g.addColorStop(0, 'rgba(0,0,0,0)'); g.addColorStop(0.55, 'rgba(0,0,0,0.25)'); g.addColorStop(1, 'rgba(0,0,0,0.85)');
108
+ k.fillStyle = g; k.beginPath(); k.roundRect(-80, -72, 160, 144, 26); k.fill(); k.fillStyle = '#000';
109
+ k.lineWidth = 4; k.beginPath(); k.roundRect(-80, -72, 160, 144, 26); k.stroke();
110
+ k.lineWidth = 3.5; k.beginPath(); k.moveTo(0, -72); k.lineTo(0, -98); k.stroke(); k.beginPath(); k.arc(0, -104, 8, 0, 7); k.fill();
111
+ for (const ex of [-36, 36]) { k.beginPath(); k.arc(ex, -10, 22, 0, 7); k.fill(); k.fillStyle = '#fff'; k.globalCompositeOperation = 'destination-out'; k.beginPath(); k.arc(ex - 7, -17, 6, 0, 7); k.fill(); k.globalCompositeOperation = 'source-over'; k.fillStyle = '#000'; }
112
+ if (mood === 'happy') { k.lineWidth = 7; k.beginPath(); k.arc(0, 36, 28, 0.2, Math.PI - 0.2); k.stroke(); }
113
+ else if (mood === 'sad') { k.lineWidth = 7; k.beginPath(); k.arc(0, 70, 28, Math.PI + 0.3, -0.3); k.stroke(); }
114
+ else { k.beginPath(); k.roundRect(-40, 38, 80, 22, 5); k.fill(); k.fillStyle = 'rgba(0,0,0,0)'; }
115
+ c.restore(); k.restore();
116
+ };
117
+ const picCard = (c, k, cx, cy, s, kind, label, pal) => {
118
+ c.save(); c.translate(cx, cy); c.scale(s / 100, s / 100); k.save(); k.translate(cx, cy); k.scale(s / 100, s / 100);
119
+ c.fillStyle = '#fbf3e4'; cutPoly(c, loose(0, 0, 100, 120, kind === 'cat' ? 9 : 10)); c.fillStyle = pal.a; cutPoly(c, loose(2, 44, 96, 30, 11));
120
+ k.fillStyle = '#000'; k.strokeStyle = '#000'; k.lineWidth = 4; k.strokeRect(-50, -60, 100, 120); k.lineCap = 'round';
121
+ k.lineWidth = 4; k.beginPath(); k.arc(0, -14, 26, 0, 7); k.stroke();
122
+ if (kind === 'cat') { k.beginPath(); k.moveTo(-22, -26); k.lineTo(-20, -50); k.lineTo(-6, -38); k.moveTo(22, -26); k.lineTo(20, -50); k.lineTo(6, -38); k.stroke(); k.lineWidth = 2.5; k.beginPath(); for (const sg of [-1, 1]) { k.moveTo(sg * 6, -6); k.lineTo(sg * 36, -8); k.moveTo(sg * 6, -2); k.lineTo(sg * 34, 2); } k.stroke(); }
123
+ else { for (const sg of [-1, 1]) { k.beginPath(); k.ellipse(sg * 26, -12, 8, 17, sg * 0.4, 0, 7); k.fill(); } k.beginPath(); k.ellipse(0, -4, 6, 4, 0, 0, 7); k.fill(); }
124
+ k.beginPath(); k.arc(-9, -18, 3.4, 0, 7); k.arc(9, -18, 3.4, 0, 7); k.fill();
125
+ if (label) anton(k, label, 26, 0, 44, 'center', 2);
126
+ c.restore(); k.restore();
127
+ };
128
+ const blackArrow = (k, p0, cp, p1, u, w) => {
129
+ if (u <= 0) return;
130
+ const q = (s) => [(1 - s) * (1 - s) * p0[0] + 2 * (1 - s) * s * cp[0] + s * s * p1[0], (1 - s) * (1 - s) * p0[1] + 2 * (1 - s) * s * cp[1] + s * s * p1[1]];
131
+ k.save(); k.strokeStyle = '#000'; k.fillStyle = '#000'; k.lineWidth = w; k.lineCap = 'round'; k.beginPath(); k.moveTo(p0[0], p0[1]);
132
+ for (let i = 1; i <= 24 * u; i++) { const p = q(i / 24); k.lineTo(p[0], p[1]); }
133
+ const e = q(u), b = q(Math.max(0, u - 0.05)), a = Math.atan2(e[1] - b[1], e[0] - b[0]); k.lineTo(e[0], e[1]); k.stroke();
134
+ k.beginPath(); k.moveTo(e[0] + Math.cos(a) * w * 2.2, e[1] + Math.sin(a) * w * 2.2); k.lineTo(e[0] + Math.cos(a + 2.4) * w * 2.6, e[1] + Math.sin(a + 2.4) * w * 2.6); k.lineTo(e[0] + Math.cos(a - 2.4) * w * 2.6, e[1] + Math.sin(a - 2.4) * w * 2.6); k.closePath(); k.fill(); k.restore();
135
+ };
136
+
137
+ // panel contents (panel-local coords 402 x 330) — colour plates into c, black screen into k
138
+ const CONTENT = [
139
+ (c, k, t, pal) => { // HOOK
140
+ miniBot(c, k, 290, 210, 0.95, pal, 'think');
141
+ c.fillStyle = pal.a; cutPoly(c, loose(118, 92, 210, 130, 21));
142
+ anton(k, 'HOW DOES', 46, 118, 62, 'center', 1); anton(k, 'AI LEARN?', 50, 118, 120, 'center', 1);
143
+ const q = Math.floor(t * 6) % 2 ? -4 : 0; if (t > 1.0) { anton(k, '?', 54, 366, 70 + q); anton(k, '?', 38, 340, 34 - q); }
144
+ },
145
+ (c, k, t, pal) => { // DATA: a contact sheet filling up + counter
146
+ const n = Math.floor(sseg(t, 3.15, 4.6) * 12);
147
+ const cyc = Math.floor(Math.max(0, t - 4.6) * 6);
148
+ for (let j = 0; j < n; j++) {
149
+ const col = j % 4, row = (j / 4) | 0, kind = (j + cyc) % 3 === 1 ? 'dog' : 'cat';
150
+ picCard(c, k, 50 + col * 72, 52 + row * 82, 58, kind, kind.toUpperCase(), pal);
151
+ }
152
+ c.fillStyle = pal.b; cutPoly(c, loose(201, 290, 380, 64, 31));
153
+ const v = Math.round(1e6 * Math.pow(sseg(t, 3.2, 5.5), 2.3));
154
+ anton(k, v.toLocaleString('en-US') + ' EXAMPLES', 40, 201, 291, 'center', 1);
155
+ if (t > 5.5) { k.save(); k.lineWidth = 5; k.beginPath(); k.moveTo(30, 322); k.lineTo(30 + 340 * SE.out(sseg(t, 5.5, 5.8)), 320); k.stroke(); k.restore(); }
156
+ },
157
+ (c, k, t, pal) => { // GUESS + CHECK
158
+ picCard(c, k, 82, 150, 120, 'cat', '?', pal);
159
+ blackArrow(k, [150, 120], [205, 70], [258, 108], SE.out(sseg(t, 6.8, 7.1)), 6);
160
+ if (t > 6.95) anton(k, 'GUESS', 30, 205, 58, 'center', 2);
161
+ if (t > 7.15) { c.fillStyle = '#fbf3e4'; cutPoly(c, loose(320, 150, 140, 90, 41)); k.lineWidth = 4; k.strokeRect(250, 105, 140, 90); anton(k, 'DOG?', 50, 320, 150); }
162
+ if (t > 7.5) {
163
+ const u = SE.back(sseg(t, 7.5, 7.7)); c.save(); c.translate(360, 110); c.scale(u, u); c.strokeStyle = pal.c; c.lineWidth = 16; c.lineCap = 'round'; c.beginPath(); c.moveTo(-28, -28); c.lineTo(28, 28); c.moveTo(28, -28); c.lineTo(-28, 28); c.stroke(); c.restore();
164
+ anton(k, 'CHECK', 30, 320, 232, 'center', 2); anton(k, "WRONG — IT'S A CAT", 22, 300, 270, 'center', 1);
165
+ }
166
+ },
167
+ (c, k, t, pal) => { // ADJUST + correct
168
+ miniBot(c, k, 110, 200, 0.75, pal, t > 10.0 ? 'happy' : 'sad');
169
+ const adj = SE.inOut(sseg(t, 9.25, 9.85));
170
+ for (let d = 0; d < 3; d++) {
171
+ const dx = 230 + d * 58, dy = 250; c.fillStyle = pal.c; cutPoly(c, loose(dx, dy, 48, 48, 50 + d));
172
+ k.lineWidth = 4; k.beginPath(); k.arc(dx, dy, 22, 0, 7); k.stroke();
173
+ const a = [-2.2, -0.6, 0.9][d] + adj * [1.8, -1.4, 2.4][d]; k.lineWidth = 6; k.lineCap = 'round'; k.beginPath(); k.moveTo(dx, dy); k.lineTo(dx + Math.cos(a) * 18, dy + Math.sin(a) * 18); k.stroke();
174
+ }
175
+ if (t > 9.1) anton(k, 'ADJUST', 34, 288, 196, 'center', 2);
176
+ if (t > 10.0) {
177
+ c.fillStyle = '#fbf3e4'; cutPoly(c, loose(290, 90, 150, 90, 61)); k.lineWidth = 4; k.strokeRect(215, 45, 150, 90); anton(k, 'CAT', 52, 290, 90);
178
+ const u = SE.back(sseg(t, 10.05, 10.25)); c.save(); c.translate(370, 50); c.scale(u, u); c.strokeStyle = '#1e7a3a'; c.lineWidth = 14; c.lineCap = 'round'; c.lineJoin = 'round'; c.beginPath(); c.moveTo(-22, 0); c.lineTo(-6, 16); c.lineTo(24, -18); c.stroke(); c.restore();
179
+ }
180
+ },
181
+ (c, k, t, pal) => { // PROGRESS
182
+ const X0 = 36, Y0 = 280, CW = 200, CH = 220, prog = SE.inOut(sseg(t, 11.3, 12.5));
183
+ const acc = (u) => 61 + 36 * (1 - Math.exp(-u * 3.2)) / (1 - Math.exp(-3.2)), yv = (v) => Y0 - ((v - 50) / 50) * CH;
184
+ c.fillStyle = pal.a; c.beginPath(); c.moveTo(X0, Y0); for (let i = 0; i <= 40 * prog; i++) c.lineTo(X0 + (i / 40) * CW + 6, yv(acc(i / 40)) + 4); c.lineTo(X0 + prog * CW + 6, Y0); c.closePath(); c.fill();
185
+ k.lineWidth = 4; k.beginPath(); k.moveTo(X0, Y0 - CH - 6); k.lineTo(X0, Y0); k.lineTo(X0 + CW + 10, Y0); k.stroke();
186
+ k.lineWidth = 7; k.lineJoin = 'round'; k.beginPath(); for (let i = 0; i <= 40 * prog; i++) { const u = i / 40; i ? k.lineTo(X0 + u * CW, yv(acc(u))) : k.moveTo(X0, yv(acc(0))); } k.stroke();
187
+ anton(k, 'TRAINING ROUNDS', 18, X0 + CW / 2, Y0 + 22, 'center', 2);
188
+ c.fillStyle = pal.b; cutPoly(c, loose(322, 160, 150, 110, 71));
189
+ anton(k, 'ACCURACY', 22, 322, 92, 'center', 3);
190
+ anton(k, Math.round(61 + 36 * prog) + '%', 78, 322, 165, 'center', 0);
191
+ if (t > 12.5) { k.lineWidth = 5; k.beginPath(); k.ellipse(322, 165, 92 * SE.back(sseg(t, 12.5, 12.7)), 62 * SE.back(sseg(t, 12.5, 12.7)), -0.1, 0, 7); k.stroke(); }
192
+ },
193
+ (c, k, t, pal) => { // TAKEAWAY
194
+ c.fillStyle = pal.a; cutPoly(c, loose(201, 100, 360, 80, 81)); cutPoly(c, loose(201, 236, 360, 80, 82));
195
+ anton(k, 'MORE EXAMPLES', 54, 201, 100, 'center', 1);
196
+ blackArrow(k, [201, 145], [201, 160], [201, 182], SE.out(sseg(t, 13.75, 13.95)), 7);
197
+ anton(k, 'BETTER GUESSES', 54, 201, 238, 'center', 1);
198
+ },
199
+ ];
200
+
201
+ const camAt = (t) => {
202
+ const P = (i) => [qx(i) + QW / 2, qy(i) + QH / 2, 1.85];
203
+ const keys = [[0, P(0)], [2.25, P(0)], [2.75, P(1)], [5.85, P(1)], [6.35, P(2)], [8.15, P(2)], [8.65, P(3)], [10.35, P(3)], [10.85, P(4)], [12.75, P(4)], [13.25, P(5)], [14.05, P(5)], [14.75, [640, 360, 1]]];
204
+ let a = keys[0], b = keys[0];
205
+ for (let i = 0; i < keys.length - 1; i++) if (t >= keys[i][0]) { a = keys[i]; b = keys[i + 1]; }
206
+ if (t >= keys[keys.length - 1][0]) return keys[keys.length - 1][1];
207
+ const u = SE.inOut(sseg(t, a[0], b[0]));
208
+ // zoom out a touch while travelling between panels
209
+ const dip = (a[1][0] !== b[1][0] || a[1][1] !== b[1][1]) && b[1][2] > 1.5 ? Math.sin(Math.PI * u) * 0.35 : 0;
210
+ return [H.lerp(a[1][0], b[1][0], u), H.lerp(a[1][1], b[1][1], u), H.lerp(a[1][2], b[1][2], u) - dip];
211
+ };
212
+
213
+ const spExplainer = (ctx, W, H_, t) => {
214
+ if (!SX_CACHE) {
215
+ const nz = H.makeNoise(8), GW = 640, GH = 360, g = new Float32Array(GW * GH); for (let y = 0; y < GH; y++) for (let x = 0; x < GW; x++) g[y * GW + x] = nz.fbm2(x * 0.11, y * 0.11, 3) * 0.7 + nz.n2(x * 0.45, y * 0.45) * 0.3;
216
+ // squeegee streak rows per panel (same field as the still's streakRows), key misregistration, ghost pulls
217
+ const streak = [], reg = [];
218
+ for (let i = 0; i < 6; i++) {
219
+ const a = new Float32Array(QH), n2 = H.makeNoise((i + 1) * 13 + 5); for (let y = 0; y < QH; y++) a[y] = n2.fbm2(y * 0.045, 0.5, 3) + 0.6 * n2.n2(y * 0.3, 7.7); streak.push(a);
220
+ }
221
+ // key offsets chosen so nothing near a panel edge is pushed out of its panel
222
+ reg.push([-4, 3], [3, -3], [-3, 4], [4, 3], [-4, -3], [3, 4]);
223
+ const ghost = [[11, -3], null, null, null, null, [-9, 5]]; // panels with no gutter arrows crossing them
224
+ SX_CACHE = { g, GW, GH, streak, reg, ghost };
225
+ }
226
+ const { streak: STK, reg: REGO, ghost: GHO } = SX_CACHE;
227
+ const [ccx, ccy, z] = camAt(t);
228
+ const col = H.layer(W, H_), c = col.getContext('2d'), key = H.layer(W, H_), k = key.getContext('2d');
229
+ c.fillStyle = `rgb(${CANVAS})`; c.fillRect(0, 0, W, H_);
230
+ const cam = (x) => { x.setTransform(z, 0, 0, z, W / 2 - ccx * z, H_ / 2 - ccy * z); };
231
+ cam(c); cam(k);
232
+ const panelState = [];
233
+ for (let i = 0; i < 6; i++) {
234
+ const x0 = qx(i), y0 = qy(i), T0 = PRINT[i];
235
+ let pal = QPAL[i];
236
+ if (i === 4 && t > 12.5 && t < 13.0) pal = { ...pal, bg: FLASH[Math.floor((t - 12.5) * 8) % FLASH.length] }; // emphasis: colourway flicker
237
+ if (i === 2 && t > 7.5 && t < 7.9) pal = { ...pal, bg: Math.floor((t - 7.5) * 10) % 2 ? '#e8242b' : pal.bg };
238
+ const kp = SE.inOut(sseg(t, T0 + 0.3, T0 + 0.7));
239
+ panelState.push({ x0, y0, kp, dir: i % 2 ? -1 : 1, seed: i, on: t >= T0, fw: QW * SE.out(sseg(t, T0, T0 + 0.18)), st: STK[i], gh: GHO[i] });
240
+ if (t < T0) { c.strokeStyle = 'rgba(90,80,70,0.3)'; c.lineWidth = 1.2; c.strokeRect(x0, y0, QW, QH); continue; }
241
+ c.save(); c.beginPath(); c.rect(x0, y0, QW * SE.out(sseg(t, T0, T0 + 0.18)), QH); c.clip(); c.translate(x0, y0);
242
+ c.fillStyle = pal.bg; c.fillRect(0, 0, QW, QH);
243
+ k.save(); k.beginPath(); k.rect(x0, y0, QW, QH); k.clip(); k.translate(x0 + REGO[i][0], y0 + REGO[i][1]); // photo screen slightly off register
244
+ CONTENT[i](c, k, t, pal);
245
+ c.restore(); k.restore();
246
+ }
247
+ // cross-panel arrows painted straight on the canvas (cause and flow between beats)
248
+ k.save(); k.strokeStyle = '#000'; k.fillStyle = '#000';
249
+ blackArrow(k, [qx(2) + 330, qy(2) + 296], [qx(2) + 380, qy(2) + 345], [qx(3) + 362, qy(3) + 168], SE.inOut(sseg(t, 8.4, 8.95)), 7); // error flows down to the dials
250
+ blackArrow(k, [qx(3) + 40, qy(3) + 70], [qx(3) - 10, qy(3) - 40], [qx(2) + 40, qy(2) + 230], SE.inOut(sseg(t, 10.15, 10.4)), 6); // ...and loops back to guess again
251
+ blackArrow(k, [qx(1) + 380, qy(1) + 165], [qx(1) + QW + 6, qy(1) + 165], [qx(2) + 18, qy(2) + 150], SE.inOut(sseg(t, 6.0, 6.3)), 6);
252
+ k.restore();
253
+ c.setTransform(1, 0, 0, 1, 0, 0); k.setTransform(1, 0, 0, 1, 0, 0);
254
+ const cd = c.getImageData(0, 0, W, H_).data, kd = k.getImageData(0, 0, W, H_).data;
255
+ const out = ctx.createImageData(W, H_), o = out.data, { g, GW, GH } = SX_CACHE;
256
+ const K = [22, 18, 20], iz = 1 / z, PS = [];
257
+ for (let j = 0; j < 2; j++) for (let ii = 0; ii < 3; ii++) PS.push(panelState[SLOT.findIndex((q) => q[0] === ii && q[1] === j)]);
258
+ const kAt = (sx, sy) => (sx < 0 || sy < 0 || sx >= W || sy >= H_ ? 0 : kd[((sy | 0) * W + (sx | 0)) * 4 + 3] / 255);
259
+ for (let y = 0; y < H_; y++) {
260
+ const py = ccy + (y - H_ / 2) * iz, rowj = Math.floor((py - 24) / (QH + QG)), ly = py - 24 - rowj * (QH + QG);
261
+ const rowOK = rowj >= 0 && rowj < 2 && ly < QH;
262
+ for (let x = 0; x < W; x++) {
263
+ const i = (y * W + x) * 4, px = ccx + (x - W / 2) * iz;
264
+ const ipx = px | 0, ipy = py | 0;
265
+ const h1 = shash(ipx, ipy, 1), weave = 1 + 0.025 * Math.sin(px * 2.1) * Math.sin(py * 2.3) + (h1 - 0.5) * 0.05;
266
+ let r = cd[i] * weave, gg = cd[i + 1] * weave, b = cd[i + 2] * weave;
267
+ const gs = g[(scl(py / 720 * GH, 0, GH - 1) | 0) * GW + (scl(px / 1280 * GW, 0, GW - 1) | 0)];
268
+ // which panel is this pixel in?
269
+ let s = null, lx = 0;
270
+ if (rowOK) { const colj = Math.floor((px - QM) / (QW + QG)); lx = px - QM - colj * (QW + QG); if (colj >= 0 && colj < 3 && lx < QW) s = PS[rowj * 3 + colj]; }
271
+ let starve = 0, along = 0, st = 0;
272
+ if (s && s.on) {
273
+ along = s.dir > 0 ? lx / QW : 1 - lx / QW; st = s.st[ly | 0];
274
+ starve = scl((st * 0.9 + along * 0.9 - 0.75) * 1.6) * 0.85;
275
+ if (lx < s.fw) {
276
+ // flood: low-frequency ink load, a darker build-up where the flood bar starts and stops, and the colour
277
+ // thinning in the squeegee's starved streaks (as in the still)
278
+ let f = 1 + gs * 0.08;
279
+ const ed = Math.min(lx, QW - lx), ev = Math.min(ly, QH - ly);
280
+ if (ed < 2.4) f *= 0.86 + 0.05 * ed; else if (ev < 1.6) f *= 0.93 + 0.04 * ev;
281
+ r *= f; gg *= f; b *= f;
282
+ const cs = starve * 0.22 + (0.5 - h1) * 0.04;
283
+ r += (CANVAS[0] - r) * cs; gg += (CANVAS[1] - gg) * cs; b += (CANVAS[2] - b) * cs;
284
+ }
285
+ }
286
+ let ka = kd[i + 3] / 255;
287
+ if (s && along > s.kp * 1.05 - 0.02) ka = 0;
288
+ if (ka > 0.01) {
289
+ const thr = 0.5 + gs * 0.28 + (shash(ipx, ipy, 5) - 0.5) * 0.22;
290
+ let cov = scl((ka - thr) * 6 + 0.5);
291
+ if (cov > 0) {
292
+ let dens = 0.97 - starve * 0.55 + (shash(ipx, ipy, 9) - 0.5) * 0.1;
293
+ if (starve > 0.4 && shash(ipx, ipy >> 1, 11) < starve * 0.5) dens *= 0.4;
294
+ cov *= scl(dens);
295
+ r = r * (1 - cov) + K[0] * cov; gg = gg * (1 - cov) + K[1] * cov; b = b * (1 - cov) + K[2] * cov;
296
+ }
297
+ }
298
+ // ghost: a second, starved pull of the screen slipped sideways (two panels, as in the still)
299
+ if (s && s.gh && along <= s.kp * 1.05 - 0.02) {
300
+ const glx = lx - s.gh[0], gly = ly - s.gh[1];
301
+ if (glx >= 0 && gly >= 0 && glx < QW && gly < QH) {
302
+ const ka2 = kAt(x - s.gh[0] * z, y - s.gh[1] * z);
303
+ if (ka2 > 0.3) {
304
+ const cov = scl((ka2 - 0.55 - gs * 0.2) * 5 + 0.5) * (0.26 + 0.18 * st) * (shash(ipx, ipy, 17) < 0.55 ? 1 : 0.3);
305
+ r *= 1 - cov * 0.85; gg *= 1 - cov * 0.85; b *= 1 - cov * 0.85;
306
+ }
307
+ }
308
+ }
309
+ o[i] = r; o[i + 1] = gg; o[i + 2] = b; o[i + 3] = 255;
310
+ }
311
+ }
312
+ ctx.putImageData(out, 0, 0);
313
+ };
314
+
315
+ registerStyle({
316
+ id: 'screenprint-grid',
317
+ name: 'Silkscreen Grid',
318
+ category: 'Print',
319
+ description: 'A Warhol-style repeat portrait: the same robot pulled eight times through a grainy black photo screen over loose, hand-cut flat colour plates in garish combinations, each pull misregistered and streaked by the squeegee.',
320
+ motion: 'Panels are printed one after another across the grid — background flood, then the loose colour shapes slap down, then the squeegee drags the black photo screen across with a wet ink bead — before a slow push-in on the finished wall.',
321
+ fonts: ['400 50px "Anton"'],
322
+ duration: 4.5,
323
+ frameT: 4.4,
324
+ readsPixels: true,
325
+ explainerDuration: 16,
326
+ explainerNote: 'Each beat is its own silkscreened panel, pulled in order (flood, loose colour shapes, squeegee drags the black screen) while the camera travels the grid; black arrows are painted across the gutters between panels, counters are stencil type, emphasis is a colourway flicker, and the final pull-back reveals all six beats as one Warhol grid.',
327
+ explainer(ctx, W, H_, t) { spExplainer(ctx, W, H_, t); },
328
+ // Sound: a screen-printing bench. Each panel is a pull: a wet flood of ink, then the rubber squeegee drags
329
+ // across the mesh (following the pull direction), and the screen lifts off with a soft clack. Painted arrows
330
+ // are brush strokes, cards and stencil labels are small dabs, the colourway flicker on 97% is four quick
331
+ // re-floods after the big hit. Timings mirror PRINT / CONTENT / camAt / spExplainer above.
332
+ // Voiceover mode: room bed drops; screen lifts, card dabs, counter ticks, labels and the flicker are minor.
333
+ explainerSound(k) {
334
+ const D = 16;
335
+ 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; };
336
+ // ink flooded across the screen with the flood bar: a short wet swish
337
+ const flood = (t, g, minor) => k.noise(t, { minor: !!minor, dur: 0.24, color: 'pink', filter: 'bandpass', f: [[0, 650], [0.2, 1150]], q: 0.9, gain: 0.2 * g, a: 0.07 });
338
+ // squeegee: rubber blade dragging ink through the mesh over d seconds, panned along the pull; screen lifts after
339
+ const squeegee = (t, d, dir, g) => {
340
+ const p0 = -0.45 * dir, p1 = 0.45 * dir;
341
+ k.noise(t, { dur: d * 0.55, color: 'brown', filter: 'bandpass', f: [[0, 420], [d * 0.55, 720]], q: 0.9, gain: 0.55 * g, a: d * 0.3, hold: d * 0.2, pan: p0 });
342
+ k.noise(t + d * 0.45, { dur: d * 0.6, color: 'brown', filter: 'bandpass', f: [[0, 720], [d * 0.6, 480]], q: 0.9, gain: 0.55 * g, a: 0.05, hold: d * 0.25, pan: p1 });
343
+ // mesh texture: the blade chatters over the threads, densest mid-pull
344
+ k.withMinor(() => { for (let x = 0.03; x < d; x += 0.022) { const e = Math.sin(Math.PI * x / d); k.noise(t + x, { dur: 0.018, filter: 'bandpass', f: 1250, q: 2, gain: 0.12 * g * e, a: 0.002, pan: p0 + (p1 - p0) * (x / d) }); } });
345
+ // screen lifts off the paper
346
+ k.withMinor(() => { k.tone(t + d + 0.06, { f: [[0, 150], [0.06, 85]], dur: 0.09, gain: 0.13 * g }); k.noise(t + d + 0.06, { dur: 0.03, filter: 'bandpass', f: 900, q: 1.2, gain: 0.2 * g, a: 0.001 }); });
347
+ };
348
+ const pull = (i, g = 1) => { flood(PRINT[i], g); squeegee(PRINT[i] + 0.3, 0.4, i % 2 ? -1 : 1, g); };
349
+ // camera travels the grid: a soft smooth slide, nothing papery
350
+ const travel = (t0, t1, g = 1) => k.noise(t0, { minor: true, dur: t1 - t0, color: 'brown', filter: 'lowpass', f: [[0, 220], [(t1 - t0) / 2, 520], [t1 - t0, 220]], gain: 0.16 * g, a: (t1 - t0) * 0.45 });
351
+ const dab = (t, g, p) => k.withMinor(() => { k.noise(t, { dur: 0.04, filter: 'bandpass', f: 800, q: 1, gain: 0.26 * g, a: 0.002, pan: p }); k.tone(t, { f: 130, dur: 0.05, gain: 0.1 * g, pan: p }); });
352
+ const tick = (t, g, p) => k.noise(t, { minor: true, dur: 0.02, filter: 'bandpass', f: 2100, q: 1.6, gain: 0.24 * g, a: 0.001, pan: p });
353
+ const brush = (t, d, g, p) => k.play('stroke', t, { material: 'brush', dur: d, gain: g, pan: p, minor: true });
354
+
355
+ k.bed('room', 0, D, { gain: 0.2 });
356
+
357
+ // ---- panel 1: HOOK (the black screen pulls HOW DOES AI LEARN? on)
358
+ pull(0);
359
+ dab(1.0, 0.6, 0.5); dab(1.07, 0.45, 0.4); // the two ?s
360
+
361
+ // ---- panel 2: DATA
362
+ travel(2.25, 2.75);
363
+ pull(1);
364
+ for (let j = 1; j <= 12; j++) dab(3.15 + (j / 12) * 1.45, 0.75, -0.4 + ((j - 1) % 4) * 0.25); // contact sheet fills
365
+ // counter (1e6 * sseg(3.2, 5.5)^2.3): ticks once the sheet is full, speeding up toward 1,000,000
366
+ for (let t = 4.66; t < 5.46; ) { const s = sseg(t, 4.6, 5.5); tick(t, 0.8 + 0.3 * s, 0); t += 0.13 - 0.05 * s; }
367
+ k.play('stamp', 5.5, { gain: 0.65 }); // 1,000,000 EXAMPLES
368
+ brush(5.5, 0.3, 0.6, -0.1); // underline
369
+ brush(6.0, 0.3, 0.7, 0.4); // arrow painted across the gutter to panel 3
370
+
371
+ // ---- panel 3: GUESS / CHECK
372
+ travel(5.85, 6.35);
373
+ pull(2);
374
+ brush(6.8, 0.3, 1.4, 0.1); // GUESS arrow
375
+ dab(6.95, 0.6, 0);
376
+ k.play('slap', 7.15, { gain: 0.45, pan: 0.35 }); // DOG? card
377
+ k.play('stamp', 7.5, { gain: 0.8, pan: 0.4 }); // red X, CHECK, WRONG
378
+ k.play('error', 7.6, { f: 'E3', gain: 0.75, pan: 0.3 });
379
+ for (let i = 1; i < 4; i++) flood(7.5 + i * 0.1, 0.45, true); // panel flickers red
380
+
381
+ // ---- panel 4: ADJUST
382
+ travel(8.15, 8.65);
383
+ brush(8.4, 0.55, 1.4, 0.3); // error arrow painted down to the dials
384
+ pull(3);
385
+ dab(9.1, 0.6, 0.2); // ADJUST
386
+ for (let i = 1; i <= 4; i++) k.play('tock', when(9.25, 9.85, SE.inOut, (i - 0.5) / 4), { f: 500 + i * 30, gain: 0.35, pan: 0.25, minor: true }); // dials turn
387
+ k.play('slap', 10.0, { gain: 0.4, pan: 0.3 }); // CAT card
388
+ k.play('success', when(10.05, 10.25, SE.back, 1), { f: 'C5', gain: 0.55, pan: 0.4 });
389
+ brush(10.15, 0.25, 0.9, -0.3); // loop arrow back to guess again
390
+
391
+ // ---- panel 5: ACCURACY
392
+ travel(10.35, 10.85);
393
+ pull(4);
394
+ brush(11.38, 1.12, 0.6, -0.3); // the curve inks along
395
+ for (let t = 11.3, last = 61, lastT = -1; t < 12.52; t += 1 / 120) { const v = Math.round(61 + 36 * SE.inOut(sseg(t, 11.3, 12.5))); if (v !== last && t - lastT >= 0.1) { tick(t, 0.9, 0.3); last = v; lastT = t; } }
396
+ // 97%: the big hit, then the colourway flickers through four quick re-floods (8 per second)
397
+ k.play('impact', 12.5, { gain: 1.2, pan: 0.25 });
398
+ k.play('stamp', 12.5, { gain: 0.6, pan: 0.25 });
399
+ for (let i = 1; i < 4; i++) flood(12.5 + i * 0.125, 0.55, true);
400
+
401
+ // ---- panel 6: TAKEAWAY
402
+ travel(12.75, 13.25);
403
+ pull(5);
404
+ brush(13.75, 0.2, 0.7, 0); // arrow
405
+ ['C4', 'G4', 'C5', 'E5'].forEach((f, i) => k.play('ding', 13.95 + i * 0.06, { f, gain: 0.38 - i * 0.05, pan: (i - 1.5) * 0.2 }));
406
+ // pull back to the whole Warhol grid
407
+ travel(14.05, 14.75, 1.4);
408
+ k.play('ding', 14.75, { f: 'G4', gain: 0.22, pan: 0 });
409
+ },
410
+ draw(ctx, W, H_, t) {
411
+ const tone = toneImage();
412
+ const step = 5; // fixed edition (matches the gallery still)
413
+ // linen canvas ground
414
+ ctx.fillStyle = `rgb(${CANVAS})`; ctx.fillRect(0, 0, W, H_);
415
+ const panels = [];
416
+ for (let j = 0; j < ROWS; j++) for (let i = 0; i < COLS; i++) {
417
+ const idx = j * COLS + i, r = H.rng(100 + idx * 7 + step * 131);
418
+ const px = M + i * (PW + G) + r.range(-2, 2), py = M + j * (PH + G) + r.range(-2, 2);
419
+ const pal = PAL[(idx + step * 3) % PAL.length];
420
+ const kdx = r.range(-7, 7), kdy = r.range(-6, 6); // black screen offset
421
+ const heavy = [0.05, -0.04, 0.15, -0.07, 0.02, 0.1, -0.1, 0.07][idx]; // ink load per pull
422
+ panels.push({ px, py, kdx, kdy, heavy, seed: idx + 1 + step * 17, pullDir: r.chance(0.5) ? 1 : -1, ghost: (idx === 2 || idx === 5) ? [r.range(10, 16), r.range(-4, 4)] : null });
423
+ // print order: one panel after another
424
+ const T0 = 0.15 + idx * 0.25;
425
+ const pp = panels[panels.length - 1];
426
+ pp.keyProg = H.ease.inOut(H.clamp((t - T0 - 0.38) / 0.36));
427
+ pp.ghostOn = t > T0 + 0.82;
428
+ if (t < T0) {
429
+ ctx.strokeStyle = 'rgba(90,80,70,0.25)'; ctx.lineWidth = 1; ctx.strokeRect(px + 0.5, py + 0.5, PW - 1, PH - 1);
430
+ continue;
431
+ }
432
+ ctx.save(); ctx.translate(px, py);
433
+ ctx.beginPath(); ctx.rect(0, 0, PW * H.ease.out(H.clamp((t - T0) / 0.16)), PH); ctx.clip();
434
+ ctx.fillStyle = pal.bg; ctx.fillRect(0, 0, PW, PH);
435
+ const plateA = t > T0 + 0.18, plateB = t > T0 + 0.28;
436
+ // colour plates: cut loosely, placed a little off
437
+ const o = () => [r.range(-12, 12), r.range(-9, 9)];
438
+ let [dx, dy] = o();
439
+ ctx.fillStyle = plateA ? pal.body : 'rgba(0,0,0,0)';
440
+ cutShape(ctx, [[34 + dx, PH + 4], [44 + dx, 270 + dy], [70 + dx, 252 + dy], [232 + dx, 250 + dy], [258 + dx, 268 + dy], [272 + dx, PH + 4]]);
441
+ [dx, dy] = o();
442
+ ctx.fillStyle = plateA ? pal.head : 'rgba(0,0,0,0)';
443
+ ctx.save(); ctx.translate(150 + dx, 145 + dy); ctx.rotate(r.range(-0.05, 0.05));
444
+ cutShape(ctx, [[-80, -62], [-66, -76], [64, -78], [82, -60], [84, 64], [70, 78], [-70, 76], [-84, 60]]);
445
+ ctx.fillRect(-100, -27, 20, 56); ctx.fillRect(80, -27, 20, 56);
446
+ ctx.restore();
447
+ [dx, dy] = o();
448
+ ctx.fillStyle = plateB ? pal.eye : 'rgba(0,0,0,0)';
449
+ cutShape(ctx, blobAround(r, 114 + dx, 136 + dy, 34, 28, 9)); cutShape(ctx, blobAround(r, 188 + dx, 136 + dy, 34, 28, 9));
450
+ [dx, dy] = o();
451
+ ctx.fillStyle = plateB ? pal.mouth : 'rgba(0,0,0,0)';
452
+ cutShape(ctx, [[100 + dx, 176 + dy], [200 + dx, 174 + dy], [202 + dx, 206 + dy], [98 + dx, 209 + dy]]);
453
+ ctx.fillStyle = plateB ? pal.tip : 'rgba(0,0,0,0)'; ctx.beginPath(); ctx.arc(150 + dx * 0.6, 35 + dy * 0.6, 13, 0, 7); ctx.fill();
454
+ ctx.fillStyle = plateB ? pal.mouth : 'rgba(0,0,0,0)'; ctx.fillRect(108 + dx * 0.5, 276 + dy * 0.5, 84, 40);
455
+ ctx.restore();
456
+ }
457
+
458
+ // ---- per-pixel: pull the black screen, squeegee streaks, ink texture, canvas weave ----
459
+ const img = ctx.getImageData(0, 0, W, H_), d = img.data;
460
+ const nz = H.makeNoise(8);
461
+ // streak fields per panel (1D along pull direction = rows) precomputed
462
+ const streakRows = panels.map((p) => { const a = new Float32Array(PH); const n2 = H.makeNoise(p.seed * 13); for (let y = 0; y < PH; y++) a[y] = n2.fbm2(y * 0.045, 0.5, 3) + 0.6 * n2.n2(y * 0.3, 7.7); return a; });
463
+ // mid-frequency grain field (shared), low-res then sampled
464
+ const GW = 320, GH = 180, grain = new Float32Array(GW * GH);
465
+ for (let y = 0; y < GH; y++) for (let x = 0; x < GW; x++) grain[y * GW + x] = nz.fbm2(x * 0.22, y * 0.22, 3) * 0.7 + nz.n2(x * 0.9, y * 0.9) * 0.3;
466
+ const hash = (x, y, s) => { let h = (Math.imul(x, 374761393) + Math.imul(y, 668265263) + Math.imul(s, 1442695041)) | 0; h = Math.imul(h ^ (h >>> 13), 1274126177); h ^= h >>> 16; return (h >>> 0) / 4294967296; };
467
+ const K = [22, 18, 20];
468
+ for (let y = 0; y < H_; y++) for (let x = 0; x < W; x++) {
469
+ const i = (y * W + x) * 4;
470
+ // canvas weave
471
+ const weave = 1 + 0.025 * Math.sin(x * 2.1) * Math.sin(y * 2.3) + (hash(x, y, 1) - 0.5) * 0.05;
472
+ let r = d[i] * weave, g = d[i + 1] * weave, b = d[i + 2] * weave;
473
+ let pi = -1;
474
+ for (let k = 0; k < panels.length; k++) { const p = panels[k]; if (x >= p.px && x < p.px + PW && y >= p.py && y < p.py + PH) { pi = k; break; }
475
+ if (pi >= 0 && panels[pi].keyProg === undefined) pi = -1; }
476
+ if (pi >= 0) {
477
+ const p = panels[pi], lx = x - p.px, ly = y - p.py;
478
+ const gs = grain[((y * GH / H_) | 0) * GW + ((x * GW / W) | 0)];
479
+ // squeegee: ink starves in streaks and toward the end of the pull
480
+ const pull = p.pullDir > 0 ? lx / PW : 1 - lx / PW;
481
+ const st = streakRows[pi][ly | 0];
482
+ const starve = H.clamp((st * 0.9 + pull * 0.9 - 0.75) * 1.6) * 0.85;
483
+ // colour plates thin slightly in the starved streaks
484
+ const cs = starve * 0.22 + (hash(x, y, 3) - 0.5) * 0.04;
485
+ r += (CANVAS[0] - r) * cs; g += (CANVAS[1] - g) * cs; b += (CANVAS[2] - b) * cs;
486
+ // black photo screen (misregistered)
487
+ const tx = Math.round(lx - p.kdx), ty = Math.round(ly - p.kdy);
488
+ const kp = p.keyProg;
489
+ if (kp > 0 && kp < 1) { // wet ink bead riding in front of the squeegee
490
+ const fr = kp * (PW + 20) - 10, dd = pull * PW - fr;
491
+ if (dd > 0 && dd < 7) { const bead = 0.55 * (1 - dd / 7) * (0.8 + 0.2 * Math.sin(ly * 0.3)); r *= 1 - bead; g *= 1 - bead; b *= 1 - bead; }
492
+ }
493
+ if (pull * PW <= kp * (PW + 20) - 10 && tx >= 0 && ty >= 0 && tx < PW && ty < PH) {
494
+ const tv = tone[(ty * PW + tx) * 4] / 255;
495
+ const thr = 0.47 + p.heavy + gs * 0.14 + (hash(x, y, 5) - 0.5) * 0.16;
496
+ let cov = H.clamp((thr - tv) * 9);
497
+ if (cov > 0) {
498
+ let dens = 0.97 - starve * 0.9 + (hash(x, y, 9) - 0.5) * 0.1;
499
+ if (starve > 0.35 && hash(x, y >> 1, 11) < starve * 0.8) dens *= 0.2;
500
+ cov *= H.clamp(dens);
501
+ r = r * (1 - cov) + K[0] * cov; g = g * (1 - cov) + K[1] * cov; b = b * (1 - cov) + K[2] * cov;
502
+ }
503
+ }
504
+ // ghost: a second, starved pull of the screen slipped sideways
505
+ if (p.ghost && p.ghostOn) {
506
+ const gx = Math.round(lx - p.kdx - p.ghost[0]), gy = Math.round(ly - p.kdy - p.ghost[1]);
507
+ if (gx >= 0 && gy >= 0 && gx < PW && gy < PH) {
508
+ const tv = tone[(gy * PW + gx) * 4] / 255;
509
+ let cov = H.clamp((0.4 + gs * 0.2 - tv) * 6) * (0.32 + 0.25 * st) * (hash(x, y, 17) < 0.55 ? 1 : 0.25);
510
+ r *= 1 - cov * 0.9; g *= 1 - cov * 0.9; b *= 1 - cov * 0.9;
511
+ }
512
+ }
513
+ }
514
+ d[i] = r; d[i + 1] = g; d[i + 2] = b;
515
+ }
516
+ ctx.putImageData(img, 0, 0);
517
+ const z = 1 + 0.03 * H.ease.inOut(H.clamp((t - 2.4) / 2.1));
518
+ if (z > 1.0001) {
519
+ const cp = H.layer(W, H_); cp.getContext('2d').drawImage(ctx.canvas, 0, 0, W, H_);
520
+ ctx.drawImage(cp, 640 - 640 / z, 360 - 360 / z, W / z, H_ / z, 0, 0, W, H_);
521
+ }
522
+ },
523
+ });
524
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 80,
3
+ "name": "Silkscreen Grid",
4
+ "nameZh": "丝网印刷网格",
5
+ "family": "Print",
6
+ "familyZh": "印刷质感",
7
+ "description": "A Warhol-style repeat portrait: the same robot pulled eight times through a grainy black photo screen over loose, hand-cut flat colour plates in garish combinations, each pull misregistered and streaked by the squeegee.",
8
+ "motionNotes": "Motion: Panels are printed one after another across the grid — background flood, then the loose colour shapes slap down, then the squeegee drags the black photo screen across with a wet ink bead — before a slow push-in on the finished wall.",
9
+ "explainerNotes": "Explainer: Each beat is its own silkscreened panel, pulled in order (flood, loose colour shapes, squeegee drags the black screen) while the camera travels the grid; black arrows are painted across the gutters between panels, counters are stencil type, emphasis is a colourway flicker, and the final pull-back reveals all six beats as one Warhol grid.",
10
+ "code": "code.js",
11
+ "registeredAs": "screenprint-grid",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }