@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,627 @@
1
+ {
2
+ // ---------- 1980 XY vector monitor: thin beams on black, additive bloom, vertex dwell dots, stroke font ----------
3
+ const GLYPH = {
4
+ A: [[[0, 6], [0, 2], [2, 0], [4, 2], [4, 6]], [[0, 4], [4, 4]]],
5
+ B: [[[0, 6], [0, 0], [3, 0], [4, 1], [4, 2], [3, 3], [0, 3]], [[3, 3], [4, 4], [4, 5], [3, 6], [0, 6]]],
6
+ C: [[[4, 0], [0, 0], [0, 6], [4, 6]]],
7
+ D: [[[0, 0], [2, 0], [4, 2], [4, 4], [2, 6], [0, 6], [0, 0]]],
8
+ E: [[[4, 0], [0, 0], [0, 6], [4, 6]], [[0, 3], [3, 3]]],
9
+ F: [[[4, 0], [0, 0], [0, 6]], [[0, 3], [3, 3]]],
10
+ G: [[[4, 1], [4, 0], [0, 0], [0, 6], [4, 6], [4, 3], [2, 3]]],
11
+ H: [[[0, 0], [0, 6]], [[4, 0], [4, 6]], [[0, 3], [4, 3]]],
12
+ I: [[[0, 0], [4, 0]], [[2, 0], [2, 6]], [[0, 6], [4, 6]]],
13
+ J: [[[4, 0], [4, 6], [2, 6], [0, 4]]],
14
+ K: [[[0, 0], [0, 6]], [[4, 0], [0, 3], [4, 6]]],
15
+ L: [[[0, 0], [0, 6], [4, 6]]],
16
+ M: [[[0, 6], [0, 0], [2, 2], [4, 0], [4, 6]]],
17
+ N: [[[0, 6], [0, 0], [4, 6], [4, 0]]],
18
+ O: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]]],
19
+ P: [[[0, 6], [0, 0], [4, 0], [4, 3], [0, 3]]],
20
+ Q: [[[0, 0], [4, 0], [4, 4], [2, 6], [0, 6], [0, 0]], [[2, 4], [4, 6]]],
21
+ R: [[[0, 6], [0, 0], [4, 0], [4, 3], [0, 3], [4, 6]]],
22
+ S: [[[4, 0], [0, 0], [0, 3], [4, 3], [4, 6], [0, 6]]],
23
+ T: [[[0, 0], [4, 0]], [[2, 0], [2, 6]]],
24
+ U: [[[0, 0], [0, 6], [4, 6], [4, 0]]],
25
+ V: [[[0, 0], [2, 6], [4, 0]]],
26
+ W: [[[0, 0], [0, 6], [2, 4], [4, 6], [4, 0]]],
27
+ X: [[[0, 0], [4, 6]], [[4, 0], [0, 6]]],
28
+ Y: [[[0, 0], [2, 2], [4, 0]], [[2, 2], [2, 6]]],
29
+ Z: [[[0, 0], [4, 0], [0, 6], [4, 6]]],
30
+ 0: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]]],
31
+ 1: [[[2, 0], [2, 6]]],
32
+ 2: [[[0, 0], [4, 0], [4, 3], [0, 3], [0, 6], [4, 6]]],
33
+ 3: [[[0, 0], [4, 0], [4, 6], [0, 6]], [[0, 3], [4, 3]]],
34
+ 4: [[[0, 0], [0, 3], [4, 3]], [[4, 0], [4, 6]]],
35
+ 5: [[[4, 0], [0, 0], [0, 3], [4, 3], [4, 6], [0, 6]]],
36
+ 6: [[[0, 0], [0, 6], [4, 6], [4, 3], [0, 3]]],
37
+ 7: [[[0, 0], [4, 0], [4, 6]]],
38
+ 8: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]], [[0, 3], [4, 3]]],
39
+ 9: [[[4, 3], [0, 3], [0, 0], [4, 0], [4, 6]]],
40
+ '-': [[[1, 3], [3, 3]]],
41
+ };
42
+
43
+ // ================= EXPLAINER: "How does AI learn?" as a vector-HUD sequence =================
44
+ const EXG = Object.assign({}, GLYPH, {
45
+ '?': [[[0, 1], [1, 0], [3, 0], [4, 1], [4, 2], [2, 3.5], [2, 4.6]], [[2, 5.7], [2, 6]]],
46
+ '%': [[[0, 6], [4, 0]], [[0, 0], [1.2, 0], [1.2, 1.2], [0, 1.2], [0, 0]], [[2.8, 4.8], [4, 4.8], [4, 6], [2.8, 6], [2.8, 4.8]]],
47
+ ',': [[[2, 5.2], [1.4, 7]]],
48
+ '.': [[[2, 5.7], [2, 6]]],
49
+ ':': [[[2, 1.5], [2, 2]], [[2, 4.5], [2, 5]]],
50
+ '>': [[[0, 0], [4, 3], [0, 6]]],
51
+ '~': [[[0, 3], [4, 3]], [[2.5, 1.4], [4, 3], [2.5, 4.6]]], // used as a right arrow glyph
52
+ });
53
+ // ---------- shared beam renderer (hero + explainer use the same passes) ----------
54
+ // Beam core layer (1.7px + dwell dots), fat halo layer (5px). Wide halos are blurred at half resolution (cheaper,
55
+ // identical look), then the tube glass is laid over: faint P31 face glow, a soft diagonal reflection of the cabinet,
56
+ // rounded-tube corner falloff and the vignette.
57
+ const glassCache = {};
58
+ function glassLayer(W, H_) {
59
+ const key = W + 'x' + H_;
60
+ if (glassCache[key]) return glassCache[key];
61
+ const G = H.layer(W, H_), g = G.getContext('2d');
62
+ g.fillStyle = '#000'; g.fillRect(0, 0, W, H_); // opaque, drawn in 'screen' mode so black = no change
63
+ // faint glow of the phosphor face (the black is lit glass, not a void)
64
+ const rg = g.createRadialGradient(W * 0.5, H_ * 0.52, 0, W * 0.5, H_ * 0.52, W * 0.62);
65
+ rg.addColorStop(0, 'rgba(20,40,30,0.55)'); rg.addColorStop(0.6, 'rgba(12,26,20,0.3)'); rg.addColorStop(1, 'rgba(0,0,0,0)');
66
+ g.fillStyle = rg; g.fillRect(0, 0, W, H_);
67
+ // tube glare up-left (as the hero had) + a soft diagonal reflection band of the cabinet
68
+ const gl = g.createRadialGradient(W * 0.3, H_ * 0.2, 0, W * 0.3, H_ * 0.2, W * 0.5); gl.addColorStop(0, 'rgba(120,160,140,0.05)'); gl.addColorStop(1, 'rgba(0,0,0,0)');
69
+ g.fillStyle = gl; g.fillRect(0, 0, W, H_);
70
+ g.save(); g.translate(W * 0.62, H_ * 0.3); g.rotate(-0.55);
71
+ const band = g.createLinearGradient(0, -90, 0, 90);
72
+ band.addColorStop(0, 'rgba(150,190,170,0)'); band.addColorStop(0.45, 'rgba(150,190,170,0.04)'); band.addColorStop(0.55, 'rgba(150,190,170,0.05)'); band.addColorStop(1, 'rgba(150,190,170,0)');
73
+ g.fillStyle = band; g.fillRect(-W, -90, W * 2, 180); g.restore();
74
+ // fine phosphor-screen grain (static, very faint)
75
+ const gr = H.rng(1981), id = g.getImageData(0, 0, W, H_), d = id.data;
76
+ for (let i = 0; i < d.length; i += 4) { const v = gr() * 3.5; d[i] += v * 0.6; d[i + 1] += v; d[i + 2] += v * 0.8; }
77
+ g.putImageData(id, 0, 0);
78
+ // rounded tube corners: the curved faceplate falls off into the bezel
79
+ const M = H.layer(W, H_), m = M.getContext('2d');
80
+ m.fillStyle = '#000'; m.fillRect(0, 0, W, H_);
81
+ m.globalCompositeOperation = 'destination-out'; m.filter = 'blur(18px)';
82
+ m.beginPath(); m.roundRect(10, 8, W - 20, H_ - 16, 90); m.fill();
83
+ m.globalCompositeOperation = 'source-over'; m.filter = 'none';
84
+ H.vignette(m, W, H_, 0.6); // baked: same vignette the frame used to draw live
85
+ const out = { glass: G, bezel: M };
86
+ return (glassCache[key] = out);
87
+ }
88
+ let halfA = null, halfB = null, halfT = null;
89
+ function renderBeams(ctx, W, H_, strokes, r, flick, jit = 0.2) {
90
+ const gls = glassLayer(W, H_);
91
+ ctx.drawImage(gls.glass, 0, 0); // opaque lit-glass base (black + faint face glow, glare, grain)
92
+ const beam = H.layer(W, H_), b = beam.getContext('2d');
93
+ b.lineCap = 'round'; b.lineJoin = 'round'; b.globalCompositeOperation = 'lighter';
94
+ const fat = H.layer(W, H_), fb = fat.getContext('2d');
95
+ fb.lineCap = 'round'; fb.lineJoin = 'round'; fb.globalCompositeOperation = 'lighter';
96
+ strokes.forEach((s) => {
97
+ // long strokes are swept faster -> dimmer; per-stroke beam jitter
98
+ let L = 0; for (let i = 1; i < s.pts.length; i++) L += Math.hypot(s.pts[i][0] - s.pts[i - 1][0], s.pts[i][1] - s.pts[i - 1][1]);
99
+ const inten = s.a * flick * (1 - jit + jit * r()) * H.clamp(1.25 - L / 2600, 0.55, 1);
100
+ const [cr, cg, cb] = s.col;
101
+ b.strokeStyle = `rgba(${cr},${cg},${cb},${inten})`; b.lineWidth = 1.7;
102
+ b.beginPath(); s.pts.forEach(([x, y], i) => (i ? b.lineTo(x + (r() - 0.5) * 0.4, y + (r() - 0.5) * 0.4) : b.moveTo(x, y))); b.stroke();
103
+ // dwell dots at vertices (beam pauses = brighter)
104
+ if (!s.ghost) {
105
+ b.fillStyle = `rgba(${Math.min(255, cr + 60)},${Math.min(255, cg + 40)},${Math.min(255, cb + 60)},${inten * 0.9})`;
106
+ s.pts.forEach(([x, y]) => { b.beginPath(); b.arc(x, y, 1.6, 0, 7); b.fill(); });
107
+ }
108
+ if (s.ghost) return; // decaying phosphor is too dim to feed the halo
109
+ fb.strokeStyle = `rgba(${cr},${cg},${cb},${s.a * 0.55})`; fb.lineWidth = 5; fb.beginPath(); s.pts.forEach(([x, y], i) => (i ? fb.lineTo(x, y) : fb.moveTo(x, y))); fb.stroke();
110
+ });
111
+ // low-res copies for the wide halos: downsample first, then blur the small canvas (much cheaper than blurring
112
+ // at full size; a 22px/14px halo has no detail to lose)
113
+ const hw = W >> 2, hh = H_ >> 2;
114
+ if (!halfA || halfA.width !== hw) { halfA = H.layer(hw, hh); halfB = H.layer(hw, hh); halfT = H.layer(hw, hh); }
115
+ const ha = halfA.getContext('2d'), hb = halfB.getContext('2d'), ht = halfT.getContext('2d');
116
+ ht.clearRect(0, 0, hw, hh); ht.drawImage(fat, 0, 0, hw, hh);
117
+ ha.clearRect(0, 0, hw, hh); ha.filter = 'blur(5.5px)'; ha.drawImage(halfT, 0, 0); ha.filter = 'none';
118
+ ht.clearRect(0, 0, hw, hh); ht.globalCompositeOperation = 'lighter'; ht.drawImage(beam, 0, 0, hw, hh); ht.drawImage(beam, 0, 0, hw, hh); ht.globalCompositeOperation = 'source-over';
119
+ hb.clearRect(0, 0, hw, hh); hb.filter = 'blur(3.5px)'; hb.drawImage(halfT, 0, 0); hb.filter = 'none';
120
+ ctx.save(); ctx.globalCompositeOperation = 'lighter';
121
+ ctx.globalAlpha = 0.75; ctx.drawImage(halfA, 0, 0, W, H_);
122
+ ctx.filter = 'blur(8px)'; ctx.globalAlpha = 0.6; ctx.drawImage(fat, 0, 0);
123
+ ctx.filter = 'none'; ctx.globalAlpha = 0.9; ctx.drawImage(halfB, 0, 0, W, H_);
124
+ ctx.filter = 'blur(4px)'; ctx.globalAlpha = 1; ctx.drawImage(beam, 0, 0);
125
+ ctx.filter = 'blur(1.5px)'; ctx.drawImage(beam, 0, 0);
126
+ ctx.filter = 'none'; ctx.globalAlpha = 0.9; ctx.drawImage(beam, 0, 0);
127
+ // white-hot centre on top of the core lines
128
+ ctx.globalAlpha = 0.35; ctx.filter = 'brightness(1.6)'; ctx.drawImage(beam, 0, 0);
129
+ ctx.restore();
130
+ ctx.drawImage(gls.bezel, 0, 0);
131
+ }
132
+ const exBeams = (ctx, W, H_, strokes, r, flick) => renderBeams(ctx, W, H_, strokes, r, flick, 0.2);
133
+
134
+ function vecExplainer(ctx, W, H_, t) {
135
+ const r = H.rng(77 + Math.floor(t * 30));
136
+ const GREEN = [110, 255, 120], RED = [255, 70, 50], WHITE = [210, 255, 220];
137
+ const flick = 0.93 + 0.07 * r();
138
+ const cx = W / 2, cyc = H_ / 2;
139
+ const strokes = [];
140
+ let REV = 1, ZOOM = 1, GHOST = false;
141
+ const cut = (pts, f) => {
142
+ if (f >= 1) return pts; if (f <= 0) return [];
143
+ let L = 0; for (let i = 1; i < pts.length; i++) L += Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
144
+ let left = L * f; const out = [pts[0]];
145
+ for (let i = 1; i < pts.length; i++) {
146
+ const d = Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
147
+ if (d >= left) { const k = d ? left / d : 0; out.push([pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k, pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k]); break; }
148
+ out.push(pts[i]); left -= d;
149
+ }
150
+ return out;
151
+ };
152
+ const poly = (pts, col = GREEN, a = 1) => {
153
+ pts = cut(pts, REV);
154
+ if (ZOOM !== 1) pts = pts.map(([x, y]) => [cx + (x - cx) * ZOOM, cyc + (y - cyc) * ZOOM]);
155
+ if (pts.length > 1 && a > 0.01) strokes.push({ pts, col, a, ghost: GHOST });
156
+ };
157
+ // phosphor persistence (P31 decay): moving strokes leave a fading smear of copies over the last ~50 ms
158
+ const LAGS = [[0.048, 0.07], [0.036, 0.12], [0.024, 0.2], [0.012, 0.32]];
159
+ const trail = (fn) => { GHOST = true; LAGS.forEach(([lag, ga]) => fn(t - lag, ga)); GHOST = false; fn(t, 1); };
160
+ const ph = (a, d) => H.ease.inOut(H.clamp((t - a) / d));
161
+ const text = (str, x, y, sz, col, a = 1, track = 1.6) => {
162
+ let px = x; const nC = REV * str.length, rev0 = REV;
163
+ for (let ci = 0; ci < str.length; ci++) {
164
+ const g = EXG[str[ci]]; REV = H.clamp(nC - ci);
165
+ if (g && REV > 0) g.forEach((pl) => poly(pl.map(([gx, gy]) => [px + gx * sz, y + gy * sz]), col, a));
166
+ px += sz * 4 + sz * track;
167
+ }
168
+ REV = rev0; return px - x;
169
+ };
170
+ const tw = (str, sz, track = 1.6) => str.length * (sz * 4 + sz * track) - sz * track;
171
+ const ctext = (str, x, y, sz, col, a, track) => text(str, x - tw(str, sz, track) / 2, y, sz, col, a, track);
172
+ const arrow = (x1, y1, x2, y2, col, a = 1) => {
173
+ const ang = Math.atan2(y2 - y1, x2 - x1), L = Math.hypot(x2 - x1, y2 - y1), f = REV;
174
+ const ex = x1 + (x2 - x1) * f, ey = y1 + (y2 - y1) * f;
175
+ const r0 = REV; REV = 1;
176
+ poly([[x1, y1], [ex, ey]], col, a);
177
+ if (f > 0.05) poly([[ex + Math.cos(ang + 2.6) * 16, ey + Math.sin(ang + 2.6) * 16], [ex, ey], [ex + Math.cos(ang - 2.6) * 16, ey + Math.sin(ang - 2.6) * 16]], col, a);
178
+ REV = r0; return L;
179
+ };
180
+ const box = (x, y, w, h, col, a = 1) => poly([[x, y], [x + w, y], [x + w, y + h], [x, y + h], [x, y]], col, a);
181
+ const brackets = (x, y, w, h, s, col, a = 1) => {
182
+ poly([[x, y + s], [x, y], [x + s, y]], col, a); poly([[x + w - s, y], [x + w, y], [x + w, y + s]], col, a);
183
+ poly([[x, y + h - s], [x, y + h], [x + s, y + h]], col, a); poly([[x + w - s, y + h], [x + w, y + h], [x + w, y + h - s]], col, a);
184
+ };
185
+ const shape = (pts, x, y, s, col, a = 1, close = true) => poly((close ? [...pts, pts[0]] : pts).map(([px, py]) => [x + px * s, y + py * s]), col, a);
186
+ const cat = (x, y, s, col, a = 1) => {
187
+ shape([[-1, -0.1], [-0.85, -1], [-0.35, -0.55], [0.35, -0.55], [0.85, -1], [1, -0.1], [0.8, 0.6], [0, 0.9], [-0.8, 0.6]], x, y, s, col, a);
188
+ shape([[-0.45, -0.1], [-0.25, -0.1]], x, y, s, col, a, false); shape([[0.25, -0.1], [0.45, -0.1]], x, y, s, col, a, false);
189
+ shape([[-0.1, 0.25], [0.1, 0.25], [0, 0.38], [-0.1, 0.25]], x, y, s, col, a, false);
190
+ shape([[-0.3, 0.35], [-1.1, 0.2]], x, y, s, col, a, false); shape([[0.3, 0.35], [1.1, 0.2]], x, y, s, col, a, false);
191
+ };
192
+ const dog = (x, y, s, col, a = 1) => {
193
+ shape([[-0.6, -0.75], [0.6, -0.75], [0.72, 0.3], [0.3, 0.85], [-0.3, 0.85], [-0.72, 0.3]], x, y, s, col, a);
194
+ shape([[-0.6, -0.75], [-1.05, -0.45], [-0.95, 0.35], [-0.66, 0.1]], x, y, s, col, a, false);
195
+ shape([[0.6, -0.75], [1.05, -0.45], [0.95, 0.35], [0.66, 0.1]], x, y, s, col, a, false);
196
+ shape([[-0.3, -0.2], [-0.15, -0.2]], x, y, s, col, a, false); shape([[0.15, -0.2], [0.3, -0.2]], x, y, s, col, a, false);
197
+ shape([[-0.15, 0.3], [0.15, 0.3], [0, 0.45]], x, y, s, col, a);
198
+ };
199
+ const check = (x, y, s, col, a = 1) => poly([[x - s, y], [x - s * 0.3, y + s * 0.7], [x + s, y - s * 0.9]], col, a);
200
+ const cross = (x, y, s, col, a = 1) => { poly([[x - s, y - s], [x + s, y + s]], col, a); poly([[x + s, y - s], [x - s, y + s]], col, a); };
201
+ // wireframe icosahedron "model core" in 2D (weak perspective), optional vertex wobble = weights moving
202
+ const phi = (1 + Math.sqrt(5)) / 2;
203
+ const icoV = [[-1, phi, 0], [1, phi, 0], [-1, -phi, 0], [1, -phi, 0], [0, -1, phi], [0, 1, phi], [0, -1, -phi], [0, 1, -phi], [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1], [-phi, 0, 1]];
204
+ const icoE = []; for (let i = 0; i < 12; i++) for (let j = i + 1; j < 12; j++) { const d = Math.hypot(icoV[i][0] - icoV[j][0], icoV[i][1] - icoV[j][1], icoV[i][2] - icoV[j][2]); if (Math.abs(d - 2) < 0.01) icoE.push([i, j]); }
205
+ const core = (x, y, s, col, a = 1, wob = 0, rev = 1, tt = t) => {
206
+ const ca = tt * 0.6, cb = tt * 0.37;
207
+ const P = icoV.map((v, k) => {
208
+ let [vx, vy, vz] = v.map((q) => q + (wob ? Math.sin(tt * 9 + k * 2.1) * wob : 0));
209
+ let y1 = vy * Math.cos(cb) - vz * Math.sin(cb), z1 = vy * Math.sin(cb) + vz * Math.cos(cb);
210
+ let x2 = vx * Math.cos(ca) - z1 * Math.sin(ca), z2 = vx * Math.sin(ca) + z1 * Math.cos(ca);
211
+ const pz = 1 / (1 + z2 * 0.08);
212
+ return [x + x2 * s * pz, y + y1 * s * pz];
213
+ });
214
+ const r0 = REV, k = rev * icoE.length;
215
+ icoE.forEach(([i, j], e) => { REV = H.clamp(k - e); if (REV > 0) poly([P[i], P[j]], col, a); });
216
+ REV = r0;
217
+ };
218
+ // the hero's mountain range (same seed and silhouette) standing on the y=520 ground line
219
+ const MTN = []; { const rs = H.rng(42); for (let i = 0; i <= 26; i++) { const x = -200 + i * ((W + 400) / 26); const h = (i % 2 ? 10 + rs() * 30 : 40 + rs() * 70); MTN.push([x, 520 - h * (i > 9 && i < 15 ? 0.45 : 1)]); } }
220
+ const fmt = (n) => String(Math.floor(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
221
+ const streaks = (k, a) => { const rs = H.rng(5); for (let i = 0; i < 46; i++) { const ang = rs() * 6.283, d0 = 40 + rs() * 300; const d1 = d0 + 60 + k * 900; poly([[cx + Math.cos(ang) * d0 * (1 + k * 2), cyc + Math.sin(ang) * d0 * (1 + k * 2)], [cx + Math.cos(ang) * d1, cyc + Math.sin(ang) * d1]], GREEN, a); } };
222
+
223
+ // ---------- persistent HUD: the question, once it has shrunk into the corner ----------
224
+ const hdrK = ph(1.7, 0.6);
225
+ {
226
+ const sz = H.lerp(10, 4, hdrK), x = H.lerp(cx - tw('HOW DOES AI LEARN?', 10) / 2, 40, hdrK), y = H.lerp(290, 34, hdrK);
227
+ REV = H.clamp((t - 0.15) / 0.9);
228
+ text('HOW DOES AI LEARN?', x, y, sz, hdrK > 0.5 ? RED : GREEN, 1);
229
+ REV = 1;
230
+ if (hdrK >= 1) poly([[40, 70], [40 + tw('HOW DOES AI LEARN?', 4), 70]], RED, 0.6);
231
+ }
232
+
233
+ // ---------- BEAT 1 -> 2: the model core appears, then the DATA stream ----------
234
+ if (t < 6.4) {
235
+ ZOOM = t > 5.8 ? 1 + Math.pow((t - 5.8) / 0.6, 2) * 9 : 1; // warp INTO the core
236
+ const coreX = cx + 180, coreY = 360;
237
+ REV = ph(0.9, 0.5); poly([[0, 520], [W, 520]], GREEN, 0.5);
238
+ poly(MTN, GREEN, 0.2); REV = 1;
239
+ trail((tt, ga) => core(coreX, coreY, 70, WHITE, ga, 0, ph(1.95, 1.0), tt));
240
+ if (t > 2.4) { REV = ph(2.4, 0.4); poly([[coreX, coreY + 130], [coreX, coreY + 168]], GREEN, 0.8); REV = ph(2.6, 0.4); ctext('MODEL', coreX, coreY + 178, 6, GREEN, 1); REV = 1; }
241
+ // the stream arrow
242
+ if (t > 2.6) { REV = ph(2.6, 0.5); arrow(90, 520, coreX - 120, 470, GREEN, 0.7); REV = ph(2.9, 0.4); text('TRAINING DATA', 100, 540, 5, GREEN, 0.95); REV = 1; }
243
+ // labelled examples fly along the arrow into the core
244
+ for (let i = 0; i < 9; i++) {
245
+ const t0 = 2.6 + i * 0.34;
246
+ if (t < t0 || t > t0 + 1.5) continue;
247
+ const isCat = (i * 7) % 3 !== 0;
248
+ trail((tt, ga) => {
249
+ const k = (tt - t0) / 1.5; if (k < 0 || k > 1) return;
250
+ const x = H.lerp(-80, coreX - 40, k), y = H.lerp(400, coreY, Math.pow(k, 2)) - Math.sin(k * Math.PI) * 30;
251
+ const s = 46 * (1 - Math.pow(k, 3) * 0.85);
252
+ box(x - s * 1.25, y - s * 1.25, s * 2.5, s * 2.5, GREEN, 0.75 * ga);
253
+ (isCat ? cat : dog)(x, y - s * 0.12, s * 0.8, GREEN, 0.95 * ga);
254
+ if (s > 20 && ga === 1) text(isCat ? 'CAT' : 'DOG', x - tw('CAT', s / 9) / 2, y + s * 1.4, s / 9, isCat ? GREEN : WHITE, 1);
255
+ });
256
+ }
257
+ // counter readout
258
+ if (t > 2.8) {
259
+ const n = 1e6 * Math.pow(H.clamp((t - 2.9) / 2.6), 2.2);
260
+ REV = ph(2.8, 0.3); box(40, 90, 560, 130, RED, 0.7); text('EXAMPLES SEEN', 60, 104, 4.2, RED, 0.95); REV = 1;
261
+ text(fmt(n), 60, 140, 11, GREEN, 1, 1.3);
262
+ }
263
+ if (t > 5.8) streaks((t - 5.8) / 0.6, 0.6);
264
+ ZOOM = 1;
265
+ }
266
+
267
+ // ---------- BEAT 3: GUESS -> CHECK -> ADJUST loop ----------
268
+ if (t >= 6.4 && t < 10.9) {
269
+ ZOOM = t < 6.75 ? Math.pow((t - 6.4) / 0.35, 0.6) : t > 10.5 ? 1 - Math.pow((t - 10.5) / 0.4, 2) : 1;
270
+ if (t < 6.8) streaks(1 - (t - 6.4) / 0.4, 0.4);
271
+ const picX = 260, picY = 380, coreX = 840, coreY = 360;
272
+ // loop indicator across the top: GUESS > CHECK > ADJUST, with return arrow
273
+ const phase = t < 7.5 ? 0 : t < 8.4 ? 1 : t < 9.4 ? 2 : 0;
274
+ const items = ['GUESS', 'CHECK', 'ADJUST'], ix = [400, 640, 890];
275
+ items.forEach((s, i) => {
276
+ const on = i === phase;
277
+ ctext(s, ix[i], 104, 5, on ? WHITE : GREEN, on ? 1 : 0.5);
278
+ if (on) brackets(ix[i] - tw(s, 5) / 2 - 16, 90, tw(s, 5) + 32, 58, 10, WHITE, 1);
279
+ if (i < 2) { REV = 1; arrow(ix[i] + tw(s, 5) / 2 + 12, 120, ix[i + 1] - tw(items[i + 1], 5) / 2 - 12, 120, GREEN, 0.5); }
280
+ });
281
+ poly([[ix[2], 152], [ix[2], 172], [ix[0], 172]], GREEN, 0.45); arrow(ix[0], 172, ix[0], 154, GREEN, 0.45);
282
+ // dim horizon + mountain range behind the loop (broken where the NEW PICTURE caption sits)
283
+ poly([[0, 520], [picX - 140, 520]], GREEN, 0.2); poly([[picX + 140, 520], [W, 520]], GREEN, 0.2);
284
+ poly(MTN, GREEN, 0.14);
285
+ // the new picture (a cat) in a targeting frame
286
+ box(picX - 110, picY - 110, 220, 220, GREEN, 0.8);
287
+ cat(picX, picY, 70, GREEN, 1);
288
+ REV = 1; text('NEW PICTURE', picX - tw('NEW PICTURE', 4.2) / 2, picY + 126, 4.2, GREEN, 0.9);
289
+ // model core with dials
290
+ trail((tt, ga) => core(coreX, coreY, 85, WHITE, ga, tt > 8.5 && tt < 9.4 ? 0.22 * (1 - (tt - 8.5) / 0.9) : 0, 1, tt));
291
+ // GUESS beam from picture into model and out to readout
292
+ const g1 = ph(6.8, 0.45);
293
+ REV = g1; arrow(picX + 125, picY, coreX - 120, coreY, GREEN, 0.9); REV = 1;
294
+ const g2 = t > 9.4 ? ph(9.45, 0.4) : 0;
295
+ if (g2 > 0 && g2 < 1) { const px = H.lerp(picX + 125, coreX - 120, g2), py = H.lerp(picY, coreY, g2); cross(px, py, 6, WHITE, 1); }
296
+ const rx = coreX + 175, ry = coreY - 40;
297
+ if (t > 7.15 && t < 9.4) {
298
+ REV = H.clamp((t - 7.15) / 0.3); text('DOG?', rx, ry, 7, t > 7.5 ? RED : GREEN, 1); REV = 1;
299
+ if (t > 7.5) { // CHECK: wrong!
300
+ const bl = Math.floor((t - 7.5) * 8) % 2 === 0 || t > 8.0;
301
+ if (bl) cross(rx + 90, ry + 22, 46, RED, 1);
302
+ if (bl) brackets(picX - 126, picY - 126, 252, 252, 26, RED, 1);
303
+ REV = ph(7.6, 0.4); text('IT IS A CAT', picX - tw('IT IS A CAT', 5) / 2, picY - 160, 5, RED, 1); REV = 1;
304
+ }
305
+ }
306
+ // ADJUST: error pulse travels back, dials turn, connections re-shape
307
+ if (t > 8.3) {
308
+ const ek = H.clamp((t - 8.35) / 0.55);
309
+ const path = [[rx + 40, ry + 60], [rx + 40, coreY + 150], [coreX, coreY + 150]];
310
+ REV = ek; poly(path, RED, 0.9); REV = 1;
311
+ if (ek > 0 && ek < 1) { const p = cut(path, ek); const [ex, ey] = p[p.length - 1]; box(ex - 8, ey - 8, 16, 16, RED, 1); }
312
+ if (t > 8.4 && t < 9.4) { REV = ph(8.4, 0.3); text('ERROR', rx + 54, coreY + 124, 5, RED, 1); REV = 1; }
313
+ }
314
+ // weight dials under the core (knobs)
315
+ ['W1', 'W2', 'W3'].forEach((s, i) => {
316
+ const dx = coreX - 150 + i * 150, dy = 600, R = 38;
317
+ const circ = []; for (let a = 0; a <= 6.3; a += 0.3) circ.push([dx + Math.cos(a) * R, dy + Math.sin(a) * R]);
318
+ poly(circ, GREEN, 0.7);
319
+ const a0 = [-2.2, -0.6, -1.4][i], a1 = [-0.9, -1.9, 0.2][i];
320
+ trail((tt, ga) => {
321
+ const ang = H.lerp(a0, a1, H.ease.inOut(H.clamp((tt - 8.6 - i * 0.12) / 0.6)));
322
+ poly([[dx, dy], [dx + Math.cos(ang) * (R - 6), dy + Math.sin(ang) * (R - 6)]], t > 8.6 && t < 9.5 ? WHITE : GREEN, ga);
323
+ });
324
+ ctext(s, dx, dy + R + 10, 4, GREEN, 0.9);
325
+ });
326
+ REV = 1; text('WEIGHTS', coreX - 360, 588, 4.2, GREEN, 0.8);
327
+ // second GUESS: correct
328
+ if (t > 9.75) {
329
+ REV = H.clamp((t - 9.75) / 0.3); text('CAT', rx, ry, 7, GREEN, 1); REV = 1;
330
+ if (t > 10.0) { check(rx + 150, ry + 22, 30, GREEN, 1); brackets(picX - 126, picY - 126, 252, 252, 26, GREEN, 1); }
331
+ }
332
+ ZOOM = 1;
333
+ }
334
+
335
+ // ---------- BEAT 4: PROGRESS chart ----------
336
+ if (t >= 10.85) {
337
+ ZOOM = t < 11.2 ? H.ease.out((t - 10.85) / 0.35) : 1;
338
+ const ox = 110, oy = 660, aw = 780, ah = 400;
339
+ REV = ph(10.9, 0.4); poly([[ox, oy - ah], [ox, oy], [ox + aw, oy]], GREEN, 0.8); REV = 1;
340
+ for (let k = 1; k <= 4; k++) poly([[ox - 8, oy - (k / 4) * ah], [ox, oy - (k / 4) * ah]], GREEN, 0.6);
341
+ REV = ph(11.0, 0.4); text('TRAINING ROUNDS', ox + aw - tw('TRAINING ROUNDS', 4.2), oy + 8, 4.2, GREEN, 0.9); REV = 1;
342
+ const N = 24, prog = H.clamp((t - 11.1) / 1.7);
343
+ const acc = (i) => 97 - 36 * Math.exp(-i / 5.5);
344
+ const pts = []; for (let i = 0; i <= N * prog + 1e-6; i++) pts.push([ox + (i / N) * aw, oy - ((acc(i) - 50) / 50) * ah]);
345
+ if (prog * N % 1 > 0.01 && pts.length <= N) { const i = N * prog, i0 = Math.floor(i); pts.push([ox + (i / N) * aw, oy - ((H.lerp(acc(i0), acc(i0 + 1), i - i0) - 50) / 50) * ah]); }
346
+ poly(pts, WHITE, 1);
347
+ pts.forEach(([x, y], k) => { if (k % 4 === 0) box(x - 3, y - 3, 6, 6, GREEN, 0.8); });
348
+ const val = Math.round(61 + 36 * H.ease.out(prog));
349
+ const last = pts[pts.length - 1] || [ox, oy];
350
+ text('ACCURACY', 950, 330, 5, GREEN, 0.95);
351
+ text(val + '%', 950, 376, 15, prog >= 1 ? WHITE : GREEN, 1, 1.3);
352
+ // emphasis: lock-on brackets + ring at the 97% point
353
+ if (prog >= 1) {
354
+ const bl = t > 13.0 || Math.floor((t - 12.8) * 8) % 2 === 0;
355
+ if (bl) brackets(930, 310, 330, 170, 24, RED, 1);
356
+ const ring = []; const rr = 26 + Math.max(0, 0.4 - (t - 12.8)) * 80; for (let a = 0; a <= 6.3; a += 0.25) ring.push([last[0] + Math.cos(a) * rr, last[1] + Math.sin(a) * rr]); poly(ring, RED, 1);
357
+ }
358
+ // ---------- BEAT 5: takeaway ----------
359
+ if (t > 13.1) {
360
+ REV = H.clamp((t - 13.1) / 0.55);
361
+ ctext('MORE EXAMPLES ~ BETTER GUESSES', cx, 132, 7, WHITE, 1, 1.4);
362
+ REV = 1;
363
+ }
364
+ ZOOM = 1;
365
+ }
366
+ exBeams(ctx, W, H_, strokes, r, flick);
367
+ }
368
+
369
+ // ================= SOUND: a 1979-83 vector cabinet. Discrete analog circuits, no music: a low buzzing beam while
370
+ // strokes draw, the two-note heartbeat thump that speeds up with the counter, laser pews, a hyperspace sweep for the
371
+ // warps, a noise-circuit explosion and alarm warble for the miss, and saw-chord bonus tones for the hits.
372
+ // Cabinet fan + mains hum underneath. Times copied from vecExplainer.
373
+ function vecSound(k) {
374
+ const END = 14.5;
375
+ const px = (x) => H.clamp(x / 1280) * 1.4 - 0.7;
376
+ // vector beam drawing: buzzy low saw through a band, pitch drifts from f0 to f1 over the draw
377
+ const beam = (t, dur, f0, f1, p0 = 0, g = 1) => k.tone(t, { f: [[0, f0], [dur, f1]], type: 'sawtooth', dur, gain: 0.05 * g, a: Math.min(0.04, dur * 0.2), hold: dur * 0.7, filter: 'bandpass', f2: 900, q: 1.5, pan: p0, minor: true });
378
+ const saw = (t, f, dur, gain, o) => k.tone(t, Object.assign({ f, type: 'sawtooth', dur, gain, a: 0.003, hold: dur * 0.5, filter: 'lowpass', f2: 2000 }, o));
379
+ // heartbeat thump (alternating low notes)
380
+ const beat = (t, hi, g = 1) => { k.tone(t, { f: [[0, hi ? 70 : 62], [0.09, 40]], type: 'square', dur: 0.12, gain: 0.085 * g, a: 0.002, hold: 0.04, filter: 'lowpass', f2: 300, minor: true }); };
381
+ // laser pew: fast falling square sweep
382
+ const pew = (t, dur, f0, f1, p = 0, g = 1) => k.tone(t, { f: [[0, f0], [dur, f1]], type: 'square', dur, gain: 0.06 * g, a: 0.002, hold: dur * 0.6, filter: 'lowpass', f2: 2200, pan: p });
383
+ // noise-circuit explosion
384
+ const boom = (t, dur, g = 1, p = 0) => { k.noise(t, { dur, color: 'brown', filter: 'lowpass', f: [[0, 1600], [dur, 120]], gain: 0.55 * g, a: 0.002, pan: p }); k.noise(t, { dur: dur * 0.5, filter: 'lowpass', f: 3000, gain: 0.12 * g, rate: 0.3, a: 0.001, pan: p }); };
385
+ // hyperspace: rising (in) or falling (out) noise + saw sweep
386
+ const warp = (t, dur, up, g = 1) => {
387
+ k.noise(t, { dur, color: 'pink', filter: 'bandpass', f: up ? [[0, 300], [dur, 3000]] : [[0, 3000], [dur, 300]], q: 1.4, gain: 0.16 * g, a: up ? dur * 0.85 : 0.01 });
388
+ k.tone(t, { f: up ? [[0, 90], [dur, 700]] : [[0, 700], [dur, 90]], type: 'sawtooth', dur, gain: 0.05 * g, a: up ? dur * 0.8 : 0.01, filter: 'lowpass', f2: 1500 });
389
+ };
390
+
391
+ k.bed('hum', 0, END, { gain: 0.5 });
392
+ k.bed('room', 0, END, { gain: 0.9 }); // cabinet fan
393
+
394
+ // ---- BEAT 1: title draws letter by letter (0.15-1.05), horizon line (0.9-1.4)
395
+ beam(0.15, 0.9, 120, 150, 0);
396
+ for (let i = 0; i < 18; i += 2) k.noise(0.15 + (i / 18) * 0.9, { dur: 0.012, filter: 'bandpass', f: 1300, q: 3, gain: 0.12, a: 0.001, pan: px(440 + i * 22), minor: true });
397
+ k.tone(0.9, { f: [[0, 150], [0.5, 260]], type: 'sawtooth', dur: 0.5, gain: 0.04, a: 0.05, hold: 0.35, filter: 'lowpass', f2: 800, pan: 0 });
398
+ // title shrinks into the corner (1.7-2.3): soft low slide, small tone as it turns red (~2.0)
399
+ k.noise(1.7, { dur: 0.6, color: 'brown', filter: 'lowpass', f: [[0, 600], [0.6, 200]], gain: 0.09, a: 0.25, pan: -0.4 });
400
+ saw(2.0, 'E3', 0.12, 0.035, { pan: -0.6 });
401
+ // ---- BEAT 2: core draws edge by edge (1.95-2.95), stem + MODEL (2.4/2.6), arrow (2.6-3.1), label (2.9)
402
+ beam(1.95, 1.0, 80, 160, px(820), 1.1);
403
+ beam(2.6, 0.5, 140, 200, px(450), 0.7);
404
+ // cards fly into the core: arrive at 2.6 + i*0.34 + 1.5 (only the ones before the warp)
405
+ for (let i = 0; i < 6; i++) { const t = 4.1 + i * 0.34; if (t > 5.75) break; k.tone(t, { f: [[0, 220], [0.08, 520]], type: 'square', dur: 0.1, gain: 0.04, a: 0.002, hold: 0.05, filter: 'lowpass', f2: 1600, pan: px(780), minor: true }); }
406
+ // EXAMPLES SEEN (box 2.8, counter 2.9-5.5, accelerating): the heartbeat speeds up with it
407
+ { let hi = true; for (let t = 2.95; t < 5.45;) { beat(t, hi); hi = !hi; t += H.lerp(0.42, 0.13, H.clamp((t - 2.9) / 2.6)); } }
408
+ saw(5.5, 'A3', 0.1, 0.05, { pan: -0.4 }); saw(5.6, 'E4', 0.3, 0.05, { pan: -0.4 }); // lands on 1,000,000
409
+ // warp into the core (5.8-6.4), out of the streaks into the loop (6.4-6.75)
410
+ warp(5.8, 0.6, true, 1);
411
+ k.play('thud', 6.4, { f: 90, gain: 0.45 });
412
+ warp(6.4, 0.35, false, 0.6);
413
+
414
+ // ---- BEAT 3: guess beam (6.8-7.25), DOG? draws (7.15-7.45)
415
+ pew(6.8, 0.45, 1400, 500, px(600), 0.9);
416
+ beam(7.15, 0.3, 130, 150, px(1100), 0.8);
417
+ // CHECK (7.5): red cross + brackets blink (on at 7.5, 7.75, steady from 8.0) -> explosion + alarm warble
418
+ boom(7.5, 0.7, 1.5, px(1080)); k.play('thud', 7.5, { f: 80, gain: 0.5, pan: px(1080) });
419
+ [7.5, 7.75, 8.0].forEach((t) => { saw(t, 'C4', 0.06, 0.06, { pan: px(260) }); saw(t + 0.06, 'G3', 0.06, 0.06, { pan: px(260) }); });
420
+ beam(7.6, 0.4, 120, 110, px(260), 0.7); // IT IS A CAT
421
+ // ADJUST (8.4): error pulse runs back (8.35-8.9), core wobbles 8.5-9.4, dials turn (8.6, 8.72, 8.84 for 0.6)
422
+ k.tone(8.35, { f: [[0, 600], [0.55, 200]], type: 'square', dur: 0.56, gain: 0.04, a: 0.01, hold: 0.4, filter: 'lowpass', f2: 1500, pan: px(1000) });
423
+ k.withMinor(() => {
424
+ for (let i = 0; i < 3; i++) k.tone(8.6 + i * 0.12, { f: [[0, 160 + i * 30], [0.6, 230 + i * 30]], type: 'sawtooth', dur: 0.6, gain: 0.03, a: 0.08, hold: 0.3, filter: 'lowpass', f2: 700, pan: px(690 + i * 150) });
425
+ for (let t = 8.5; t < 9.35; t += 1 / 9) k.tone(t, { f: 95 + 30 * Math.sin(t * 9), type: 'sawtooth', dur: 0.1, gain: 0.04 * (1 - (t - 8.5) / 0.9), filter: 'lowpass', f2: 500, pan: px(840) });
426
+ });
427
+ // GUESS again: a shot travels from picture to core (9.45-9.85), CAT draws (9.75), lock-on check (10.0): the correction
428
+ pew(9.45, 0.4, 1200, 400, px(560), 1.0);
429
+ beam(9.75, 0.3, 140, 170, px(1100), 0.8);
430
+ saw(10.0, 'E4', 0.1, 0.06, { pan: px(1100) }); saw(10.1, 'A4', 0.35, 0.06, { pan: px(1100) }); saw(10.1, 'A3', 0.35, 0.05, { pan: px(1100) });
431
+ // zoom out of the loop (10.5-10.9), chart zooms in (10.85-11.2)
432
+ warp(10.5, 0.4, false, 0.7);
433
+
434
+ // ---- BEAT 4: axes draw (10.9-11.3), curve draws 11.1-12.8 with the % climbing (ease-out)
435
+ beam(10.9, 0.4, 110, 130, px(400), 0.8);
436
+ k.tone(11.1, { f: [[0, 120], [0.6, 210], [1.7, 240]], type: 'sawtooth', dur: 1.7, gain: 0.045, a: 0.05, hold: 1.5, filter: 'bandpass', f2: 900, q: 1.5, pan: px(500), minor: true });
437
+ { let last = 61, lastT = -1; for (let f = Math.ceil(11.1 * 60); f < 12.75 * 60; f++) { const t = f / 60, v = Math.round(61 + 36 * H.ease.out(H.clamp((t - 11.1) / 1.7))); if (v > last && t - lastT >= 1 / 12) { k.noise(t, { dur: 0.014, filter: 'bandpass', f: 1600, q: 3, gain: 0.13, a: 0.001, pan: px(1080), minor: true }); last = v; lastT = t; } } }
438
+ // 97% (12.8): lock-on brackets blink, ring collapses on the last point. the biggest hit: deep boom + saw chord
439
+ const P = 12.8;
440
+ k.play('thud', P, { f: 110, gain: 0.8 }); boom(P, 0.5, 0.6, px(1000));
441
+ ['C4', 'E4', 'G4', 'C5'].forEach((n) => saw(P, n, 0.9, 0.04, { hold: 0.35, verb: 0.15, pan: px(1080) })); k.tone(P, { f: 'C2', dur: 0.9, gain: 0.25, a: 0.004, hold: 0.2 });
442
+ k.tone(P, { f: [[0, 900], [0.4, 300]], type: 'square', dur: 0.4, gain: 0.03, a: 0.005, hold: 0.25, filter: 'lowpass', f2: 1600, pan: px(870) }); // ring shrinking
443
+ // ---- BEAT 5: takeaway draws (13.1-13.65) and lands with a resolving two-note
444
+ beam(13.1, 0.55, 140, 170, 0, 0.9);
445
+ saw(13.68, 'G3', 0.12, 0.04); saw(13.8, 'C4', 0.7, 0.04, { verb: 0.12 }); k.tone(13.8, { f: 'C3', type: 'triangle', dur: 0.8, gain: 0.09, a: 0.005, hold: 0.3 });
446
+ }
447
+
448
+ registerStyle({
449
+ id: 'vector-arcade',
450
+ name: 'Vector Arcade 1980',
451
+ category: 'Retro Digital',
452
+ description: 'An XY vector monitor cabinet game: hair-thin phosphor beams on pure black, additive bloom, brighter dwell dots where the beam pauses at vertices, dimmer long strokes, wireframe 3D in perspective and a hand-built stroke font.',
453
+ motion: 'Attract-mode build: the beam traces the horizon and mountains, wireframes draw on edge by edge, the AI core flies in from the distance spinning, the saucer slides in with phosphor trails, then the radar, score and title are stroked in letter by letter while everything flickers with beam load.',
454
+ duration: 5,
455
+ frameT: 4.8,
456
+ readsPixels: false,
457
+ explainerDuration: 14.5,
458
+ explainerNote: 'Told as a vector-cabinet HUD: the question shrinks into a red corner readout, labelled wireframe cards stream into a spinning icosahedron core, a hyperspace-style zoom carries us into a lock-on loop (GUESS > CHECK > ADJUST with brackets, red error beam, turning weight dials), and the stroked accuracy graph climbs to a flashing 97%.',
459
+ explainer(ctx, W, H_, t) { vecExplainer(ctx, W, H_, t); },
460
+ explainerSound(k) { vecSound(k); },
461
+ draw(ctx, W, H_, t) {
462
+ ctx.fillStyle = '#000'; ctx.fillRect(0, 0, W, H_);
463
+ const r = H.rng(1980 + Math.floor(t * 30)); // per-frame beam jitter/flicker
464
+ const rs = H.rng(42); // static scene randomness
465
+ const GREEN = [110, 255, 120], RED = [255, 70, 50], WHITE = [210, 255, 220];
466
+ const globalFlick = 0.93 + 0.07 * r();
467
+
468
+ // collect beam strokes first, render them in bloom passes after
469
+ const strokes = []; // {pts:[[x,y]...], col, a}
470
+ let REV = 1; // fraction of each path the beam has traced so far
471
+ const cut = (pts, f) => {
472
+ if (f >= 1) return pts; if (f <= 0) return [];
473
+ let L = 0; for (let i = 1; i < pts.length; i++) L += Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
474
+ let left = L * f; const out = [pts[0]];
475
+ for (let i = 1; i < pts.length; i++) {
476
+ const d = Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
477
+ if (d >= left) { const k = d ? left / d : 0; out.push([pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k, pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k]); break; }
478
+ out.push(pts[i]); left -= d;
479
+ }
480
+ return out;
481
+ };
482
+ let GHOST = false;
483
+ const poly = (pts, col = GREEN, a = 1) => { pts = cut(pts, REV); if (pts.length > 1) strokes.push({ pts, col, a, ghost: GHOST }); };
484
+ const ph = (a, d) => H.ease.inOut(H.clamp((t - a) / d));
485
+
486
+ // ---------- 3D camera ----------
487
+ const camH = 1.5, f = 760, hz = 420, cx = W / 2;
488
+ const yaw = Math.sin(t * 0.25) * 0.08;
489
+ const proj = ([x, y, z]) => {
490
+ const xr = x * Math.cos(yaw) - z * Math.sin(yaw), zr = x * Math.sin(yaw) + z * Math.cos(yaw);
491
+ if (zr < 0.5) return null;
492
+ return [cx + (f * xr) / zr, hz - (f * (y - camH)) / zr, zr];
493
+ };
494
+ const edges3 = (verts, edges, col, a = 1) => {
495
+ const P = verts.map(proj);
496
+ const rev0 = REV, k = REV * edges.length;
497
+ edges.forEach(([i, j], e) => { REV = H.clamp(k - e); if (P[i] && P[j] && REV > 0) poly([[P[i][0], P[i][1]], [P[j][0], P[j][1]]], col, a * H.clamp(2.2 - P[i][2] / 30, 0.35, 1)); });
498
+ REV = rev0;
499
+ };
500
+ const rotY = (v, a) => [v[0] * Math.cos(a) - v[2] * Math.sin(a), v[1], v[0] * Math.sin(a) + v[2] * Math.cos(a)];
501
+ const rotX = (v, a) => [v[0], v[1] * Math.cos(a) - v[2] * Math.sin(a), v[1] * Math.sin(a) + v[2] * Math.cos(a)];
502
+ const tr = (v, p) => [v[0] + p[0], v[1] + p[1], v[2] + p[2]];
503
+
504
+ // ---------- horizon + mountain range + moon + volcano ----------
505
+ REV = ph(0.1, 0.35); poly([[0, hz], [W, hz]], GREEN, 0.75);
506
+ REV = ph(0.35, 0.6);
507
+ const mx = (x) => (((x - t * 18 * 0 - yaw * 900) % (W + 400)) + W + 400) % (W + 400) - 200;
508
+ const mtn = [];
509
+ for (let i = 0; i <= 26; i++) { const x = -200 + i * ((W + 400) / 26); const h = (i % 2 ? 10 + rs() * 30 : 40 + rs() * 70); mtn.push([x - yaw * 900, hz - h * (i > 9 && i < 15 ? 0.45 : 1)]); }
510
+ poly(mtn, GREEN, 0.7);
511
+ REV = ph(0.6, 0.3);
512
+ // volcano (left) with erupting debris arcs
513
+ const vx = 1170 - yaw * 900;
514
+ poly([[vx - 90, hz], [vx - 22, hz - 92], [vx + 22, hz - 92], [vx + 90, hz]], GREEN, 0.75);
515
+ for (let k = 0; k < (t > 0.9 ? 6 : 0); k++) {
516
+ const ph = ((t * 0.7 + k / 6) % 1), dir = (k % 2 ? 1 : -1) * (0.3 + rs() * 0.7);
517
+ const px = vx + dir * ph * 90, py = hz - 92 - (ph * 160 - ph * ph * 190);
518
+ poly([[px, py], [px + dir * 6, py - 4]], GREEN, 1 - ph * 0.6);
519
+ }
520
+ // crescent moon (right)
521
+ const moon = [];
522
+ for (let a = -1.9; a <= 1.9; a += 0.12) moon.push([1040 + Math.cos(a) * 34 - yaw * 900, 235 + Math.sin(a) * 34]);
523
+ for (let a = 1.6; a >= -1.6; a -= 0.12) moon.push([1054 + Math.cos(a) * 26 - yaw * 900, 235 + Math.sin(a) * 30]);
524
+ REV = ph(0.7, 0.4); poly(moon, GREEN, 0.8);
525
+
526
+ // ---------- ground objects: pyramids + blocks in perspective ----------
527
+ const pyramid = (p, s, h) => edges3(
528
+ [[-s, 0, -s], [s, 0, -s], [s, 0, s], [-s, 0, s], [0, h, 0]].map((v) => tr(v, p)),
529
+ [[0, 1], [1, 2], [2, 3], [3, 0], [0, 4], [1, 4], [2, 4], [3, 4]], GREEN);
530
+ const block = (p, sx, sy, sz) => edges3(
531
+ [[-sx, 0, -sz], [sx, 0, -sz], [sx, 0, sz], [-sx, 0, sz], [-sx, sy, -sz], [sx, sy, -sz], [sx, sy, sz], [-sx, sy, sz]].map((v) => tr(v, p)),
532
+ [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]], GREEN);
533
+ let oi = 0; const nx = () => { REV = H.clamp((t - 0.8 - (oi++) * 0.1) / 0.3); };
534
+ nx(); pyramid([-12, 0, 27], 2, 3.4); nx(); block([11.5, 0, 19], 1.6, 2.2, 1.6); nx(); pyramid([9.5, 0, 30], 2.4, 4); nx(); block([-17, 0, 15], 1.2, 1.2, 1.2);
535
+ nx(); pyramid([-30, 0, 44], 3, 5); nx(); block([-6, 0, 36], 2.2, 1.4, 1.4); nx(); pyramid([24, 0, 58], 3, 5);
536
+
537
+ // ---------- AI CORE: rotating icosahedron + counter-rotating inner cube (focal point) ----------
538
+ const phi = (1 + Math.sqrt(5)) / 2;
539
+ const icoV = [[-1, phi, 0], [1, phi, 0], [-1, -phi, 0], [1, -phi, 0], [0, -1, phi], [0, 1, phi], [0, -1, -phi], [0, 1, -phi], [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1], [-phi, 0, 1]];
540
+ const icoE = [];
541
+ for (let i = 0; i < 12; i++) for (let j = i + 1; j < 12; j++) { const d = Math.hypot(icoV[i][0] - icoV[j][0], icoV[i][1] - icoV[j][1], icoV[i][2] - icoV[j][2]); if (Math.abs(d - 2) < 0.01) icoE.push([i, j]); }
542
+ const corePAt = (tt) => [0.4, 3.3 + Math.sin(tt * 1.3) * 0.2, 15 + 50 * (1 - H.ease.out(H.clamp((tt - 1.2) / 1.3)))];
543
+ const coreP = corePAt(t);
544
+ REV = H.clamp((t - 1.2) / 0.6);
545
+ const ca = t * 0.6, cb = t * 0.37;
546
+ const cubeV = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]];
547
+ // phosphor persistence: the spinning core leaves a fading smear of copies over the last ~50 ms
548
+ [[0.048, 0.07], [0.036, 0.12], [0.024, 0.2], [0.012, 0.32], [0, 1]].forEach(([lag, ga]) => {
549
+ const tt = t - lag, pa = tt * 0.6, pb = tt * 0.37, cp = corePAt(tt); GHOST = lag > 0;
550
+ edges3(icoV.map((v) => tr(rotX(rotY(v.map((q) => q * 1.05), pa), pb), cp)), icoE, WHITE, ga);
551
+ edges3(cubeV.map((v) => tr(rotY(rotX(v.map((q) => q * 0.62), -pa * 1.7), -pb * 2), cp)), [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]], GREEN, ga);
552
+ });
553
+ GHOST = false;
554
+ // orbit ring around core
555
+ const ring = []; for (let a = 0; a <= 6.3; a += 0.15) { const v = proj(tr(rotX([Math.cos(a + t) * 2.6, 0, Math.sin(a + t) * 2.6], 0.3), coreP)); if (v) ring.push([v[0], v[1]]); }
556
+ REV = ph(2.1, 0.5); poly(ring, GREEN, 0.55);
557
+ REV = 1;
558
+
559
+ // ---------- enemy saucer with phosphor-decay trail ----------
560
+ const saucerAt = (tt) => [-3 + Math.sin(tt * 0.9) * 1.6 - 16 * (1 - H.ease.out(H.clamp((tt - 1.7) / 1.1))), 1.0, 12 + Math.cos(tt * 0.9) * 2];
561
+ if (t > 1.7) [0.12, 0.06, 0].forEach((lag, li) => {
562
+ const p = saucerAt(t - lag), sa = (t - lag) * 2.5;
563
+ const ring1 = [], ring2 = [], spokes = [];
564
+ const n = 8;
565
+ const v1 = [], v2 = [], v3 = [];
566
+ for (let k = 0; k < n; k++) { const a = sa + (k / n) * Math.PI * 2; v1.push([Math.cos(a) * 1.3, 0, Math.sin(a) * 1.3]); v2.push([Math.cos(a) * 0.55, 0.45, Math.sin(a) * 0.55]); v3.push([Math.cos(a) * 0.6, -0.25, Math.sin(a) * 0.6]); }
567
+ const V = [...v1, ...v2, ...v3].map((v) => tr(v, p));
568
+ const E = [];
569
+ for (let k = 0; k < n; k++) { E.push([k, (k + 1) % n], [n + k, n + (k + 1) % n], [2 * n + k, 2 * n + (k + 1) % n], [k, n + k]); if (k % 2 === 0) E.push([k, 2 * n + k]); }
570
+ edges3(V, E, GREEN, [0.08, 0.16, 0.8][li]);
571
+ });
572
+ // player shell flying toward the core
573
+ const shellK = (t * 0.9) % 1;
574
+ if (t > 2.6) [0.08, 0.04, 0].forEach((lag, li) => { const k = ((t - lag - 2.6) * 0.9) % 1; const p = proj([0.2, 0.6 + k * 3.2, 2 + k * 10]); if (p) poly([[p[0] - 3, p[1]], [p[0] + 3, p[1]], [p[0], p[1] - 5], [p[0] - 3, p[1]]], GREEN, [0.2, 0.4, 1][li]); });
575
+
576
+ // ---------- gunsight reticle ----------
577
+ const ry = 520;
578
+ REV = ph(1.6, 0.35);
579
+ poly([[cx, ry - 120], [cx, ry - 60]], GREEN, 0.9); poly([[cx, ry + 20], [cx, ry + 80]], GREEN, 0.9);
580
+ poly([[cx - 70, ry - 40], [cx - 70, ry - 60], [cx - 20, ry - 60]], GREEN, 0.9); poly([[cx + 70, ry - 40], [cx + 70, ry - 60], [cx + 20, ry - 60]], GREEN, 0.9);
581
+ poly([[cx - 70, ry], [cx - 70, ry + 20], [cx - 20, ry + 20]], GREEN, 0.9); poly([[cx + 70, ry], [cx + 70, ry + 20], [cx + 20, ry + 20]], GREEN, 0.9);
582
+
583
+ // ---------- HUD (red overlay zone, like the cabinet's colour gel) ----------
584
+ const text = (str, x, y, sz, col, a = 1, track = 1.6) => {
585
+ let px = x;
586
+ const nC = REV * str.length, rev0 = REV;
587
+ for (let ci = 0; ci < str.length; ci++) {
588
+ const ch = str[ci]; REV = H.clamp(nC - ci);
589
+ const g = GLYPH[ch];
590
+ if (g) g.forEach((pl) => poly(pl.map(([gx, gy]) => [px + gx * sz, y + gy * sz]), col, a));
591
+ px += sz * 4 + sz * track;
592
+ }
593
+ REV = rev0;
594
+ return px - x;
595
+ };
596
+ const textW = (str, sz, track = 1.6) => str.length * (sz * 4 + sz * track) - sz * track;
597
+ // radar
598
+ const rx0 = cx, ry0 = 92, rr = 62;
599
+ REV = ph(1.9, 0.45);
600
+ const circ = []; for (let a = 0; a <= 6.30; a += 0.2) circ.push([rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]); poly(circ, RED, 0.85);
601
+ for (let k = 0; k < 8; k++) { const a = k * Math.PI / 4; poly([[rx0 + Math.cos(a) * (rr - 8), ry0 + Math.sin(a) * (rr - 8)], [rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]], RED, 0.7); }
602
+ const sw = t * 2.2;
603
+ REV = t > 2.35 ? 1 : 0;
604
+ poly([[rx0, ry0], [rx0 + Math.cos(sw) * rr, ry0 + Math.sin(sw) * rr]], RED, 1);
605
+ if (REV > 0) { GHOST = true; for (let k = 1; k <= 5; k++) { const a = sw - k * 0.07; poly([[rx0, ry0], [rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]], RED, 0.5 * Math.pow(0.6, k)); } GHOST = false; }
606
+ [[0.5, 0.6], [2.4, 0.4], [4.1, 0.75]].forEach(([a, d]) => { const fade = ((sw - a) % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2); const al = H.clamp(1 - fade / 4); if (al > 0.05) { const bx = rx0 + Math.cos(a) * rr * d, by = ry0 + Math.sin(a) * rr * d; poly([[bx - 2, by], [bx + 2, by]], RED, al); } });
607
+ poly([[rx0 - 6, ry0 - 14], [rx0, ry0 - 22], [rx0 + 6, ry0 - 14]], RED, 0.8);
608
+ // score / hi-score
609
+ REV = H.clamp((t - 2.2) / 0.3); text('SCORE', 60, 40, 4, RED, 0.85);
610
+ REV = H.clamp((t - 2.35) / 0.35); text(String(42150 + Math.floor(Math.max(0, t - 2.4) * 50) * 10).padStart(7, '0'), 60, 74, 6, GREEN, 1);
611
+ const hs = 'HI 0099900'; REV = H.clamp((t - 2.3) / 0.35); text(hs, W - 60 - textW(hs, 4), 40, 4, RED, 0.85);
612
+ REV = t > 2.6 ? 1 : 0;
613
+ // lives as tiny tank glyphs
614
+ for (let k = 0; k < 3; k++) { const lx = W - 60 - textW(hs, 4) + k * 34, ly = 82; poly([[lx, ly + 12], [lx + 24, ly + 12], [lx + 20, ly + 6], [lx + 4, ly + 6], [lx, ly + 12]], GREEN, 0.9); poly([[lx + 8, ly + 6], [lx + 10, ly + 1], [lx + 16, ly + 1], [lx + 17, ly + 6]], GREEN, 0.9); poly([[lx + 16, ly + 3], [lx + 26, ly + 2]], GREEN, 0.9); }
615
+ // blinking alert line
616
+ REV = 1;
617
+ if (t > 2.9 && Math.floor(t * 2.5) % 3 !== 1) { const msg = 'AI CORE IN RANGE'; text(msg, cx - textW(msg, 5) / 2, 180, 5, RED, 1); }
618
+ // title, bottom
619
+ const title = 'NEURAL ZONE';
620
+ REV = H.clamp((t - 2.4) / 0.7); text(title, cx - textW(title, 11, 1.4) / 2, 632, 11, GREEN, 1, 1.4);
621
+ REV = 1;
622
+
623
+ // ---------- render: beam strokes with intensity model, additive bloom, tube glass ----------
624
+ renderBeams(ctx, W, H_, strokes, r, globalFlick, 0.22);
625
+ },
626
+ });
627
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 72,
3
+ "name": "Vector Arcade 1980",
4
+ "nameZh": "1980年代矢量街机",
5
+ "family": "Retro Digital",
6
+ "familyZh": "复古数字",
7
+ "description": "An XY vector monitor cabinet game: hair-thin phosphor beams on pure black, additive bloom, brighter dwell dots where the beam pauses at vertices, dimmer long strokes, wireframe 3D in perspective and a hand-built stroke font.",
8
+ "motionNotes": "Motion: Attract-mode build: the beam traces the horizon and mountains, wireframes draw on edge by edge, the AI core flies in from the distance spinning, the saucer slides in with phosphor trails, then the radar, score and title are stroked in letter by letter while everything flickers with beam load.",
9
+ "explainerNotes": "Explainer: Told as a vector-cabinet HUD: the question shrinks into a red corner readout, labelled wireframe cards stream into a spinning icosahedron core, a hyperspace-style zoom carries us into a lock-on loop (GUESS > CHECK > ADJUST with brackets, red error beam, turning weight dials), and the stroked accuracy graph climbs to a flashing 97%.",
10
+ "code": "code.js",
11
+ "registeredAs": "vector-arcade",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }