@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": 68,
3
+ "name": "Ballpoint Notebook",
4
+ "nameZh": "圆珠笔笔记",
5
+ "family": "Hand-Drawn",
6
+ "familyZh": "手绘",
7
+ "description": "A blue Bic doodle in the back of a college-ruled notebook: dense crosshatching for darks, pressure-varied lines with ink blobs where strokes start, bubble lettering with a hatched drop shadow and compulsive margin doodles.",
8
+ "motionNotes": "Motion: Lines scratch on stroke by stroke, then layers of crosshatching build up the shadows pass by pass, while the margin doodles get traced over and over.",
9
+ "explainerNotes": "Explainer: Doodled down one long college-ruled page: the pen draws while the camera scrolls along and then down the page, headings are bubble letters with crosshatched shadows, boxes get hatched drop-shadows, a red pen marks the wrong guess, and counters are re-inked in bubble numerals.",
10
+ "code": "code.js",
11
+ "registeredAs": "ballpoint-notebook",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }
@@ -0,0 +1,440 @@
1
+ {
2
+ // Op Art — Vasarely "Vega"-style warped checkerboard bulging into a sphere, with Riley-like
3
+ // undulating rows and column compression. Rendered per-pixel with analytic anti-aliasing.
4
+ const S = 40; // base cell size (px)
5
+
6
+
7
+ // =====================================================================================
8
+ // EXPLAINER — the model is the Vasarely lens. Untrained, its grid is melted noise; every
9
+ // correction (an error ripple travelling back through the field) combs it toward the clean
10
+ // optical sphere. Scenes change with a Riley fold: the picture pinches into crowded columns
11
+ // and unfolds as the next one.
12
+ // =====================================================================================
13
+ const OX = (t, a, b) => H.clamp((t - a) / (b - a));
14
+ const IVc = '#f0ebdd', BKc = '#141315', RDc = '#d02622';
15
+ let OPTEX = null;
16
+ function opTexture(W, H_) {
17
+ if (OPTEX) return OPTEX;
18
+ const nz = H.makeNoise(11), r = H.rng(5), T = new Float32Array(W * H_);
19
+ for (let y = 0; y < H_; y++) for (let x = 0; x < W; x++) {
20
+ const wx = Math.sin(x * 1.9) * 0.5 + 0.5, wy = Math.sin(y * 1.9) * 0.5 + 0.5;
21
+ const weave = ((x >> 1) + (y >> 1)) & 1 ? wx : wy;
22
+ const brush = nz.n2(x * 0.004, y * 0.03) * 0.5 + nz.n2(x * 0.05, y * 0.002) * 0.5;
23
+ T[y * W + x] = (weave - 0.5) * 0.04 + brush * 0.025 + (r() - 0.5) * 0.03;
24
+ }
25
+ return (OPTEX = T);
26
+ }
27
+ // the same canvas weave as a drawable overlay (white/black at |tex| alpha ~ multiply by 1+tex), for flat slabs and bands
28
+ let OPWEAVE = null;
29
+ function opWeave(W, H_) {
30
+ if (OPWEAVE && OPWEAVE.width === W && OPWEAVE.height === H_) return OPWEAVE;
31
+ const tex = opTexture(W, H_), c = H.layer(W, H_), x = c.getContext('2d'), img = x.createImageData(W, H_), d = img.data;
32
+ for (let i = 0; i < W * H_; i++) { const v = tex[i] * 1.6, o = i * 4; d[o] = d[o + 1] = d[o + 2] = v > 0 ? 255 : 0; d[o + 3] = Math.min(255, Math.abs(v) * 255); }
33
+ x.putImageData(img, 0, 0); return (OPWEAVE = c);
34
+ }
35
+ const weaveOver = (c, x, y, w, h) => { const wv = OPWEAVE; if (!wv || w <= 0 || h <= 0) return; const sx = H.clamp(Math.round(Math.abs(x)) % 600, 0, wv.width - 1), sy = H.clamp(Math.round(Math.abs(y)) % 300, 0, wv.height - 1); const sw = Math.min(w, wv.width - sx), sh = Math.min(h, wv.height - sy); c.drawImage(wv, sx, sy, sw, sh, x, y, w, h); };
36
+ // P: {cx, cy, R, lens, fold, chaos, red, rip:{r, amp}, t}
37
+ function opField(ctx, W, H_, P) {
38
+ const img = ctx.createImageData(W, H_), d = img.data, tex = opTexture(W, H_);
39
+ const GW = W + 1, GH = H_ + 1, G = new Float32Array(GW * GH), CU = new Float32Array(GW * GH), CV = new Float32Array(GW * GH);
40
+ const PI = Math.PI, S = 40, R2 = P.R * P.R, A0 = 1 - 0.58 * P.lens, tau = P.t;
41
+ const foldU = (x) => P.fold * (58 * Math.sin((x / W) * PI * 2.5 + 0.6 + tau * 0.25) - 9 * Math.sin((x / W) * PI * 6 + tau * 0.4));
42
+ const ux = P.cx + foldU(P.cx);
43
+ for (let y = 0; y < GH; y++) {
44
+ const yy = (y - P.cy) / 360;
45
+ for (let x = 0; x < GW; x++) {
46
+ let u = x + foldU(x);
47
+ let v = P.cy + (y - P.cy) * (1 + 0.42 * P.fold * yy * yy) + P.fold * 8 * Math.sin((y / H_) * PI * 2 + tau * 0.3);
48
+ const dx = x - P.cx, dy = y - P.cy, r2 = dx * dx + dy * dy;
49
+ if (P.rip && P.rip.amp > 0) { // error ripple: a radial ring that displaces the grid
50
+ const dd = Math.sqrt(r2) + 1e-3, q = (dd - P.rip.r) / 38, w = P.rip.amp * Math.exp(-q * q) * Math.sin(q * 3.2);
51
+ u += (dx / dd) * w; v += (dy / dd) * w;
52
+ }
53
+ if (r2 < R2) {
54
+ const rho = Math.sqrt(r2) / P.R, k = A0 + (1 - A0) * rho * rho, blend = 1 - rho * rho;
55
+ u = u * (1 - blend) + (ux + dx) * blend;
56
+ u = ux + (u - ux) * k; v = P.cy + (v - P.cy) * k;
57
+ if (P.chaos > 0) { // untrained: the lens grid is twisted and rippled (still hard-edged checks, just disordered geometry)
58
+ const th = Math.atan2(dy, dx), fall = 1 - rho * rho;
59
+ // smooth swirl (angle depends on radius) + a 3-fold wobble that vanishes at the centre and rim (no pinch points)
60
+ const ang = P.chaos * (1.15 * Math.sin(Math.PI * rho) * Math.sin(tau * 0.5 + 1.1) + 1.1 * rho * fall * Math.sin(3 * th + tau * 0.9));
61
+ const rr = P.chaos * P.R * 0.07 * Math.sin(rho * 9 - tau * 1.3) * fall * rho;
62
+ const ou = u - ux, ov = v - P.cy, ca = Math.cos(ang), sa = Math.sin(ang), rl = Math.hypot(ou, ov) + 1e-3;
63
+ u = ux + ou * ca - ov * sa + (ou / rl) * rr; v = P.cy + ou * sa + ov * ca + (ov / rl) * rr;
64
+ }
65
+ }
66
+ const i = y * GW + x;
67
+ G[i] = Math.sin((PI * (u - P.cx)) / S) * Math.sin((PI * (v - P.cy)) / S);
68
+ CU[i] = Math.floor((u - P.cx) / S); CV[i] = Math.floor((v - P.cy) / S);
69
+ }
70
+ }
71
+ const IV = [240, 235, 221], BK = [20, 19, 21], RD = [208, 38, 34];
72
+ for (let y = 0; y < H_; y++) for (let x = 0; x < W; x++) {
73
+ const i = y * GW + x, g = G[i];
74
+ const gx = G[i + 1] - g, gy = G[i + GW] - g, gm = Math.sqrt(gx * gx + gy * gy) + 1e-5;
75
+ let a = 0.5 + (g / gm) * 0.9; a = a < 0 ? 0 : a > 1 ? 1 : a;
76
+ let L = IV;
77
+ const dx = x - P.cx, dy = y - P.cy;
78
+ if (dx * dx + dy * dy < R2 && P.red > 0) {
79
+ let j = i;
80
+ if (g <= 0) { if (G[i + 1] > 0) j = i + 1; else if (G[i + GW] > 0) j = i + GW; else if (x > 0 && G[i - 1] > 0) j = i - 1; else if (y > 0 && G[i - GW] > 0) j = i - GW; }
81
+ const cr = Math.hypot((CU[j] + 0.5) * S, (CV[j] + 0.5) * S) / (P.R * 0.42);
82
+ if (cr < P.red) L = RD;
83
+ }
84
+ const o = (y * W + x) * 4, k = 1 + tex[y * W + x];
85
+ d[o] = (BK[0] + (L[0] - BK[0]) * a) * k; d[o + 1] = (BK[1] + (L[1] - BK[1]) * a) * k; d[o + 2] = (BK[2] + (L[2] - BK[2]) * a) * k; d[o + 3] = 255;
86
+ }
87
+ ctx.putImageData(img, 0, 0);
88
+ opWeave(W, H_);
89
+ }
90
+ // hard-edged label: black slab, ivory type, red keyline (or red slab)
91
+ function oLabel(c, text, x, y, o = {}) {
92
+ const sz = o.size || 20, f = o.big ? `400 ${sz}px "Archivo Black"` : `700 ${sz}px "Space Grotesk"`;
93
+ c.save(); c.font = f; c.textBaseline = 'middle'; try { c.letterSpacing = o.big ? '1px' : '2px'; } catch (e) {}
94
+ const w = c.measureText(text).width + sz * 1.1, h = sz * 1.75;
95
+ const ox = o.align === 'center' ? x - w / 2 : o.align === 'right' ? x - w : x;
96
+ c.fillStyle = o.red ? RDc : o.inv ? IVc : BKc; c.fillRect(ox, y - h / 2, w, h);
97
+ if (!o.red) { c.fillStyle = RDc; c.fillRect(ox, y + h / 2, w, Math.max(3, sz * 0.16)); }
98
+ weaveOver(c, ox, y - h / 2, w, h + (o.red ? 0 : Math.max(3, sz * 0.16)));
99
+ if (o.inv) { c.strokeStyle = BKc; c.lineWidth = 3; c.strokeRect(ox, y - h / 2, w, h); }
100
+ c.fillStyle = o.inv ? BKc : IVc;
101
+ if (o.lead && text.startsWith('→ ')) { // hard-edged arrow in the type's own weight instead of the thin fallback glyph
102
+ const lw0 = c.measureText('→ ').width, aw = lw0 - sz * 0.32, x0 = ox + sz * 0.55, sw = sz * 0.2;
103
+ c.beginPath(); c.moveTo(x0, y - sw / 2); c.lineTo(x0 + aw - sz * 0.42, y - sw / 2); c.lineTo(x0 + aw - sz * 0.42, y - sz * 0.32); c.lineTo(x0 + aw, y); c.lineTo(x0 + aw - sz * 0.42, y + sz * 0.32); c.lineTo(x0 + aw - sz * 0.42, y + sw / 2); c.lineTo(x0, y + sw / 2); c.closePath(); c.fill();
104
+ c.fillText(text.slice(2), x0 + lw0, y + 1);
105
+ } else c.fillText(text, ox + sz * 0.55, y + 1);
106
+ c.restore(); return w;
107
+ }
108
+ function oArrow(c, x0, y0, x1, y1, p, lw = 8) {
109
+ if (p <= 0) return; const x = x0 + (x1 - x0) * p, y = y0 + (y1 - y0) * p, a = Math.atan2(y1 - y0, x1 - x0), hd = lw * 3;
110
+ c.save();
111
+ for (const [col, ex] of [[IVc, 6], [RDc, 0]]) {
112
+ c.strokeStyle = col; c.fillStyle = col; c.lineWidth = lw + ex; c.lineJoin = 'miter';
113
+ c.beginPath(); c.moveTo(x0, y0); c.lineTo(x - Math.cos(a) * hd * 0.8, y - Math.sin(a) * hd * 0.8); c.stroke();
114
+ c.beginPath(); c.moveTo(x + Math.cos(a) * ex * 0.6, y + Math.sin(a) * ex * 0.6); c.lineTo(x - Math.cos(a - 0.5) * (hd + ex), y - Math.sin(a - 0.5) * (hd + ex)); c.lineTo(x - Math.cos(a + 0.5) * (hd + ex), y - Math.sin(a + 0.5) * (hd + ex)); c.closePath(); c.fill();
115
+ }
116
+ c.restore();
117
+ }
118
+ function oPet(c, kind, x, y, s) {
119
+ c.save(); c.fillStyle = IVc; c.fillRect(x, y, s, s); weaveOver(c, x, y, s, s); c.strokeStyle = BKc; c.lineWidth = Math.max(3, s * 0.035); c.strokeRect(x, y, s, s);
120
+ const cx = x + s / 2, cy = y + s / 2; c.fillStyle = BKc;
121
+ c.beginPath(); c.arc(cx, cy + s * 0.08, s * 0.28, 0, Math.PI * 2); c.fill();
122
+ if (kind === 'cat') for (const k of [-1, 1]) { c.beginPath(); c.moveTo(cx + k * s * 0.06, cy - s * 0.1); c.lineTo(cx + k * s * 0.28, cy - s * 0.34); c.lineTo(cx + k * s * 0.28, cy); c.fill(); }
123
+ else { for (const k of [-1, 1]) { c.beginPath(); c.ellipse(cx + k * s * 0.29, cy + s * 0.08, s * 0.09, s * 0.21, k * 0.3, 0, Math.PI * 2); c.fill(); } }
124
+ c.fillStyle = IVc; for (const k of [-1, 1]) c.fillRect(cx + k * s * 0.11 - s * 0.04, cy, s * 0.08, s * 0.08);
125
+ c.restore();
126
+ }
127
+ function band(c, W, y, h) { c.fillStyle = IVc; c.fillRect(0, y, W, h); weaveOver(c, 0, y, W, h); c.fillStyle = BKc; c.fillRect(0, y, W, 5); c.fillRect(0, y + h - 5, W, 5); }
128
+ const oHeader = (c) => oLabel(c, 'HOW DOES AI LEARN?', 22, 32, { size: 28, big: true });
129
+ const fmtN = (n) => Math.round(n).toLocaleString('en-US');
130
+
131
+ // scene field params + overlays
132
+ const OS = {
133
+ hook: { P: (t) => ({ cx: 740, cy: 360, R: 268 + 6 * Math.sin(t * 2), lens: 1, fold: 1, chaos: 0, red: 1.183, t }),
134
+ O(c, W, H_, t) {
135
+ const k = H.ease.inOut(OX(t, 1.7, 2.3)), pop = H.ease.back(OX(t, 0.1, 0.45));
136
+ if (k < 1) {
137
+ c.save(); c.font = '400 52px "Archivo Black"'; try { c.letterSpacing = '1px'; } catch (e) {} const w0 = c.measureText('HOW DOES AI LEARN?').width + 57; c.restore();
138
+ const SC = 28 / 52, sc = 1 - (1 - SC) * k, ex = 22 + (w0 * SC) / 2;
139
+ c.save(); c.translate(640 + (ex - 640) * k, 360 + (32 - 360) * k); c.scale(pop * sc, pop * sc); oLabel(c, 'HOW DOES AI LEARN?', 0, 0, { size: 52, big: true, align: 'center' }); c.restore();
140
+ }
141
+ else oHeader(c);
142
+ } },
143
+ data: { P: (t) => ({ cx: 1010, cy: 330, R: 205, lens: 1, fold: 0.8, chaos: 1, red: 0.25 + 0.3 * OX(t, 2.5, 6), t }),
144
+ O(c, W, H_, t) {
145
+ oHeader(c);
146
+ const rows = [170, 300, 430];
147
+ rows.forEach((y, i) => oArrow(c, 30, y + 40, 770, y + 40, H.ease.out(OX(t, 2.55 + i * 0.07, 2.95 + i * 0.07)), 6));
148
+ const rr = H.rng(9), K = 36;
149
+ for (let k = 0; k < K; k++) {
150
+ const kind = rr() < 0.5 ? 'cat' : 'dog', ts = 2.6 + 3.1 * Math.pow(k / K, 0.8);
151
+ if (t < ts) continue;
152
+ let x = -90 + (t - ts) * (520 + k * 16); const y = rows[k % 3];
153
+ let s = 80; if (x > 760) { const q = H.clamp((x - 760) / 160); s = 80 * (1 - q); x = 760 + (x - 760) * 0.6; }
154
+ if (s < 3) continue;
155
+ oPet(c, kind, x, y + (80 - s) / 2, s);
156
+ if (s > 60) oLabel(c, kind, x + s / 2, y + s + 26, { size: 28, align: 'center' });
157
+ }
158
+ oLabel(c, 'MODEL', 1010, 92 + (1 - H.ease.back(OX(t, 2.7, 3.0))) * -60, { size: 32, big: true, align: 'center', red: true });
159
+ band(c, W, 600, 100);
160
+ if (t >= 2.7) {
161
+ const p = OX(t, 2.75, 5.0), n = p >= 1 ? 1e6 : Math.floor(Math.pow(10, 6 * H.ease.inOut(p))) - 1;
162
+ c.save(); c.font = '400 72px "Archivo Black"'; c.textBaseline = 'middle'; c.fillStyle = BKc; c.fillText(fmtN(n), 34, 654);
163
+ const w = c.measureText(fmtN(n)).width; c.restore();
164
+ oLabel(c, 'EXAMPLES', 34 + w + 22, 650, { size: 30, red: true });
165
+ }
166
+ } },
167
+ loop: { P: (t) => {
168
+ const adj = H.ease.inOut(OX(t, 8.9, 9.4));
169
+ return { cx: 640, cy: 300, R: 190, lens: 1, fold: 0.6, chaos: 0.9 - 0.55 * adj, red: 0.55 + 0.35 * adj, t,
170
+ rip: t > 8.35 && t < 9.05 ? { r: 620 - 640 * OX(t, 8.35, 9.0), amp: 22 * Math.sin(Math.PI * OX(t, 8.35, 9.05)) } : null };
171
+ },
172
+ O(c, W, H_, t) {
173
+ oHeader(c);
174
+ oPet(c, 'cat', 60, 225, 150);
175
+ oLabel(c, t >= 8.0 ? 'ANSWER: CAT' : 'NEW PICTURE', 135, 414, { size: 28, align: 'center', red: t >= 8.0 });
176
+ oArrow(c, 222, 300, 436, 300, H.ease.out(OX(t, 6.4, 6.65)));
177
+ oArrow(c, 844, 300, 940, 300, H.ease.out(OX(t, 7.05, 7.25)));
178
+ oLabel(c, 'MODEL', 640, 76, { size: 28, big: true, align: 'center', red: true });
179
+ const right = t >= 9.6, drop = (t0) => (1 - H.ease.back(OX(t, t0, t0 + 0.25))) * -50;
180
+ if (t >= 7.25 && !right) {
181
+ const s = 1 + 0.3 * Math.pow(1 - H.ease.out(OX(t, 7.25, 7.4)), 2);
182
+ c.save(); c.translate(1090, 300); c.scale(s, s); oLabel(c, 'DOG?', 0, 0, { size: 54, big: true, align: 'center' }); c.restore();
183
+ oLabel(c, 'GUESS', 1090, 212 + drop(7.3), { size: 28, align: 'center', inv: true });
184
+ }
185
+ if (t >= 7.85 && !right) {
186
+ const k = H.ease.out(OX(t, 7.85, 8.05));
187
+ c.save(); c.strokeStyle = RDc; c.lineWidth = 10; c.beginPath(); c.arc(1090, 300, 110, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * k); c.stroke();
188
+ if (k >= 1) { c.lineWidth = 14; c.beginPath(); c.moveTo(1020, 230); c.lineTo(1160, 370); c.moveTo(1160, 230); c.lineTo(1020, 370); c.stroke(); }
189
+ c.restore();
190
+ oLabel(c, 'CHECK: WRONG', 1090, 450 + drop(7.95), { size: 28, align: 'center', red: true });
191
+ }
192
+ if (t >= 8.35 && t < 9.7) oLabel(c, 'ERROR RIPPLES BACK', 885, 520 + drop(8.35), { size: 28, align: 'center' });
193
+ if (t >= 8.85 && t < 10.4) oLabel(c, 'ADJUST: GRID SHARPENS', 460, 548 + drop(8.85), { size: 28, align: 'center', red: true });
194
+ if (right) {
195
+ const s = 1 + 0.3 * Math.pow(1 - H.ease.out(OX(t, 9.6, 9.75)), 2);
196
+ c.save(); c.translate(1090, 300); c.scale(s, s); oLabel(c, 'CAT ✓', 0, 0, { size: 54, big: true, align: 'center', red: true }); c.restore();
197
+ }
198
+ band(c, W, 600, 100);
199
+ const words = ['GUESS', 'CHECK', 'ADJUST'];
200
+ const act = t < 7.25 ? -1 : t < 7.85 ? 0 : t < 8.35 ? 1 : t < 9.6 ? 2 : Math.floor((t - 9.6) / 0.15) % 3;
201
+ let x = 34;
202
+ words.forEach((w, i) => { x += oLabel(c, w, x, 650, { size: 28, big: true, red: i === act }) + 14; if (i < 2) { oArrow(c, x, 650, x + 50, 650, 1, 6); x += 70; } });
203
+ c.save(); c.font = '700 28px "Space Grotesk"'; c.fillStyle = BKc; c.textBaseline = 'middle'; c.fillText('↻ ×1,000,000', x + 10, 652); c.restore();
204
+ } },
205
+ prog: { P: (t) => { const p = H.ease.inOut(OX(t, 10.85, 12.3)); return { cx: 950, cy: 340, R: 230, lens: 1, fold: 0.6 + 0.4 * p, chaos: 0.35 * (1 - p), red: 0.9 + 0.283 * p, t }; },
206
+ O(c, W, H_, t) {
207
+ oHeader(c);
208
+ c.fillStyle = IVc; c.fillRect(30, 90, 560, 560); weaveOver(c, 30, 90, 560, 560); c.fillStyle = BKc; c.fillRect(30, 90, 560, 5); c.fillRect(30, 645, 560, 5);
209
+ oLabel(c, 'ACCURACY', 60, 140, { size: 32, big: true });
210
+ const p = H.ease.inOut(OX(t, 10.85, 12.3)), v = Math.round(61 + 36 * p), punch = t >= 12.3 ? 1 + 0.15 * Math.exp(-(t - 12.3) * 9) : 1;
211
+ c.save(); c.translate(60, 290); c.scale(punch, punch); c.font = '400 150px "Archivo Black"'; c.textBaseline = 'middle'; c.fillStyle = BKc; c.fillText(v + '%', 0, 0); const w = c.measureText('97%').width; c.restore();
212
+ if (t >= 12.3) { const k = H.ease.out(OX(t, 12.3, 12.55)); c.save(); c.strokeStyle = RDc; c.lineWidth = 9; c.beginPath(); c.ellipse(60 + w / 2, 290, w / 2 + 26, 100, 0, Math.PI, Math.PI + Math.PI * 2 * k); c.stroke(); c.restore(); }
213
+ // stepped chart: each training round a column of checker cells
214
+ const x0 = 70, y0 = 610, cs = 20, n = 12;
215
+ for (let i = 0; i < n; i++) {
216
+ const acc = 61 + 36 * (1 - Math.exp(-i / 3.2)) / (1 - Math.exp(-(n - 1) / 3.2)), m = Math.round((acc - 50) / 47 * 9);
217
+ for (let k = 0; k < m; k++) { if (t < 10.85 + i * 0.11 + k * 0.02) break; c.fillStyle = (i + k) % 2 ? BKc : (i === n - 1 && t >= 12.3 ? RDc : '#3a383c'); c.fillRect(x0 + i * (cs + 22), y0 - (k + 1) * cs, cs + 18, cs); }
218
+ }
219
+ c.save(); c.font = '700 22px "Space Grotesk"'; c.fillStyle = BKc; try { c.letterSpacing = '2px'; } catch (e) {} c.fillText('TRAINING ROUNDS →', x0, y0 + 30); c.restore();
220
+ oLabel(c, 'MODEL', 950, 76, { size: 28, big: true, align: 'center', red: true });
221
+ } },
222
+ take: { P: (t) => ({ cx: 740, cy: 360, R: 268 + 6 * Math.sin(t * 2), lens: 1, fold: 1, chaos: 0, red: 1.183, t }),
223
+ O(c, W, H_, t) {
224
+ const k = H.ease.back(OX(t, 13.25, 13.55));
225
+ c.save(); c.translate(640, 360); c.scale(k, k);
226
+ oLabel(c, 'MORE EXAMPLES', 0, -50, { size: 50, big: true, align: 'center' });
227
+ oLabel(c, '→ BETTER GUESSES', 0, 52, { size: 50, big: true, align: 'center', red: true, lead: true });
228
+ c.restore();
229
+ } },
230
+ };
231
+ function opScene(c, W, H_, name, t) { opField(c, W, H_, OS[name].P(t)); OS[name].O(c, W, H_, t); }
232
+ function explainOP(ctx, W, H_, t) {
233
+ const seq = [[0, 'hook'], [2.45, 'data'], [6.0, 'loop'], [10.5, 'prog'], [13.0, 'take']];
234
+ let i = 0; for (let k = 0; k < seq.length; k++) if (t >= seq[k][0]) i = k;
235
+ const t0 = seq[i][0], D = 0.5;
236
+ if (i > 0 && t < t0 + D) {
237
+ // Riley fold wipe: the old picture is crushed into crowded columns at a travelling fold
238
+ const A = H.layer(W, H_), B = H.layer(W, H_);
239
+ opScene(A.getContext('2d'), W, H_, seq[i - 1][1], t0 - 0.01); opScene(B.getContext('2d'), W, H_, seq[i][1], t);
240
+ const sx = -120 + (W + 240) * H.ease.inOut(OX(t, t0, t0 + D)), st = 4;
241
+ for (let x = 0; x < W; x += st) {
242
+ const dd = x - sx, src = sx + dd * (1 + 3.2 * Math.exp(-Math.abs(dd) / 70)), sxp = H.clamp(src, 0, W - st);
243
+ ctx.drawImage(dd < 0 ? B : A, sxp, 0, st, H_, x, 0, st, H_);
244
+ }
245
+ ctx.fillStyle = BKc; ctx.fillRect(sx - 3, 0, 6, H_);
246
+ } else opScene(ctx, W, H_, seq[i][1], t);
247
+ H.vignette(ctx, W, H_, 0.18);
248
+ }
249
+
250
+ registerStyle({
251
+ id: 'op-art',
252
+ name: 'Op Art Sphere',
253
+ category: 'Generative & Type',
254
+ description: 'Hard-edged black/ivory checkerboard that swells into a lens-like sphere and ripples at the margins — Vasarely and Riley optical vibration, flat acrylic on primed canvas.',
255
+ motion: 'A flat checkerboard first ripples into Riley folds, then swells into a lens-sphere that pops past its size and settles; red squares flip in cell by cell from the centre, then the field breathes and the folds drift.',
256
+ fonts: ['600 13px "Space Grotesk"', '700 20px "Space Grotesk"', '400 40px "Archivo Black"'],
257
+ duration: 5,
258
+ frameT: 4.9,
259
+ readsPixels: true,
260
+ explainerDuration: 14.5,
261
+ explainerNote: 'The model is the Vasarely lens: untrained its grid is melted noise, the error travels back as an optical ripple through the checkerboard and each adjustment combs the sphere toward perfect order; scenes change with a Riley fold that crushes the picture into crowded columns, labels are hard-edged black and red slabs, and the numbers are Archivo Black on ivory bands.',
262
+ explainer(ctx, W, H_, t) { explainOP(ctx, W, H_, t); },
263
+ // Sound: clean optical pulses, no hand-made noise. Hard slabs land with a short low knock, the Riley fold is a
264
+ // ratcheting zip of crushed columns that sweeps left to right with the fold, the error ripple is a train of sine
265
+ // pulses converging on the lens, and "grid sharpens" is two detuned sines whose beating slows into unison.
266
+ // A quiet sine pad re-breathes every π s, the period the sphere breathes at (R = 268 + 6 sin 2t).
267
+ // Timings mirror explainOP / OS above (seq starts, OX windows, ease curves).
268
+ explainerSound(k) {
269
+ const D = 14.5, E = H.ease;
270
+ const pan = (px) => H.clamp((px / 1280) * 2 - 1, -1, 1) * 0.7;
271
+ // first time an eased progress over [t0,t1] reaches u
272
+ 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; };
273
+ // optical pulse: a clean sine blip with a faint triangle edge
274
+ const pulse = (t, f, g, p, dur = 0.14) => { k.tone(t, { f, dur, gain: 0.2 * g, a: 0.003, pan: p }); k.tone(t, { f: k.hz(f) * 2, type: 'triangle', dur: dur * 0.5, gain: 0.04 * g, a: 0.002, pan: p }); };
275
+ // hard-edged slab landing: short damped knock, no paper
276
+ const slab = (t, g, p, f = 150) => { k.tone(t, { f: [[0, f], [0.07, f * 0.55]], dur: 0.12, gain: 0.32 * g, a: 0.002, pan: p }); k.noise(t, { dur: 0.025, filter: 'bandpass', f: 1400, q: 1.2, gain: 0.18 * g, a: 0.001, pan: p }); };
277
+ // a red arrow bar extending: a short smooth rising zip
278
+ const zip = (t, dur, p) => k.noise(t, { dur, color: 'pink', filter: 'bandpass', f: [[0, 500], [dur, 1500]], q: 1.4, gain: 0.08, a: dur * 0.5, pan: p, minor: true });
279
+ // Riley fold wipe: sx = -120 + 1520 * inOut((t - t0) / 0.5); one column-crush click per 120 px it passes
280
+ const fold = (t0) => {
281
+ const sx = (t) => -120 + 1520 * E.inOut(H.clamp((t - t0) / 0.5));
282
+ k.noise(t0, { dur: 0.5, color: 'pink', filter: 'bandpass', f: [[0, 300], [0.25, 1100], [0.5, 350]], q: 1.1, gain: 0.14, a: 0.24 });
283
+ k.tone(t0, { f: [[0, 70], [0.25, 110], [0.5, 60]], dur: 0.5, gain: 0.09, a: 0.22 });
284
+ k.withMinor(() => { for (let x = 0; x <= 1280; x += 120) { const t = when(t0, t0 + 0.5, (u) => sx(t0 + u * 0.5) / 1280, x / 1280); k.noise(t, { dur: 0.018, filter: 'bandpass', f: 2200, q: 2, gain: 0.16, a: 0.001, pan: pan(x) }); k.tone(t, { f: 180, dur: 0.03, gain: 0.07, pan: pan(x) }); } });
285
+ };
286
+ // throttled counter: one click per step of the eased progress, at most `rate` per second
287
+ const counter = (t0, t1, ease, steps, rate, f, p) => { let last = -1, prev = 0;
288
+ for (let t = t0; t <= t1; t += 0.002) { const s = Math.floor(ease(H.clamp((t - t0) / (t1 - t0))) * steps); if (s !== prev && t - last >= 1 / rate) { k.noise(t, { dur: 0.016, filter: 'bandpass', f, q: 2, gain: 0.2, a: 0.001, pan: p, minor: true }); k.tone(t, { f: f * 0.1, dur: 0.025, gain: 0.05, pan: p, minor: true }); last = t; } prev = s; } };
289
+
290
+ // ---- underneath: soft gallery room + a sine pad that breathes with the sphere (bar = π s)
291
+ k.bed('room', 0, D, { gain: 0.15 });
292
+ k.music('pad', 0, D, { bpm: 240 / Math.PI, gain: 0.4, chords: [['A2', 'E3', 'A3', 'C4'], ['F2', 'C3', 'A3', 'E4'], ['D3', 'A3', 'C4', 'F4'], ['A2', 'E3', 'A3', 'C4'], ['C3', 'G3', 'C4', 'E4']] });
293
+
294
+ // ---- hook: title slab pops in (back, 0.1-0.45), shrinks into the header (1.7-2.3)
295
+ k.tone(0.1, { f: [[0, 160], [0.2, 330]], dur: 0.22, gain: 0.1, a: 0.12, minor: true });
296
+ const tl = when(0.1, 0.45, E.back, 1);
297
+ slab(tl, 0.85, 0); pulse(tl, 'A3', 0.7, 0, 0.5);
298
+ k.noise(1.7, { dur: 0.6, color: 'pink', filter: 'lowpass', f: [[0, 700], [0.6, 250]], gain: 0.03, a: 0.25, minor: true }); // soft slide to the corner
299
+
300
+ // ---- data: fold in, three arrows, MODEL slab drops, cards swallowed by the lens, counter
301
+ fold(2.45);
302
+ [0, 1, 2].forEach((i) => zip(2.55 + i * 0.07, 0.35, -0.3 + i * 0.05));
303
+ slab(when(2.7, 3.0, E.back, 1), 0.7, pan(1010), 180);
304
+ { // card k spawns at ts = 2.6 + 3.1 (k/36)^0.8 and enters the lens (x = 760) at ts + 850/(520 + 16k)
305
+ let last = -1; const enter = [];
306
+ for (let c = 0; c < 36; c++) enter.push(2.6 + 3.1 * Math.pow(c / 36, 0.8) + 850 / (520 + 16 * c));
307
+ enter.sort((a, b) => a - b).forEach((t, i) => { if (t - last < 0.16 || t > 6.0) return; last = t; k.tone(t, { f: [[0, 260 + (i % 4) * 30], [0.08, 140]], dur: 0.1, gain: 0.09, a: 0.003, pan: pan(800), minor: true }); });
308
+ }
309
+ // n = 10^(6 inOut(OX(t, 2.75, 5.0))) - 1: slow, fast, slow; at most 11 clicks/s
310
+ counter(2.76, 4.98, E.inOut, 30, 11, 2600, pan(180));
311
+ slab(5.0, 1.1, pan(200), 120); pulse(5.0, 'E3', 1, pan(200), 0.45); // 1,000,000 lands
312
+
313
+ // ---- loop: fold in, arrows, DOG? guess, wrong, ripple back, grid sharpens, CAT ✓
314
+ fold(6.0);
315
+ zip(6.4, 0.25, pan(330));
316
+ zip(7.05, 0.2, pan(890));
317
+ slab(7.25, 0.9, pan(1090), 170); pulse(7.25, 'E4', 0.9, pan(1090), 0.25); // DOG? punches in
318
+ k.play('tock', when(7.3, 7.55, E.back, 1), { f: 520, gain: 0.35, pan: pan(1090), minor: true }); // GUESS tag
319
+ k.tone(7.85, { f: [[0, 220], [0.2, 440]], type: 'triangle', dur: 0.22, gain: 0.08, a: 0.15, pan: pan(1090), minor: true }); // red ring draws round
320
+ slab(8.05, 1.1, pan(1090), 130); k.play('error', 8.05, { f: 'A3', gain: 0.8, pan: pan(1090) }); // X: wrong
321
+ // error ripple: ring r = 620 -> -20 over 8.35-9.0, amp sin(pi u): a pulse train converging on the lens
322
+ for (let i = 0; i < 9; i++) { const t = 8.37 + i * 0.075, u = H.clamp((t - 8.35) / 0.7), a = Math.sin(Math.PI * u);
323
+ pulse(t, 300 + 260 * u, 0.2 + 0.4 * a, 0.45 * (1 - u), 0.1); }
324
+ k.play('tock', when(8.85, 9.1, E.back, 1), { f: 440, gain: 0.3, pan: pan(480), minor: true }); // ADJUST tag
325
+ // grid sharpens 8.9-9.4: beating between two sines slows to unison as the chaos combs out
326
+ k.tone(8.9, { f: 330, dur: 0.75, gain: 0.045, a: 0.08, hold: 0.45 });
327
+ k.tone(8.9, { f: [[0, 352], [0.5, 330.5]], dur: 0.75, gain: 0.045, a: 0.08, hold: 0.45 });
328
+ k.tone(8.9, { f: 165, dur: 0.75, gain: 0.035, a: 0.1, hold: 0.4 });
329
+ slab(9.6, 0.9, pan(1090), 190); k.play('success', 9.6, { f: 'E5', gain: 0.75, pan: pan(1090) }); // CAT ✓
330
+ // the loop keeps cycling GUESS -> CHECK -> ADJUST every 0.15 s
331
+ k.withMinor(() => { for (let i = 1; i <= 5; i++) pulse(9.6 + i * 0.15, ['G3', 'C4', 'E4'][i % 3], 0.35, pan(300), 0.09); });
332
+
333
+ // ---- progress: fold in, checker columns stack, counter 61 -> 97, the 97% hit
334
+ fold(10.5);
335
+ k.withMinor(() => { for (let i = 0; i < 12; i++) { const acc = 61 + (36 * (1 - Math.exp(-i / 3.2))) / (1 - Math.exp(-11 / 3.2)); pulse(10.85 + i * 0.11, 150 + (acc - 61) * 5, 0.45, pan(70 + i * 42), 0.1); } });
336
+ counter(10.86, 12.28, E.inOut, 36, 9, 3000, pan(160));
337
+ k.play('impact', 12.3, { gain: 1.2, pan: pan(200) });
338
+ ['A2', 'E3', 'A3', 'C#4'].forEach((n, i) => k.tone(12.3, { f: n, dur: 1.1, gain: 0.09 - i * 0.012, a: 0.004, verb: 0.3, pan: (i - 1.5) * 0.2 }));
339
+ k.tone(12.3, { f: [[0, 260], [0.25, 520]], type: 'triangle', dur: 0.28, gain: 0.07, a: 0.2, pan: pan(200), minor: true }); // red ring
340
+
341
+ // ---- takeaway: fold in, slabs pop, resolving pulse chord
342
+ fold(13.0);
343
+ const tk = when(13.25, 13.55, E.back, 1);
344
+ slab(tk, 1, 0, 160);
345
+ ['A2', 'E3', 'A3', 'C#4', 'E4'].forEach((n, i) => k.tone(tk + i * 0.04, { f: n, dur: 1.15, gain: 0.08, a: 0.01, verb: 0.35, pan: (i - 2) * 0.2 }));
346
+ },
347
+ draw(ctx, W, H_, t) {
348
+ const img = ctx.createImageData(W, H_), d = img.data;
349
+ // timeline: folds ripple in (0.2-1.6s), lens swells with overshoot (1.1-2.6s),
350
+ // red cells flip in on twos from the centre (2.3-3.4s), then ambient breathing.
351
+ const tau = t - 2.9; // ambient phase; at frameT matches the still
352
+ const fold = H.ease.inOut(H.clamp((t - 0.05) / 1.3));
353
+ const lensE = H.clamp((t - 1.1) / 1.5);
354
+ const lens = lensE <= 0 ? 0 : H.ease.back(lensE);
355
+ const tq = Math.floor(t * 15) / 15; // on twos
356
+ const redThr = 1.183 * H.ease.out(H.clamp((tq - 2.3) / 1.1));
357
+ const cx = 740 + Math.sin(tau * 0.4) * 30, cy = 360;
358
+ const R = 268 + Math.sin(tau * 0.9) * 6;
359
+ const R2 = R * R;
360
+ const nz = H.makeNoise(11);
361
+
362
+ // --- field: Riley column folds outside, Vasarely lens bulge inside (grid stays continuous)
363
+ const GW = W + 1, GH = H_ + 1;
364
+ const G = new Float32Array(GW * GH);
365
+ const CU = new Float32Array(GW * GH), CV = new Float32Array(GW * GH);
366
+ const PI = Math.PI, A0 = 1 - 0.58 * lens;
367
+ for (let y = 0; y < GH; y++) {
368
+ for (let x = 0; x < GW; x++) {
369
+ // Riley "Movement in Squares": columns pinch together toward two vertical folds
370
+ let u = x + fold * (58 * Math.sin((x / W) * PI * 2.5 + 0.6 + tau * 0.25) - 9 * Math.sin((x / W) * PI * 6 + tau * 0.4));
371
+ const yy = (y - cy) / 360; let v = cy + (y - cy) * (1 + 0.42 * fold * yy * yy) + fold * 8 * Math.sin((y / H_) * PI * 2 + tau * 0.3);
372
+ const dx = x - cx, dy = y - cy, r2 = dx * dx + dy * dy;
373
+ if (r2 < R2) {
374
+ const rho = Math.sqrt(r2) / R;
375
+ const k = A0 + (1 - A0) * rho * rho; // f(rho)=rho*k : magnified centre, crushed rim
376
+ // lens acts on the folded coordinates around the centre
377
+ const ux = cx + fold * (58 * Math.sin((cx / W) * PI * 2.5 + 0.6 + tau * 0.25) - 9 * Math.sin((cx / W) * PI * 6 + tau * 0.4));
378
+ const blend = (1 - Math.pow(rho, 2)) * H.clamp(lens * 1.5);
379
+ u = u * (1 - blend) + (ux + dx) * blend;
380
+ u = ux + (u - ux) * k; v = cy + (v - cy) * k;
381
+ }
382
+ const i = y * GW + x;
383
+ G[i] = Math.sin((PI * (u - cx)) / S) * Math.sin((PI * (v - cy)) / S);
384
+ CU[i] = Math.floor((u - cx) / S); CV[i] = Math.floor((v - cy) / S);
385
+ }
386
+ }
387
+
388
+ // palette: ivory ground, carbon black, one signal red that floods the swelling cells
389
+ const IV = [240, 235, 221], BK = [20, 19, 21], RD = [208, 38, 34];
390
+ // the lens magnification of each cell -> flat, stepped tint (each square painted one colour)
391
+ const k0 = 0.42;
392
+ for (let y = 0; y < H_; y++) {
393
+ for (let x = 0; x < W; x++) {
394
+ const i = y * GW + x;
395
+ const g = G[i];
396
+ const gx = (G[i + 1] - g), gy = (G[i + GW] - g);
397
+ const gm = Math.sqrt(gx * gx + gy * gy) + 1e-5;
398
+ let a = 0.5 + (g / gm) * 0.9;
399
+ a = a < 0 ? 0 : a > 1 ? 1 : a;
400
+ let L = IV;
401
+ const dx = x - cx, dy = y - cy, r2 = dx * dx + dy * dy;
402
+ if (r2 < R2) {
403
+ // cell centre in texture space -> its radius -> quantised tint step
404
+ // use the neighbouring *light* cell for edge pixels so tints never fringe
405
+ let j = i;
406
+ if (g <= 0) { if (G[i + 1] > 0) j = i + 1; else if (G[i + GW] > 0) j = i + GW; else if (x > 0 && G[i - 1] > 0) j = i - 1; else if (y > 0 && G[i - GW] > 0) j = i - GW; }
407
+ const ccu = (CU[j] + 0.5) * S, ccv = (CV[j] + 0.5) * S;
408
+ const cr = Math.hypot(ccu, ccv) / (R * k0 * 1.0);
409
+ const step = Math.max(0, Math.min(1, 1.15 - cr * 0.9));
410
+ const q = cr < redThr ? 1 : 0;
411
+ L = [IV[0] + (RD[0] - IV[0]) * q, IV[1] + (RD[1] - IV[1]) * q, IV[2] + (RD[2] - IV[2]) * q];
412
+ }
413
+ const o = (y * W + x) * 4;
414
+ d[o] = BK[0] + (L[0] - BK[0]) * a;
415
+ d[o + 1] = BK[1] + (L[1] - BK[1]) * a;
416
+ d[o + 2] = BK[2] + (L[2] - BK[2]) * a;
417
+ d[o + 3] = 255;
418
+ }
419
+ }
420
+ ctx.putImageData(img, 0, 0);
421
+
422
+ // --- canvas finish: linen weave + brush unevenness, multiplied lightly
423
+ const tex = ctx.getImageData(0, 0, W, H_), td = tex.data, r = H.rng(5);
424
+ for (let y = 0; y < H_; y++) {
425
+ const wy = Math.sin(y * 1.9) * 0.5 + 0.5;
426
+ for (let x = 0; x < W; x++) {
427
+ const o = (y * W + x) * 4;
428
+ const wx = Math.sin(x * 1.9) * 0.5 + 0.5;
429
+ const weave = ((x >> 1) + (y >> 1)) & 1 ? wx : wy;
430
+ const lum = (td[o] + td[o + 1] + td[o + 2]) / 765;
431
+ const brush = nz.n2(x * 0.004, y * 0.03) * 0.5 + nz.n2(x * 0.05, y * 0.002) * 0.5;
432
+ const k = 1 + (weave - 0.5) * 0.05 * (0.3 + lum) + brush * 0.025 + (r() - 0.5) * 0.03;
433
+ td[o] *= k; td[o + 1] *= k; td[o + 2] *= k;
434
+ }
435
+ }
436
+ ctx.putImageData(tex, 0, 0);
437
+ H.vignette(ctx, W, H_, 0.18);
438
+ },
439
+ });
440
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 69,
3
+ "name": "Op Art Sphere",
4
+ "nameZh": "欧普艺术球体",
5
+ "family": "Generative & Type",
6
+ "familyZh": "生成艺术与字体",
7
+ "description": "Hard-edged black/ivory checkerboard that swells into a lens-like sphere and ripples at the margins — Vasarely and Riley optical vibration, flat acrylic on primed canvas.",
8
+ "motionNotes": "Motion: A flat checkerboard first ripples into Riley folds, then swells into a lens-sphere that pops past its size and settles; red squares flip in cell by cell from the centre, then the field breathes and the folds drift.",
9
+ "explainerNotes": "Explainer: The model is the Vasarely lens: untrained its grid is melted noise, the error travels back as an optical ripple through the checkerboard and each adjustment combs the sphere toward perfect order; scenes change with a Riley fold that crushes the picture into crowded columns, labels are hard-edged black and red slabs, and the numbers are Archivo Black on ivory bands.",
10
+ "code": "code.js",
11
+ "registeredAs": "op-art",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }