@prismer/runtime 2.0.8 → 2.2.63

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 (633) hide show
  1. package/CHANGELOG.md +4004 -0
  2. package/README.md +34 -12
  3. package/apc/skills/FIELD-DICTIONARY.md +111 -0
  4. package/apc/skills/bug-reproduce/SKILL.md +150 -0
  5. package/apc/skills/bug-reproduce/skill.json +96 -0
  6. package/apc/skills/code-review/SKILL.md +198 -0
  7. package/apc/skills/code-review/skill.json +124 -0
  8. package/apc/skills/design-review/SKILL.md +122 -0
  9. package/apc/skills/design-review/skill.json +88 -0
  10. package/apc/skills/doc-sync/SKILL.md +168 -0
  11. package/apc/skills/doc-sync/skill.json +81 -0
  12. package/apc/skills/env-doctor/SKILL.md +194 -0
  13. package/apc/skills/env-doctor/skill.json +209 -0
  14. package/apc/skills/git-ops/SKILL.md +189 -0
  15. package/apc/skills/git-ops/skill.json +94 -0
  16. package/apc/skills/impact-trace/SKILL.md +168 -0
  17. package/apc/skills/impact-trace/skill.json +104 -0
  18. package/apc/skills/observability/SKILL.md +195 -0
  19. package/apc/skills/observability/skill.json +116 -0
  20. package/apc/skills/release-db-config-sync/SKILL.md +186 -0
  21. package/apc/skills/release-db-config-sync/skill.json +109 -0
  22. package/apc/skills/release-ota-promote/SKILL.md +195 -0
  23. package/apc/skills/release-ota-promote/skill.json +176 -0
  24. package/apc/skills/release-preflight/SKILL.md +174 -0
  25. package/apc/skills/release-preflight/skill.json +175 -0
  26. package/apc/skills/release-rollback/SKILL.md +214 -0
  27. package/apc/skills/release-rollback/skill.json +230 -0
  28. package/apc/skills/release-tag/SKILL.md +194 -0
  29. package/apc/skills/release-tag/skill.json +94 -0
  30. package/apc/skills/releasing-prod/SKILL.md +49 -0
  31. package/apc/skills/releasing-test/SKILL.md +144 -0
  32. package/apc/skills/sdk-release/SKILL.md +200 -0
  33. package/apc/skills/spec-intake/SKILL.md +169 -0
  34. package/apc/skills/spec-intake/skill.json +93 -0
  35. package/apc/skills/test-result-feedback/SKILL.md +239 -0
  36. package/apc/skills/test-result-feedback/skill.json +193 -0
  37. package/apc/skills/test-runner/SKILL.md +169 -0
  38. package/apc/skills/test-runner/skill.json +103 -0
  39. package/apc/skills/ui-align/SKILL.md +209 -0
  40. package/apc/skills/ui-align/skill.json +114 -0
  41. package/apc/skills/ui-canvas/SKILL.md +148 -0
  42. package/apc/skills/ui-canvas/skill.json +127 -0
  43. package/built-in-skills/agent-coordination/SKILL.md +77 -38
  44. package/built-in-skills/agent-meta/SKILL.md +12 -0
  45. package/built-in-skills/assets/SKILL.md +9 -7
  46. package/built-in-skills/browser-use/SKILL.md +93 -0
  47. package/built-in-skills/canvas-design/SKILL.md +4 -3
  48. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-OFL.txt +93 -0
  49. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
  50. package/built-in-skills/canvas-design/canvas-fonts/LICENSE-SOURCES.md +16 -0
  51. package/built-in-skills/claim-agent-ownership/SKILL.md +10 -5
  52. package/built-in-skills/claude-api/SKILL.md +2 -1
  53. package/built-in-skills/claude-api/python/claude-api/README.md +2 -2
  54. package/built-in-skills/claude-api/python/claude-api/tool-use.md +9 -5
  55. package/built-in-skills/claude-api/python/managed-agents/README.md +11 -1
  56. package/built-in-skills/claude-api/shared/error-codes.md +4 -4
  57. package/built-in-skills/claude-api/shared/managed-agents-client-patterns.md +4 -2
  58. package/built-in-skills/claude-api/shared/managed-agents-core.md +1 -2
  59. package/built-in-skills/claude-api/shared/managed-agents-events.md +41 -22
  60. package/built-in-skills/claude-api/shared/managed-agents-multiagent.md +5 -3
  61. package/built-in-skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +1 -1
  62. package/built-in-skills/claude-api/shared/managed-agents-tools.md +2 -3
  63. package/built-in-skills/claude-api/shared/model-migration.md +3 -3
  64. package/built-in-skills/claude-api/shared/models.md +1 -1
  65. package/built-in-skills/claude-api/typescript/claude-api/batches.md +1 -1
  66. package/built-in-skills/claude-api/typescript/claude-api/files-api.md +3 -1
  67. package/built-in-skills/claude-api/typescript/claude-api/tool-use.md +8 -2
  68. package/built-in-skills/claude-api/typescript/managed-agents/README.md +8 -2
  69. package/built-in-skills/codebase-design/DEEPENING.md +37 -0
  70. package/built-in-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  71. package/built-in-skills/codebase-design/LICENSE +21 -0
  72. package/built-in-skills/codebase-design/SKILL.md +130 -0
  73. package/built-in-skills/codebase-design/references/requesting-code-review/GUIDE.md +291 -0
  74. package/built-in-skills/codebase-design/references/requesting-code-review/LICENSE +21 -0
  75. package/built-in-skills/codebase-design/references/requesting-code-review/LICENSE.superpowers +21 -0
  76. package/built-in-skills/codebase-design/references/requesting-code-review/NOTICE.md +37 -0
  77. package/built-in-skills/codebase-design/references/sdlc-review/GUIDE.md +130 -0
  78. package/built-in-skills/codebase-design/references/sdlc-review/LICENSE +21 -0
  79. package/built-in-skills/codebase-design/references/sdlc-review/NOTICE.md +17 -0
  80. package/built-in-skills/codebase-design/references/simplify-code/GUIDE.md +269 -0
  81. package/built-in-skills/codebase-design/references/simplify-code/LICENSE +21 -0
  82. package/built-in-skills/codebase-design/references/simplify-code/NOTICE.md +37 -0
  83. package/built-in-skills/competitor-intelligence/LICENSE +21 -0
  84. package/built-in-skills/competitor-intelligence/NOTICE.md +6 -0
  85. package/built-in-skills/competitor-intelligence/SKILL.md +125 -0
  86. package/built-in-skills/competitor-intelligence/scripts/watch_state.py +160 -0
  87. package/built-in-skills/conversation-compaction/SKILL.md +117 -0
  88. package/built-in-skills/council-creator/SKILL.md +427 -0
  89. package/built-in-skills/diagnosing-bugs/LICENSE +21 -0
  90. package/built-in-skills/diagnosing-bugs/SKILL.md +148 -0
  91. package/built-in-skills/diagnosing-bugs/references/desktop-dom/GUIDE.md +64 -0
  92. package/built-in-skills/diagnosing-bugs/references/desktop-dom/LICENSE +21 -0
  93. package/built-in-skills/diagnosing-bugs/references/desktop-dom/NOTICE.md +17 -0
  94. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/GUIDE.md +410 -0
  95. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/LICENSE +21 -0
  96. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/LICENSE.superpowers +21 -0
  97. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/NOTICE.md +37 -0
  98. package/built-in-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  99. package/built-in-skills/doc-coauthoring/SKILL.md +7 -0
  100. package/built-in-skills/doc-coauthoring/references/humanizer/GUIDE.md +647 -0
  101. package/built-in-skills/doc-coauthoring/references/humanizer/LICENSE +21 -0
  102. package/built-in-skills/doc-coauthoring/references/humanizer/LICENSE.hermes +21 -0
  103. package/built-in-skills/doc-coauthoring/references/humanizer/PROVENANCE.md +7 -0
  104. package/built-in-skills/document-actions/LICENSE +21 -0
  105. package/built-in-skills/document-actions/NOTICE.md +21 -0
  106. package/built-in-skills/document-actions/SKILL.md +46 -0
  107. package/built-in-skills/domain-modeling/ADR-FORMAT.md +47 -0
  108. package/built-in-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  109. package/built-in-skills/domain-modeling/LICENSE +21 -0
  110. package/built-in-skills/domain-modeling/SKILL.md +76 -0
  111. package/built-in-skills/evidence-citations/LICENSE +21 -0
  112. package/built-in-skills/evidence-citations/NOTICE.md +21 -0
  113. package/built-in-skills/evidence-citations/SKILL.md +46 -0
  114. package/built-in-skills/evidence-citations/references/citation-formats.md +68 -0
  115. package/built-in-skills/evidence-citations/references/grounding-rationale.md +64 -0
  116. package/built-in-skills/evidence-citations/scripts/sources.py +720 -0
  117. package/built-in-skills/frontend-design/SKILL.md +7 -0
  118. package/built-in-skills/frontend-design/references/claude-design/GUIDE.md +650 -0
  119. package/built-in-skills/frontend-design/references/claude-design/LICENSE +21 -0
  120. package/built-in-skills/frontend-design/references/claude-design/LICENSE.hermes +21 -0
  121. package/built-in-skills/frontend-design/references/claude-design/PROVENANCE.md +7 -0
  122. package/built-in-skills/human-approval/SKILL.md +17 -2
  123. package/built-in-skills/image-generate/SKILL.md +107 -301
  124. package/built-in-skills/image-generate/scripts/generate-and-deliver.mjs +310 -0
  125. package/built-in-skills/ingest/SKILL.md +13 -45
  126. package/built-in-skills/internal-comms/SKILL.md +1 -0
  127. package/built-in-skills/liteparse/SKILL.md +130 -110
  128. package/built-in-skills/mcp-builder/SKILL.md +1 -0
  129. package/built-in-skills/mcp-builder/reference/evaluation.md +11 -2
  130. package/built-in-skills/mcp-builder/reference/node_mcp_server.md +34 -5
  131. package/built-in-skills/mcp-builder/reference/python_mcp_server.md +6 -2
  132. package/built-in-skills/mcp-builder/scripts/connections.py +1 -1
  133. package/built-in-skills/mcp-builder/scripts/evaluation.py +52 -34
  134. package/built-in-skills/mcp-builder/scripts/requirements.txt +2 -2
  135. package/built-in-skills/meeting-actions/LICENSE +21 -0
  136. package/built-in-skills/meeting-actions/NOTICE.md +21 -0
  137. package/built-in-skills/meeting-actions/SKILL.md +48 -0
  138. package/built-in-skills/memory/SKILL.md +444 -55
  139. package/built-in-skills/memory/references/llm-wiki/GUIDE.md +468 -0
  140. package/built-in-skills/memory/references/llm-wiki/LICENSE +21 -0
  141. package/built-in-skills/memory/references/llm-wiki/NOTICE.md +4 -0
  142. package/built-in-skills/memory/references/llm-wiki/scripts/lint_wiki.py +84 -0
  143. package/built-in-skills/memory/references/llm-wiki/scripts/requirements.txt +1 -0
  144. package/built-in-skills/memory/references/llm-wiki/templates/SCHEMA.md +19 -0
  145. package/built-in-skills/memory/references/llm-wiki/templates/index.md +9 -0
  146. package/built-in-skills/memory/references/llm-wiki/templates/log.md +4 -0
  147. package/built-in-skills/memory-dream/SKILL.md +332 -0
  148. package/built-in-skills/memory-import/SKILL.md +51 -0
  149. package/built-in-skills/office-artifacts/NOTICE.md +15 -0
  150. package/built-in-skills/office-artifacts/SKILL.md +37 -4
  151. package/built-in-skills/office-artifacts/references/RESOURCE-LIMITS.md +44 -0
  152. package/built-in-skills/office-artifacts/references/document-generation/GUIDE.md +105 -0
  153. package/built-in-skills/office-artifacts/references/docx/GUIDE.md +196 -0
  154. package/built-in-skills/office-artifacts/references/docx/LICENSE +21 -0
  155. package/built-in-skills/office-artifacts/references/docx/NOTICE.md +21 -0
  156. package/built-in-skills/office-artifacts/references/docx/references/revisions-and-comments.md +88 -0
  157. package/built-in-skills/office-artifacts/references/docx/scripts/docx_comments.py +292 -0
  158. package/built-in-skills/office-artifacts/references/docx/scripts/docx_common.py +93 -0
  159. package/built-in-skills/office-artifacts/references/docx/scripts/docx_create.py +177 -0
  160. package/built-in-skills/office-artifacts/references/docx/scripts/docx_edit.py +250 -0
  161. package/built-in-skills/office-artifacts/references/docx/scripts/docx_read.py +169 -0
  162. package/built-in-skills/office-artifacts/references/docx/scripts/docx_revisions.py +149 -0
  163. package/built-in-skills/office-artifacts/references/docx/scripts/docx_template.py +70 -0
  164. package/built-in-skills/office-artifacts/references/docx/scripts/docx_validate.py +155 -0
  165. package/built-in-skills/office-artifacts/references/docx/tests/test_docx_skill.py +525 -0
  166. package/built-in-skills/office-artifacts/references/pdf/GUIDE.md +125 -0
  167. package/built-in-skills/office-artifacts/references/pdf/LICENSE +21 -0
  168. package/built-in-skills/office-artifacts/references/pdf/NOTICE.md +21 -0
  169. package/built-in-skills/office-artifacts/references/pdf/references/forms.md +99 -0
  170. package/built-in-skills/office-artifacts/references/pdf/references/nano-pdf-editing.md +40 -0
  171. package/built-in-skills/office-artifacts/references/pdf/references/ocr-extraction.md +165 -0
  172. package/built-in-skills/office-artifacts/references/pdf/scripts/_raster.py +87 -0
  173. package/built-in-skills/office-artifacts/references/pdf/scripts/extract_marker.py +95 -0
  174. package/built-in-skills/office-artifacts/references/pdf/scripts/extract_pymupdf.py +107 -0
  175. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_create.py +153 -0
  176. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_fill_form.py +97 -0
  177. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_form_layout.py +168 -0
  178. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_make_form.py +145 -0
  179. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_merge.py +50 -0
  180. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_meta.py +123 -0
  181. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_page_image.py +101 -0
  182. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_read.py +153 -0
  183. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_secure.py +71 -0
  184. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_split.py +86 -0
  185. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_stamp.py +145 -0
  186. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_watermark.py +51 -0
  187. package/built-in-skills/office-artifacts/references/pdf/tests/test_pdf_skill.py +414 -0
  188. package/built-in-skills/office-artifacts/references/powerpoint/GUIDE.md +220 -0
  189. package/built-in-skills/office-artifacts/references/powerpoint/LICENSE +21 -0
  190. package/built-in-skills/office-artifacts/references/powerpoint/NOTICE.md +21 -0
  191. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_create.py +214 -0
  192. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_edit.py +442 -0
  193. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_from_template.py +88 -0
  194. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_read.py +131 -0
  195. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_render.py +104 -0
  196. package/built-in-skills/office-artifacts/references/powerpoint/tests/test_powerpoint_skill.py +478 -0
  197. package/built-in-skills/office-artifacts/references/xlsx/GUIDE.md +196 -0
  198. package/built-in-skills/office-artifacts/references/xlsx/LICENSE +21 -0
  199. package/built-in-skills/office-artifacts/references/xlsx/NOTICE.md +21 -0
  200. package/built-in-skills/office-artifacts/references/xlsx/references/restructuring.md +74 -0
  201. package/built-in-skills/office-artifacts/references/xlsx/scripts/csv_to_xlsx.py +108 -0
  202. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_create.py +259 -0
  203. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_edit.py +269 -0
  204. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_read.py +160 -0
  205. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_recalc.py +111 -0
  206. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_restructure.py +418 -0
  207. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_to_csv.py +69 -0
  208. package/built-in-skills/office-artifacts/references/xlsx/tests/test_xlsx_skill.py +542 -0
  209. package/built-in-skills/office-artifacts/tests/test_resource_regressions.py +272 -0
  210. package/built-in-skills/okr/SKILL.md +154 -0
  211. package/built-in-skills/persona/SKILL.md +81 -0
  212. package/built-in-skills/persona-generator/SKILL.md +296 -0
  213. package/built-in-skills/pkf-svg/SKILL.md +258 -0
  214. package/built-in-skills/pkf-svg/references/architecture-diagram/GUIDE.md +148 -0
  215. package/built-in-skills/pkf-svg/references/architecture-diagram/LICENSE +21 -0
  216. package/built-in-skills/pkf-svg/references/architecture-diagram/LICENSE.hermes +21 -0
  217. package/built-in-skills/pkf-svg/references/architecture-diagram/PROVENANCE.md +7 -0
  218. package/built-in-skills/pkf-svg/references/architecture-diagram/templates/template.html +345 -0
  219. package/built-in-skills/pkf-writing/SKILL.md +258 -0
  220. package/built-in-skills/prismer-airtable/LICENSE +21 -0
  221. package/built-in-skills/prismer-airtable/NOTICE.md +11 -0
  222. package/built-in-skills/prismer-airtable/SKILL.md +248 -0
  223. package/built-in-skills/prismer-airtable/scripts/airtable_client.py +76 -0
  224. package/built-in-skills/prismer-apple-notes/LICENSE +21 -0
  225. package/built-in-skills/prismer-apple-notes/NOTICE.md +17 -0
  226. package/built-in-skills/prismer-apple-notes/SKILL.md +118 -0
  227. package/built-in-skills/prismer-apple-notes/scripts/readiness.py +47 -0
  228. package/built-in-skills/prismer-apple-reminders/LICENSE +21 -0
  229. package/built-in-skills/prismer-apple-reminders/NOTICE.md +17 -0
  230. package/built-in-skills/prismer-apple-reminders/SKILL.md +155 -0
  231. package/built-in-skills/prismer-apple-reminders/scripts/readiness.py +47 -0
  232. package/built-in-skills/prismer-ascii-video/LICENSE +21 -0
  233. package/built-in-skills/prismer-ascii-video/LICENSE.hermes +21 -0
  234. package/built-in-skills/prismer-ascii-video/PROVENANCE.md +7 -0
  235. package/built-in-skills/prismer-ascii-video/README.md +290 -0
  236. package/built-in-skills/prismer-ascii-video/SKILL.md +252 -0
  237. package/built-in-skills/prismer-ascii-video/provenance/category-DESCRIPTION.md +3 -0
  238. package/built-in-skills/prismer-ascii-video/references/architecture.md +807 -0
  239. package/built-in-skills/prismer-ascii-video/references/composition.md +900 -0
  240. package/built-in-skills/prismer-ascii-video/references/effects.md +1877 -0
  241. package/built-in-skills/prismer-ascii-video/references/inputs.md +693 -0
  242. package/built-in-skills/prismer-ascii-video/references/optimization.md +688 -0
  243. package/built-in-skills/prismer-ascii-video/references/scenes.md +1018 -0
  244. package/built-in-skills/prismer-ascii-video/references/shaders.md +1408 -0
  245. package/built-in-skills/prismer-ascii-video/references/troubleshooting.md +360 -0
  246. package/built-in-skills/prismer-baoyu-infographic/LICENSE +21 -0
  247. package/built-in-skills/prismer-baoyu-infographic/LICENSE.hermes +21 -0
  248. package/built-in-skills/prismer-baoyu-infographic/LICENSE.origin +21 -0
  249. package/built-in-skills/prismer-baoyu-infographic/PORT_NOTES.md +37 -0
  250. package/built-in-skills/prismer-baoyu-infographic/PROVENANCE.md +7 -0
  251. package/built-in-skills/prismer-baoyu-infographic/SKILL.md +240 -0
  252. package/built-in-skills/prismer-baoyu-infographic/references/analysis-framework.md +182 -0
  253. package/built-in-skills/prismer-baoyu-infographic/references/base-prompt.md +43 -0
  254. package/built-in-skills/prismer-baoyu-infographic/references/layouts/bento-grid.md +41 -0
  255. package/built-in-skills/prismer-baoyu-infographic/references/layouts/binary-comparison.md +48 -0
  256. package/built-in-skills/prismer-baoyu-infographic/references/layouts/bridge.md +41 -0
  257. package/built-in-skills/prismer-baoyu-infographic/references/layouts/circular-flow.md +41 -0
  258. package/built-in-skills/prismer-baoyu-infographic/references/layouts/comic-strip.md +41 -0
  259. package/built-in-skills/prismer-baoyu-infographic/references/layouts/comparison-matrix.md +41 -0
  260. package/built-in-skills/prismer-baoyu-infographic/references/layouts/dashboard.md +41 -0
  261. package/built-in-skills/prismer-baoyu-infographic/references/layouts/dense-modules.md +72 -0
  262. package/built-in-skills/prismer-baoyu-infographic/references/layouts/funnel.md +41 -0
  263. package/built-in-skills/prismer-baoyu-infographic/references/layouts/hierarchical-layers.md +48 -0
  264. package/built-in-skills/prismer-baoyu-infographic/references/layouts/hub-spoke.md +41 -0
  265. package/built-in-skills/prismer-baoyu-infographic/references/layouts/iceberg.md +41 -0
  266. package/built-in-skills/prismer-baoyu-infographic/references/layouts/isometric-map.md +41 -0
  267. package/built-in-skills/prismer-baoyu-infographic/references/layouts/jigsaw.md +41 -0
  268. package/built-in-skills/prismer-baoyu-infographic/references/layouts/linear-progression.md +48 -0
  269. package/built-in-skills/prismer-baoyu-infographic/references/layouts/periodic-table.md +41 -0
  270. package/built-in-skills/prismer-baoyu-infographic/references/layouts/story-mountain.md +41 -0
  271. package/built-in-skills/prismer-baoyu-infographic/references/layouts/structural-breakdown.md +48 -0
  272. package/built-in-skills/prismer-baoyu-infographic/references/layouts/tree-branching.md +41 -0
  273. package/built-in-skills/prismer-baoyu-infographic/references/layouts/venn-diagram.md +41 -0
  274. package/built-in-skills/prismer-baoyu-infographic/references/layouts/winding-roadmap.md +41 -0
  275. package/built-in-skills/prismer-baoyu-infographic/references/structured-content-template.md +244 -0
  276. package/built-in-skills/prismer-baoyu-infographic/references/styles/aged-academia.md +36 -0
  277. package/built-in-skills/prismer-baoyu-infographic/references/styles/bold-graphic.md +36 -0
  278. package/built-in-skills/prismer-baoyu-infographic/references/styles/chalkboard.md +61 -0
  279. package/built-in-skills/prismer-baoyu-infographic/references/styles/claymation.md +29 -0
  280. package/built-in-skills/prismer-baoyu-infographic/references/styles/corporate-memphis.md +29 -0
  281. package/built-in-skills/prismer-baoyu-infographic/references/styles/craft-handmade.md +44 -0
  282. package/built-in-skills/prismer-baoyu-infographic/references/styles/cyberpunk-neon.md +29 -0
  283. package/built-in-skills/prismer-baoyu-infographic/references/styles/hand-drawn-edu.md +63 -0
  284. package/built-in-skills/prismer-baoyu-infographic/references/styles/ikea-manual.md +29 -0
  285. package/built-in-skills/prismer-baoyu-infographic/references/styles/kawaii.md +29 -0
  286. package/built-in-skills/prismer-baoyu-infographic/references/styles/knolling.md +29 -0
  287. package/built-in-skills/prismer-baoyu-infographic/references/styles/lego-brick.md +29 -0
  288. package/built-in-skills/prismer-baoyu-infographic/references/styles/morandi-journal.md +60 -0
  289. package/built-in-skills/prismer-baoyu-infographic/references/styles/origami.md +29 -0
  290. package/built-in-skills/prismer-baoyu-infographic/references/styles/pixel-art.md +29 -0
  291. package/built-in-skills/prismer-baoyu-infographic/references/styles/pop-laboratory.md +48 -0
  292. package/built-in-skills/prismer-baoyu-infographic/references/styles/retro-pop-grid.md +47 -0
  293. package/built-in-skills/prismer-baoyu-infographic/references/styles/storybook-watercolor.md +29 -0
  294. package/built-in-skills/prismer-baoyu-infographic/references/styles/subway-map.md +29 -0
  295. package/built-in-skills/prismer-baoyu-infographic/references/styles/technical-schematic.md +36 -0
  296. package/built-in-skills/prismer-baoyu-infographic/references/styles/ui-wireframe.md +29 -0
  297. package/built-in-skills/prismer-blocked-page-recovery/LICENSE +21 -0
  298. package/built-in-skills/prismer-blocked-page-recovery/NOTICE.md +17 -0
  299. package/built-in-skills/prismer-blocked-page-recovery/SKILL.md +158 -0
  300. package/built-in-skills/prismer-blocked-page-recovery/scripts/recover_page.py +311 -0
  301. package/built-in-skills/prismer-box/LICENSE +21 -0
  302. package/built-in-skills/prismer-box/NOTICE.md +11 -0
  303. package/built-in-skills/prismer-box/SKILL.md +147 -0
  304. package/built-in-skills/prismer-box/references/bulk-operations.md +43 -0
  305. package/built-in-skills/prismer-box/references/cli-guide.md +133 -0
  306. package/built-in-skills/prismer-box/references/content-workflows.md +86 -0
  307. package/built-in-skills/prismer-box/references/hubs.md +77 -0
  308. package/built-in-skills/prismer-box/references/oauth-setup.md +92 -0
  309. package/built-in-skills/prismer-box/references/rest-api.md +37 -0
  310. package/built-in-skills/prismer-box/references/sdk-development.md +85 -0
  311. package/built-in-skills/prismer-box/references/search-and-ai.md +147 -0
  312. package/built-in-skills/prismer-box/references/troubleshooting.md +33 -0
  313. package/built-in-skills/prismer-box/references/webhooks-and-events.md +43 -0
  314. package/built-in-skills/prismer-codebase-inspection/LICENSE +21 -0
  315. package/built-in-skills/prismer-codebase-inspection/NOTICE.md +17 -0
  316. package/built-in-skills/prismer-codebase-inspection/SKILL.md +141 -0
  317. package/built-in-skills/prismer-computer-use/LICENSE +21 -0
  318. package/built-in-skills/prismer-computer-use/NOTICE.md +17 -0
  319. package/built-in-skills/prismer-computer-use/SKILL.md +372 -0
  320. package/built-in-skills/prismer-design-md/LICENSE +21 -0
  321. package/built-in-skills/prismer-design-md/LICENSE.hermes +21 -0
  322. package/built-in-skills/prismer-design-md/PROVENANCE.md +7 -0
  323. package/built-in-skills/prismer-design-md/SKILL.md +227 -0
  324. package/built-in-skills/prismer-design-md/templates/starter.md +99 -0
  325. package/built-in-skills/prismer-email-inbox-triage/LICENSE +21 -0
  326. package/built-in-skills/prismer-email-inbox-triage/NOTICE.md +11 -0
  327. package/built-in-skills/prismer-email-inbox-triage/SKILL.md +116 -0
  328. package/built-in-skills/prismer-findmy/LICENSE +21 -0
  329. package/built-in-skills/prismer-findmy/NOTICE.md +17 -0
  330. package/built-in-skills/prismer-findmy/SKILL.md +150 -0
  331. package/built-in-skills/prismer-findmy/scripts/readiness.py +47 -0
  332. package/built-in-skills/prismer-gif-search/LICENSE +21 -0
  333. package/built-in-skills/prismer-gif-search/LICENSE.hermes +21 -0
  334. package/built-in-skills/prismer-gif-search/PROVENANCE.md +7 -0
  335. package/built-in-skills/prismer-gif-search/SKILL.md +97 -0
  336. package/built-in-skills/prismer-gif-search/scripts/tenor.py +81 -0
  337. package/built-in-skills/prismer-github/LICENSE +21 -0
  338. package/built-in-skills/prismer-github/NOTICE.md +17 -0
  339. package/built-in-skills/prismer-github/SKILL.md +74 -0
  340. package/built-in-skills/prismer-github/references/auth.md +91 -0
  341. package/built-in-skills/prismer-github/references/ci-troubleshooting.md +193 -0
  342. package/built-in-skills/prismer-github/references/code-review.md +465 -0
  343. package/built-in-skills/prismer-github/references/conventional-commits.md +81 -0
  344. package/built-in-skills/prismer-github/references/github-api-cheatsheet.md +177 -0
  345. package/built-in-skills/prismer-github/references/issue-to-pr.md +74 -0
  346. package/built-in-skills/prismer-github/references/issues.md +336 -0
  347. package/built-in-skills/prismer-github/references/pr-workflow.md +346 -0
  348. package/built-in-skills/prismer-github/references/repo-management.md +484 -0
  349. package/built-in-skills/prismer-github/references/review-output-template.md +84 -0
  350. package/built-in-skills/prismer-github/scripts/ensure-gh.py +132 -0
  351. package/built-in-skills/prismer-github/scripts/gh-env.sh +25 -0
  352. package/built-in-skills/prismer-github/scripts/git-credential-token.py +68 -0
  353. package/built-in-skills/prismer-github/scripts/github-api.py +71 -0
  354. package/built-in-skills/prismer-github/scripts/github-context.py +28 -0
  355. package/built-in-skills/prismer-github/templates/bug-report.md +35 -0
  356. package/built-in-skills/prismer-github/templates/feature-request.md +31 -0
  357. package/built-in-skills/prismer-github/templates/pr-body-bugfix.md +35 -0
  358. package/built-in-skills/prismer-github/templates/pr-body-feature.md +33 -0
  359. package/built-in-skills/prismer-google-workspace/LICENSE +21 -0
  360. package/built-in-skills/prismer-google-workspace/NOTICE.md +11 -0
  361. package/built-in-skills/prismer-google-workspace/SKILL.md +369 -0
  362. package/built-in-skills/prismer-google-workspace/references/daily-brief.md +65 -0
  363. package/built-in-skills/prismer-google-workspace/references/gmail-search-syntax.md +63 -0
  364. package/built-in-skills/prismer-google-workspace/scripts/_hermes_home.py +17 -0
  365. package/built-in-skills/prismer-google-workspace/scripts/_private_state.py +39 -0
  366. package/built-in-skills/prismer-google-workspace/scripts/google_api.py +1339 -0
  367. package/built-in-skills/prismer-google-workspace/scripts/gws_bridge.py +121 -0
  368. package/built-in-skills/prismer-google-workspace/scripts/setup.py +563 -0
  369. package/built-in-skills/prismer-google-workspace/tests/test_productivity_regressions.py +214 -0
  370. package/built-in-skills/prismer-hermes-agent/LICENSE +21 -0
  371. package/built-in-skills/prismer-hermes-agent/NOTICE.md +17 -0
  372. package/built-in-skills/prismer-hermes-agent/SKILL.md +247 -0
  373. package/built-in-skills/prismer-hermes-agent/references/background-systems.md +108 -0
  374. package/built-in-skills/prismer-hermes-agent/references/cli-reference.md +158 -0
  375. package/built-in-skills/prismer-hermes-agent/references/configuration.md +96 -0
  376. package/built-in-skills/prismer-hermes-agent/references/contributor-guide.md +151 -0
  377. package/built-in-skills/prismer-hermes-agent/references/delegate-task-concurrency-diagnosis.md +47 -0
  378. package/built-in-skills/prismer-hermes-agent/references/desktop-plugins.md +215 -0
  379. package/built-in-skills/prismer-hermes-agent/references/native-mcp.md +351 -0
  380. package/built-in-skills/prismer-hermes-agent/references/petdex.md +82 -0
  381. package/built-in-skills/prismer-hermes-agent/references/portal-auth-for-third-party-apps.md +135 -0
  382. package/built-in-skills/prismer-hermes-agent/references/project-context-files.md +55 -0
  383. package/built-in-skills/prismer-hermes-agent/references/providers-and-models.md +81 -0
  384. package/built-in-skills/prismer-hermes-agent/references/security-privacy.md +73 -0
  385. package/built-in-skills/prismer-hermes-agent/references/slash-commands.md +119 -0
  386. package/built-in-skills/prismer-hermes-agent/references/themes.md +134 -0
  387. package/built-in-skills/prismer-hermes-agent/references/troubleshooting.md +87 -0
  388. package/built-in-skills/prismer-hermes-agent/references/tui-widgets.md +144 -0
  389. package/built-in-skills/prismer-hermes-agent/references/webhooks.md +219 -0
  390. package/built-in-skills/prismer-hermes-agent/references/windows-quirks.md +64 -0
  391. package/built-in-skills/prismer-hermes-agent/templates/clock.mjs +51 -0
  392. package/built-in-skills/prismer-hermes-agent/templates/plugin.js +97 -0
  393. package/built-in-skills/prismer-hermes-agent/templates/skin.yaml +49 -0
  394. package/built-in-skills/prismer-himalaya/LICENSE +21 -0
  395. package/built-in-skills/prismer-himalaya/NOTICE.md +11 -0
  396. package/built-in-skills/prismer-himalaya/SKILL.md +340 -0
  397. package/built-in-skills/prismer-himalaya/references/configuration.md +227 -0
  398. package/built-in-skills/prismer-himalaya/references/message-composition.md +206 -0
  399. package/built-in-skills/prismer-himalaya/scripts/compose_draft.py +48 -0
  400. package/built-in-skills/prismer-im-collab/SKILL.md +27 -7
  401. package/built-in-skills/prismer-imessage/LICENSE +21 -0
  402. package/built-in-skills/prismer-imessage/NOTICE.md +17 -0
  403. package/built-in-skills/prismer-imessage/SKILL.md +128 -0
  404. package/built-in-skills/prismer-imessage/scripts/readiness.py +47 -0
  405. package/built-in-skills/prismer-manim-video/LICENSE +21 -0
  406. package/built-in-skills/prismer-manim-video/LICENSE.hermes +21 -0
  407. package/built-in-skills/prismer-manim-video/PROVENANCE.md +7 -0
  408. package/built-in-skills/prismer-manim-video/README.md +23 -0
  409. package/built-in-skills/prismer-manim-video/SKILL.md +279 -0
  410. package/built-in-skills/prismer-manim-video/references/animation-design-thinking.md +161 -0
  411. package/built-in-skills/prismer-manim-video/references/animations.md +282 -0
  412. package/built-in-skills/prismer-manim-video/references/camera-and-3d.md +135 -0
  413. package/built-in-skills/prismer-manim-video/references/decorations.md +202 -0
  414. package/built-in-skills/prismer-manim-video/references/equations.md +216 -0
  415. package/built-in-skills/prismer-manim-video/references/graphs-and-data.md +163 -0
  416. package/built-in-skills/prismer-manim-video/references/mobjects.md +333 -0
  417. package/built-in-skills/prismer-manim-video/references/paper-explainer.md +255 -0
  418. package/built-in-skills/prismer-manim-video/references/production-quality.md +190 -0
  419. package/built-in-skills/prismer-manim-video/references/rendering.md +187 -0
  420. package/built-in-skills/prismer-manim-video/references/scene-planning.md +118 -0
  421. package/built-in-skills/prismer-manim-video/references/troubleshooting.md +135 -0
  422. package/built-in-skills/prismer-manim-video/references/updaters-and-trackers.md +260 -0
  423. package/built-in-skills/prismer-manim-video/references/visual-design.md +124 -0
  424. package/built-in-skills/prismer-manim-video/scripts/setup.sh +15 -0
  425. package/built-in-skills/prismer-maps/LICENSE +21 -0
  426. package/built-in-skills/prismer-maps/NOTICE.md +11 -0
  427. package/built-in-skills/prismer-maps/SKILL.md +240 -0
  428. package/built-in-skills/prismer-maps/scripts/maps_client.py +1324 -0
  429. package/built-in-skills/prismer-node-inspect-debugger/LICENSE +21 -0
  430. package/built-in-skills/prismer-node-inspect-debugger/NOTICE.md +17 -0
  431. package/built-in-skills/prismer-node-inspect-debugger/SKILL.md +347 -0
  432. package/built-in-skills/prismer-notion/LICENSE +21 -0
  433. package/built-in-skills/prismer-notion/NOTICE.md +11 -0
  434. package/built-in-skills/prismer-notion/SKILL.md +495 -0
  435. package/built-in-skills/prismer-notion/references/block-types.md +112 -0
  436. package/built-in-skills/prismer-obsidian/LICENSE +21 -0
  437. package/built-in-skills/prismer-obsidian/LICENSE.hermes +21 -0
  438. package/built-in-skills/prismer-obsidian/PROVENANCE.md +7 -0
  439. package/built-in-skills/prismer-obsidian/SKILL.md +80 -0
  440. package/built-in-skills/prismer-obsidian/provenance/category-DESCRIPTION.md +3 -0
  441. package/built-in-skills/prismer-obsidian/scripts/write_note.py +84 -0
  442. package/built-in-skills/prismer-p5js/LICENSE +21 -0
  443. package/built-in-skills/prismer-p5js/LICENSE.hermes +21 -0
  444. package/built-in-skills/prismer-p5js/PROVENANCE.md +7 -0
  445. package/built-in-skills/prismer-p5js/README.md +64 -0
  446. package/built-in-skills/prismer-p5js/SKILL.md +574 -0
  447. package/built-in-skills/prismer-p5js/references/animation.md +439 -0
  448. package/built-in-skills/prismer-p5js/references/color-systems.md +355 -0
  449. package/built-in-skills/prismer-p5js/references/core-api.md +410 -0
  450. package/built-in-skills/prismer-p5js/references/export-pipeline.md +534 -0
  451. package/built-in-skills/prismer-p5js/references/interaction.md +398 -0
  452. package/built-in-skills/prismer-p5js/references/shapes-and-geometry.md +300 -0
  453. package/built-in-skills/prismer-p5js/references/troubleshooting.md +532 -0
  454. package/built-in-skills/prismer-p5js/references/typography.md +302 -0
  455. package/built-in-skills/prismer-p5js/references/visual-effects.md +905 -0
  456. package/built-in-skills/prismer-p5js/references/webgl-and-3d.md +423 -0
  457. package/built-in-skills/prismer-p5js/scripts/export-frames.js +91 -0
  458. package/built-in-skills/prismer-p5js/scripts/render.sh +113 -0
  459. package/built-in-skills/prismer-p5js/scripts/serve.sh +25 -0
  460. package/built-in-skills/prismer-p5js/scripts/setup.sh +87 -0
  461. package/built-in-skills/prismer-p5js/templates/viewer.html +401 -0
  462. package/built-in-skills/prismer-popular-web-designs/LICENSE +21 -0
  463. package/built-in-skills/prismer-popular-web-designs/LICENSE.hermes +21 -0
  464. package/built-in-skills/prismer-popular-web-designs/LICENSE.origin +21 -0
  465. package/built-in-skills/prismer-popular-web-designs/PROVENANCE.md +7 -0
  466. package/built-in-skills/prismer-popular-web-designs/SKILL.md +218 -0
  467. package/built-in-skills/prismer-popular-web-designs/templates/airbnb.md +259 -0
  468. package/built-in-skills/prismer-popular-web-designs/templates/airtable.md +102 -0
  469. package/built-in-skills/prismer-popular-web-designs/templates/apple.md +326 -0
  470. package/built-in-skills/prismer-popular-web-designs/templates/bmw.md +193 -0
  471. package/built-in-skills/prismer-popular-web-designs/templates/cal.md +272 -0
  472. package/built-in-skills/prismer-popular-web-designs/templates/claude.md +325 -0
  473. package/built-in-skills/prismer-popular-web-designs/templates/clay.md +317 -0
  474. package/built-in-skills/prismer-popular-web-designs/templates/clickhouse.md +294 -0
  475. package/built-in-skills/prismer-popular-web-designs/templates/cohere.md +279 -0
  476. package/built-in-skills/prismer-popular-web-designs/templates/coinbase.md +142 -0
  477. package/built-in-skills/prismer-popular-web-designs/templates/composio.md +320 -0
  478. package/built-in-skills/prismer-popular-web-designs/templates/cursor.md +322 -0
  479. package/built-in-skills/prismer-popular-web-designs/templates/elevenlabs.md +278 -0
  480. package/built-in-skills/prismer-popular-web-designs/templates/expo.md +294 -0
  481. package/built-in-skills/prismer-popular-web-designs/templates/figma.md +233 -0
  482. package/built-in-skills/prismer-popular-web-designs/templates/framer.md +259 -0
  483. package/built-in-skills/prismer-popular-web-designs/templates/hashicorp.md +291 -0
  484. package/built-in-skills/prismer-popular-web-designs/templates/ibm.md +345 -0
  485. package/built-in-skills/prismer-popular-web-designs/templates/intercom.md +159 -0
  486. package/built-in-skills/prismer-popular-web-designs/templates/kraken.md +138 -0
  487. package/built-in-skills/prismer-popular-web-designs/templates/linear.app.md +380 -0
  488. package/built-in-skills/prismer-popular-web-designs/templates/lovable.md +311 -0
  489. package/built-in-skills/prismer-popular-web-designs/templates/minimax.md +270 -0
  490. package/built-in-skills/prismer-popular-web-designs/templates/mintlify.md +339 -0
  491. package/built-in-skills/prismer-popular-web-designs/templates/miro.md +121 -0
  492. package/built-in-skills/prismer-popular-web-designs/templates/mistral.ai.md +274 -0
  493. package/built-in-skills/prismer-popular-web-designs/templates/mongodb.md +279 -0
  494. package/built-in-skills/prismer-popular-web-designs/templates/notion.md +322 -0
  495. package/built-in-skills/prismer-popular-web-designs/templates/nvidia.md +306 -0
  496. package/built-in-skills/prismer-popular-web-designs/templates/ollama.md +280 -0
  497. package/built-in-skills/prismer-popular-web-designs/templates/opencode.ai.md +294 -0
  498. package/built-in-skills/prismer-popular-web-designs/templates/pinterest.md +243 -0
  499. package/built-in-skills/prismer-popular-web-designs/templates/posthog.md +269 -0
  500. package/built-in-skills/prismer-popular-web-designs/templates/raycast.md +281 -0
  501. package/built-in-skills/prismer-popular-web-designs/templates/replicate.md +274 -0
  502. package/built-in-skills/prismer-popular-web-designs/templates/resend.md +316 -0
  503. package/built-in-skills/prismer-popular-web-designs/templates/revolut.md +198 -0
  504. package/built-in-skills/prismer-popular-web-designs/templates/runwayml.md +257 -0
  505. package/built-in-skills/prismer-popular-web-designs/templates/sanity.md +370 -0
  506. package/built-in-skills/prismer-popular-web-designs/templates/sentry.md +275 -0
  507. package/built-in-skills/prismer-popular-web-designs/templates/spacex.md +207 -0
  508. package/built-in-skills/prismer-popular-web-designs/templates/spotify.md +259 -0
  509. package/built-in-skills/prismer-popular-web-designs/templates/stripe.md +335 -0
  510. package/built-in-skills/prismer-popular-web-designs/templates/supabase.md +268 -0
  511. package/built-in-skills/prismer-popular-web-designs/templates/superhuman.md +265 -0
  512. package/built-in-skills/prismer-popular-web-designs/templates/together.ai.md +276 -0
  513. package/built-in-skills/prismer-popular-web-designs/templates/uber.md +308 -0
  514. package/built-in-skills/prismer-popular-web-designs/templates/vercel.md +323 -0
  515. package/built-in-skills/prismer-popular-web-designs/templates/voltagent.md +336 -0
  516. package/built-in-skills/prismer-popular-web-designs/templates/warp.md +266 -0
  517. package/built-in-skills/prismer-popular-web-designs/templates/webflow.md +105 -0
  518. package/built-in-skills/prismer-popular-web-designs/templates/wise.md +186 -0
  519. package/built-in-skills/prismer-popular-web-designs/templates/x.ai.md +270 -0
  520. package/built-in-skills/prismer-popular-web-designs/templates/zapier.md +341 -0
  521. package/built-in-skills/prismer-product-price-monitor/LICENSE +21 -0
  522. package/built-in-skills/prismer-product-price-monitor/NOTICE.md +11 -0
  523. package/built-in-skills/prismer-product-price-monitor/SKILL.md +97 -0
  524. package/built-in-skills/prismer-product-price-monitor/scripts/watch_state.py +116 -0
  525. package/built-in-skills/prismer-python-debugpy/LICENSE +21 -0
  526. package/built-in-skills/prismer-python-debugpy/NOTICE.md +17 -0
  527. package/built-in-skills/prismer-python-debugpy/SKILL.md +377 -0
  528. package/built-in-skills/prismer-python-debugpy/scripts/dap_client.py +68 -0
  529. package/built-in-skills/prismer-songsee/LICENSE +21 -0
  530. package/built-in-skills/prismer-songsee/LICENSE.hermes +21 -0
  531. package/built-in-skills/prismer-songsee/PROVENANCE.md +7 -0
  532. package/built-in-skills/prismer-songsee/SKILL.md +91 -0
  533. package/built-in-skills/prismer-songwriting-and-ai-music/LICENSE +21 -0
  534. package/built-in-skills/prismer-songwriting-and-ai-music/LICENSE.hermes +21 -0
  535. package/built-in-skills/prismer-songwriting-and-ai-music/PROVENANCE.md +7 -0
  536. package/built-in-skills/prismer-songwriting-and-ai-music/SKILL.md +317 -0
  537. package/built-in-skills/prismer-spike/LICENSE +21 -0
  538. package/built-in-skills/prismer-spike/LICENSE.gsd +21 -0
  539. package/built-in-skills/prismer-spike/NOTICE.md +17 -0
  540. package/built-in-skills/prismer-spike/SKILL.md +212 -0
  541. package/built-in-skills/prismer-teams-meeting-pipeline/LICENSE +21 -0
  542. package/built-in-skills/prismer-teams-meeting-pipeline/NOTICE.md +11 -0
  543. package/built-in-skills/prismer-teams-meeting-pipeline/SKILL.md +156 -0
  544. package/built-in-skills/prismer-xurl/LICENSE +21 -0
  545. package/built-in-skills/prismer-xurl/LICENSE.hermes +21 -0
  546. package/built-in-skills/prismer-xurl/PROVENANCE.md +7 -0
  547. package/built-in-skills/prismer-xurl/SKILL.md +444 -0
  548. package/built-in-skills/prismer-xurl/provenance/category-DESCRIPTION.md +3 -0
  549. package/built-in-skills/prismer-youtube-content/LICENSE +21 -0
  550. package/built-in-skills/prismer-youtube-content/LICENSE.hermes +21 -0
  551. package/built-in-skills/prismer-youtube-content/PROVENANCE.md +7 -0
  552. package/built-in-skills/prismer-youtube-content/SKILL.md +86 -0
  553. package/built-in-skills/prismer-youtube-content/provenance/category-DESCRIPTION.md +3 -0
  554. package/built-in-skills/prismer-youtube-content/references/output-formats.md +56 -0
  555. package/built-in-skills/prismer-youtube-content/scripts/fetch_transcript.py +146 -0
  556. package/built-in-skills/proactivity/SKILL.md +84 -0
  557. package/built-in-skills/remotion/SKILL.md +431 -0
  558. package/built-in-skills/research-arxiv/LICENSE +21 -0
  559. package/built-in-skills/research-arxiv/NOTICE.md +4 -0
  560. package/built-in-skills/research-arxiv/SKILL.md +263 -0
  561. package/built-in-skills/research-arxiv/scripts/search_arxiv.py +111 -0
  562. package/built-in-skills/role-builder/SKILL.md +209 -0
  563. package/built-in-skills/role-builder/scripts/author-role.mjs +350 -0
  564. package/built-in-skills/role-builder/scripts/ingest-role.mjs +121 -0
  565. package/built-in-skills/role-builder/scripts/instantiate-and-run.mjs +304 -0
  566. package/built-in-skills/role-builder/scripts/operation-harness.mjs +269 -0
  567. package/built-in-skills/skill-creator/LICENSE.prismer +21 -0
  568. package/built-in-skills/skill-creator/NOTICE.md +18 -0
  569. package/built-in-skills/skill-creator/SKILL.md +182 -421
  570. package/built-in-skills/skill-creator/eval-viewer/generate_review.py +20 -31
  571. package/built-in-skills/skill-creator/eval-viewer/viewer.html +35 -23
  572. package/built-in-skills/skill-creator/references/external-library-import.md +110 -0
  573. package/built-in-skills/skill-creator/references/hermes-agent-skill-authoring/GUIDE.md +220 -0
  574. package/built-in-skills/skill-creator/references/hermes-agent-skill-authoring/LICENSE +21 -0
  575. package/built-in-skills/skill-creator/references/skill-authoring/GUIDE.md +134 -0
  576. package/built-in-skills/{skill-authoring/skill.json → skill-creator/references/skill-authoring/draft-contract.json} +3 -3
  577. package/built-in-skills/skill-creator/references/skill-builder/GUIDE.md +167 -0
  578. package/built-in-skills/skill-creator/references/skill-builder/scripts/ingest.mjs +93 -0
  579. package/built-in-skills/skill-creator/scripts/aggregate_benchmark.py +3 -2
  580. package/built-in-skills/skill-creator/scripts/import-library.mjs +498 -0
  581. package/built-in-skills/skill-creator/scripts/improve_description.py +14 -16
  582. package/built-in-skills/skill-creator/scripts/package_skill.py +16 -9
  583. package/built-in-skills/skill-creator/scripts/quick_validate.py +22 -5
  584. package/built-in-skills/skill-creator/scripts/run_eval.py +63 -144
  585. package/built-in-skills/skill-creator/scripts/run_loop.py +24 -16
  586. package/built-in-skills/slack-gif-creator/SKILL.md +20 -0
  587. package/built-in-skills/slack-gif-creator/core/gif_builder.py +13 -2
  588. package/built-in-skills/slack-gif-creator/core/validators.py +17 -7
  589. package/built-in-skills/tasks/SKILL.md +39 -24
  590. package/built-in-skills/tdd/LICENSE +21 -0
  591. package/built-in-skills/tdd/SKILL.md +117 -0
  592. package/built-in-skills/tdd/mocking.md +59 -0
  593. package/built-in-skills/tdd/refactoring.md +10 -0
  594. package/built-in-skills/tdd/references/test-driven-development/GUIDE.md +356 -0
  595. package/built-in-skills/tdd/references/test-driven-development/LICENSE +21 -0
  596. package/built-in-skills/tdd/references/test-driven-development/LICENSE.superpowers +21 -0
  597. package/built-in-skills/tdd/references/test-driven-development/NOTICE.md +37 -0
  598. package/built-in-skills/tdd/tests.md +61 -0
  599. package/built-in-skills/team/SKILL.md +3 -2
  600. package/built-in-skills/web-artifacts-builder/SKILL.md +4 -3
  601. package/built-in-skills/web-artifacts-builder/scripts/bundle-artifact.sh +9 -7
  602. package/built-in-skills/web-artifacts-builder/scripts/init-artifact.sh +28 -55
  603. package/built-in-skills/webapp-qa/LICENSE +21 -0
  604. package/built-in-skills/webapp-qa/NOTICE.md +37 -0
  605. package/built-in-skills/webapp-qa/SKILL.md +41 -0
  606. package/built-in-skills/webapp-qa/references/dogfood/GUIDE.md +161 -0
  607. package/built-in-skills/webapp-qa/references/issue-taxonomy.md +109 -0
  608. package/built-in-skills/webapp-qa/templates/dogfood-report-template.md +86 -0
  609. package/built-in-skills/webapp-testing/SKILL.md +10 -2
  610. package/built-in-skills/webapp-testing/examples/console_logging.py +10 -4
  611. package/built-in-skills/webapp-testing/examples/element_discovery.py +11 -4
  612. package/built-in-skills/webapp-testing/examples/static_html_automation.py +14 -9
  613. package/built-in-skills/webapp-testing/scripts/with_server.py +34 -10
  614. package/built-in-skills/wechat-pay/SKILL.md +59 -0
  615. package/built-in-skills/weekly-review/LICENSE +21 -0
  616. package/built-in-skills/weekly-review/NOTICE.md +21 -0
  617. package/built-in-skills/weekly-review/SKILL.md +49 -0
  618. package/components/eaas-pi-components.manifest.json +37 -0
  619. package/dist/cli.cjs +75799 -20210
  620. package/dist/cli.js +75900 -20248
  621. package/dist/index.cjs +76159 -20082
  622. package/dist/index.d.cts +4766 -766
  623. package/dist/index.d.ts +4766 -766
  624. package/dist/index.js +76040 -19960
  625. package/package.json +42 -6
  626. package/plugins/memory/prismer/__init__.py +1215 -0
  627. package/plugins/memory/prismer/plugin.yaml +8 -0
  628. package/plugins/memory/prismer/tool-schemas.generated.json +257 -0
  629. package/plugins/tools/prismer-recall/__init__.py +282 -0
  630. package/plugins/tools/prismer-recall/plugin.yaml +15 -0
  631. package/built-in-skills/memory-curation/SKILL.md +0 -135
  632. package/built-in-skills/skill-authoring/SKILL.md +0 -124
  633. package/built-in-skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
@@ -0,0 +1,1877 @@
1
+ # Effect Catalog
2
+
3
+ Effect building blocks that produce visual patterns. In v2, these are used **inside scene functions** that return a pixel canvas directly. The building blocks below operate on grid coordinate arrays and produce `(chars, colors)` or value/hue fields that the scene function renders to canvas via `_render_vf()`.
4
+
5
+ > **See also:** architecture.md · composition.md · scenes.md · shaders.md · troubleshooting.md
6
+
7
+ ## Design Philosophy
8
+
9
+ Effects are the creative core. Don't copy these verbatim for every project -- use them as **building blocks** and **combine, modify, and invent** new ones. Every project should feel distinct.
10
+
11
+ Key principles:
12
+ - **Layer multiple effects** rather than using a single monolithic function
13
+ - **Parameterize everything** -- hue, speed, density, amplitude should all be arguments
14
+ - **React to features** -- audio/video features should modulate at least 2-3 parameters per effect
15
+ - **Vary per section** -- never use the same effect config for the entire video
16
+ - **Invent project-specific effects** -- the catalog below is a starting vocabulary, not a fixed set
17
+
18
+ ---
19
+
20
+ ## Background Fills
21
+
22
+ Every effect should start with a background. Never leave flat black.
23
+
24
+ ### Animated Sine Field (General Purpose)
25
+ ```python
26
+ def bg_sinefield(g, f, t, hue=0.6, bri=0.5, pal=PAL_DEFAULT,
27
+ freq=(0.13, 0.17, 0.07, 0.09), speed=(0.5, -0.4, -0.3, 0.2)):
28
+ """Layered sine field. Adjust freq/speed tuples for different textures."""
29
+ v1 = np.sin(g.cc*freq[0] + t*speed[0]) * np.sin(g.rr*freq[1] - t*speed[1]) * 0.5 + 0.5
30
+ v2 = np.sin(g.cc*freq[2] - t*speed[2] + g.rr*freq[3]) * 0.4 + 0.5
31
+ v3 = np.sin(g.dist_n*5 + t*0.2) * 0.3 + 0.4
32
+ v4 = np.cos(g.angle*3 - t*0.6) * 0.15 + 0.5
33
+ val = np.clip((v1*0.3 + v2*0.25 + v3*0.25 + v4*0.2) * bri * (0.6 + f["rms"]*0.6), 0.06, 1)
34
+ mask = val > 0.03
35
+ ch = val2char(val, mask, pal)
36
+ h = np.full_like(val, hue) + f.get("cent", 0.5)*0.1 + val*0.08
37
+ R, G, B = hsv2rgb(h, np.clip(0.35+f.get("flat",0.4)*0.4, 0, 1) * np.ones_like(val), val)
38
+ return ch, mkc(R, G, B, g.rows, g.cols)
39
+ ```
40
+
41
+ ### Video-Source Background
42
+ ```python
43
+ def bg_video(g, frame_rgb, pal=PAL_DEFAULT, brightness=0.5):
44
+ small = np.array(Image.fromarray(frame_rgb).resize((g.cols, g.rows)))
45
+ lum = np.mean(small, axis=2) / 255.0 * brightness
46
+ mask = lum > 0.02
47
+ ch = val2char(lum, mask, pal)
48
+ co = np.clip(small * np.clip(lum[:,:,None]*1.5+0.3, 0.3, 1), 0, 255).astype(np.uint8)
49
+ return ch, co
50
+ ```
51
+
52
+ ### Noise / Static Field
53
+ ```python
54
+ def bg_noise(g, f, t, pal=PAL_BLOCKS, density=0.3, hue_drift=0.02):
55
+ val = np.random.random((g.rows, g.cols)).astype(np.float32) * density * (0.5 + f["rms"]*0.5)
56
+ val = np.clip(val, 0, 1); mask = val > 0.02
57
+ ch = val2char(val, mask, pal)
58
+ R, G, B = hsv2rgb(np.full_like(val, t*hue_drift % 1), np.full_like(val, 0.3), val)
59
+ return ch, mkc(R, G, B, g.rows, g.cols)
60
+ ```
61
+
62
+ ### Perlin-Like Smooth Noise
63
+ ```python
64
+ def bg_smooth_noise(g, f, t, hue=0.5, bri=0.5, pal=PAL_DOTS, octaves=3):
65
+ """Layered sine approximation of Perlin noise. Cheap, smooth, organic."""
66
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
67
+ for i in range(octaves):
68
+ freq = 0.05 * (2 ** i)
69
+ amp = 0.5 / (i + 1)
70
+ phase = t * (0.3 + i * 0.2)
71
+ val += np.sin(g.cc * freq + phase) * np.cos(g.rr * freq * 0.7 - phase * 0.5) * amp
72
+ val = np.clip(val * 0.5 + 0.5, 0, 1) * bri
73
+ mask = val > 0.03
74
+ ch = val2char(val, mask, pal)
75
+ h = np.full_like(val, hue) + val * 0.1
76
+ R, G, B = hsv2rgb(h, np.full_like(val, 0.5), val)
77
+ return ch, mkc(R, G, B, g.rows, g.cols)
78
+ ```
79
+
80
+ ### Cellular / Voronoi Approximation
81
+ ```python
82
+ def bg_cellular(g, f, t, n_centers=12, hue=0.5, bri=0.6, pal=PAL_BLOCKS):
83
+ """Voronoi-like cells using distance to nearest of N moving centers."""
84
+ rng = np.random.RandomState(42) # deterministic centers
85
+ cx = (rng.rand(n_centers) * g.cols).astype(np.float32)
86
+ cy = (rng.rand(n_centers) * g.rows).astype(np.float32)
87
+ # Animate centers
88
+ cx_t = cx + np.sin(t * 0.5 + np.arange(n_centers) * 0.7) * 5
89
+ cy_t = cy + np.cos(t * 0.4 + np.arange(n_centers) * 0.9) * 3
90
+ # Min distance to any center
91
+ min_d = np.full((g.rows, g.cols), 999.0, dtype=np.float32)
92
+ for i in range(n_centers):
93
+ d = np.sqrt((g.cc - cx_t[i])**2 + (g.rr - cy_t[i])**2)
94
+ min_d = np.minimum(min_d, d)
95
+ val = np.clip(1.0 - min_d / (g.cols * 0.3), 0, 1) * bri
96
+ # Cell edges (where distance is near-equal between two centers)
97
+ # ... second-nearest trick for edge highlighting
98
+ mask = val > 0.03
99
+ ch = val2char(val, mask, pal)
100
+ R, G, B = hsv2rgb(np.full_like(val, hue) + min_d * 0.005, np.full_like(val, 0.5), val)
101
+ return ch, mkc(R, G, B, g.rows, g.cols)
102
+ ```
103
+
104
+ ---
105
+
106
+ > **Note:** The v1 `eff_rings`, `eff_rays`, `eff_spiral`, `eff_glow`, `eff_tunnel`, `eff_vortex`, `eff_freq_waves`, `eff_interference`, `eff_aurora`, and `eff_ripple` functions are superseded by the `vf_*` value field generators below (used via `_render_vf()`). The `vf_*` versions integrate with the multi-grid composition pipeline and are preferred for all new scenes.
107
+
108
+ ---
109
+
110
+ ## Particle Systems
111
+
112
+ ### General Pattern
113
+ All particle systems use persistent state via the `S` dict parameter:
114
+ ```python
115
+ # S is the persistent state dict (same as r.S, passed explicitly)
116
+ if "px" not in S:
117
+ S["px"]=[]; S["py"]=[]; S["vx"]=[]; S["vy"]=[]; S["life"]=[]; S["char"]=[]
118
+
119
+ # Emit new particles (on beat, continuously, or on trigger)
120
+ # Update: position += velocity, apply forces, decay life
121
+ # Draw: map to grid, set char/color based on life
122
+ # Cull: remove dead, cap total count
123
+ ```
124
+
125
+ ### Particle Character Sets
126
+
127
+ Don't hardcode particle chars. Choose per project/mood:
128
+
129
+ ```python
130
+ # Energy / explosive
131
+ PART_ENERGY = list("*+#@\u26a1\u2726\u2605\u2588\u2593")
132
+ PART_SPARK = list("\u00b7\u2022\u25cf\u2605\u2736*+")
133
+ # Organic / natural
134
+ PART_LEAF = list("\u2740\u2741\u2742\u2743\u273f\u2618\u2022")
135
+ PART_SNOW = list("\u2744\u2745\u2746\u00b7\u2022*\u25cb")
136
+ PART_RAIN = list("|\u2502\u2503\u2551/\\")
137
+ PART_BUBBLE = list("\u25cb\u25ce\u25c9\u25cf\u2218\u2219\u00b0")
138
+ # Data / tech
139
+ PART_DATA = list("01{}[]<>|/\\")
140
+ PART_HEX = list("0123456789ABCDEF")
141
+ PART_BINARY = list("01")
142
+ # Mystical
143
+ PART_RUNE = list("\u16a0\u16a2\u16a6\u16b1\u16b7\u16c1\u16c7\u16d2\u16d6\u16da\u16de\u16df\u2726\u2605")
144
+ PART_ZODIAC = list("\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653")
145
+ # Minimal
146
+ PART_DOT = list("\u00b7\u2022\u25cf")
147
+ PART_DASH = list("-=~\u2500\u2550")
148
+ ```
149
+
150
+ ### Explosion (Beat-Triggered)
151
+ ```python
152
+ def emit_explosion(S, f, center_r, center_c, char_set=PART_ENERGY, count_base=80):
153
+ if f.get("beat", 0) > 0:
154
+ for _ in range(int(count_base + f["rms"]*150)):
155
+ ang = random.uniform(0, 2*math.pi)
156
+ sp = random.uniform(1, 9) * (0.5 + f.get("sub_r", 0.3)*2)
157
+ S["px"].append(float(center_c))
158
+ S["py"].append(float(center_r))
159
+ S["vx"].append(math.cos(ang)*sp*2.5)
160
+ S["vy"].append(math.sin(ang)*sp)
161
+ S["life"].append(1.0)
162
+ S["char"].append(random.choice(char_set))
163
+ # Update: gravity on vy += 0.03, life -= 0.015
164
+ # Color: life * 255 for brightness, hue fade controlled by caller
165
+ ```
166
+
167
+ ### Rising Embers
168
+ ```python
169
+ # Emit: sy = rows-1, vy = -random.uniform(1,5), vx = random.uniform(-1.5,1.5)
170
+ # Update: vx += random jitter * 0.3, life -= 0.01
171
+ # Cap at ~1500 particles
172
+ ```
173
+
174
+ ### Dissolving Cloud
175
+ ```python
176
+ # Init: N=600 particles spread across screen
177
+ # Update: slow upward drift, fade life progressively
178
+ # life -= 0.002 * (1 + elapsed * 0.05) # accelerating fade
179
+ ```
180
+
181
+ ### Starfield (3D Projection)
182
+ ```python
183
+ # N stars with (sx, sy, sz) in normalized coords
184
+ # Move: sz -= speed (stars approach camera)
185
+ # Project: px = cx + sx/sz * cx, py = cy + sy/sz * cy
186
+ # Reset stars that pass camera (sz <= 0.01)
187
+ # Brightness = (1 - sz), draw streaks behind bright stars
188
+ ```
189
+
190
+ ### Orbit (Circular/Elliptical Motion)
191
+ ```python
192
+ def emit_orbit(S, n=20, radius=15, speed=1.0, char_set=PART_DOT):
193
+ """Particles orbiting a center point."""
194
+ for i in range(n):
195
+ angle = i * 2 * math.pi / n
196
+ S["px"].append(0.0); S["py"].append(0.0) # will be computed from angle
197
+ S["vx"].append(angle) # store angle as "vx" for orbit
198
+ S["vy"].append(radius + random.uniform(-2, 2)) # store radius
199
+ S["life"].append(1.0)
200
+ S["char"].append(random.choice(char_set))
201
+ # Update: angle += speed * dt, px = cx + radius * cos(angle), py = cy + radius * sin(angle)
202
+ ```
203
+
204
+ ### Gravity Well
205
+ ```python
206
+ # Particles attracted toward one or more gravity points
207
+ # Update: compute force vector toward each well, apply as acceleration
208
+ # Particles that reach well center respawn at edges
209
+ ```
210
+
211
+ ### Flocking / Boids
212
+
213
+ Emergent swarm behavior from three simple rules: separation, alignment, cohesion.
214
+
215
+ ```python
216
+ def update_boids(S, g, f, n_boids=200, perception=8.0, max_speed=2.0,
217
+ sep_weight=1.5, ali_weight=1.0, coh_weight=1.0,
218
+ char_set=None):
219
+ """Boids flocking simulation. Particles self-organize into organic groups.
220
+
221
+ perception: how far each boid can see (grid cells)
222
+ sep_weight: separation (avoid crowding) strength
223
+ ali_weight: alignment (match neighbor velocity) strength
224
+ coh_weight: cohesion (steer toward group center) strength
225
+ """
226
+ if char_set is None:
227
+ char_set = list("·•●◦∘⬤")
228
+ if "boid_x" not in S:
229
+ rng = np.random.RandomState(42)
230
+ S["boid_x"] = rng.uniform(0, g.cols, n_boids).astype(np.float32)
231
+ S["boid_y"] = rng.uniform(0, g.rows, n_boids).astype(np.float32)
232
+ S["boid_vx"] = (rng.random(n_boids).astype(np.float32) - 0.5) * max_speed
233
+ S["boid_vy"] = (rng.random(n_boids).astype(np.float32) - 0.5) * max_speed
234
+ S["boid_ch"] = [random.choice(char_set) for _ in range(n_boids)]
235
+
236
+ bx = S["boid_x"]; by = S["boid_y"]
237
+ bvx = S["boid_vx"]; bvy = S["boid_vy"]
238
+ n = len(bx)
239
+
240
+ # For each boid, compute steering forces
241
+ ax = np.zeros(n, dtype=np.float32)
242
+ ay = np.zeros(n, dtype=np.float32)
243
+
244
+ # Spatial hash for efficient neighbor lookup
245
+ cell_size = perception
246
+ cells = {}
247
+ for i in range(n):
248
+ cx_i = int(bx[i] / cell_size)
249
+ cy_i = int(by[i] / cell_size)
250
+ key = (cx_i, cy_i)
251
+ if key not in cells:
252
+ cells[key] = []
253
+ cells[key].append(i)
254
+
255
+ for i in range(n):
256
+ cx_i = int(bx[i] / cell_size)
257
+ cy_i = int(by[i] / cell_size)
258
+ sep_x, sep_y = 0.0, 0.0
259
+ ali_x, ali_y = 0.0, 0.0
260
+ coh_x, coh_y = 0.0, 0.0
261
+ count = 0
262
+
263
+ # Check neighboring cells
264
+ for dcx in range(-1, 2):
265
+ for dcy in range(-1, 2):
266
+ for j in cells.get((cx_i + dcx, cy_i + dcy), []):
267
+ if j == i:
268
+ continue
269
+ dx = bx[j] - bx[i]
270
+ dy = by[j] - by[i]
271
+ dist = np.sqrt(dx * dx + dy * dy)
272
+ if dist < perception and dist > 0.01:
273
+ count += 1
274
+ # Separation: steer away from close neighbors
275
+ if dist < perception * 0.4:
276
+ sep_x -= dx / (dist * dist)
277
+ sep_y -= dy / (dist * dist)
278
+ # Alignment: match velocity
279
+ ali_x += bvx[j]
280
+ ali_y += bvy[j]
281
+ # Cohesion: steer toward center of group
282
+ coh_x += bx[j]
283
+ coh_y += by[j]
284
+
285
+ if count > 0:
286
+ # Normalize and weight
287
+ ax[i] += sep_x * sep_weight
288
+ ay[i] += sep_y * sep_weight
289
+ ax[i] += (ali_x / count - bvx[i]) * ali_weight * 0.1
290
+ ay[i] += (ali_y / count - bvy[i]) * ali_weight * 0.1
291
+ ax[i] += (coh_x / count - bx[i]) * coh_weight * 0.01
292
+ ay[i] += (coh_y / count - by[i]) * coh_weight * 0.01
293
+
294
+ # Audio reactivity: bass pushes boids outward from center
295
+ if f.get("bass", 0) > 0.5:
296
+ cx_g, cy_g = g.cols / 2, g.rows / 2
297
+ dx = bx - cx_g; dy = by - cy_g
298
+ dist = np.sqrt(dx**2 + dy**2) + 1
299
+ ax += (dx / dist) * f["bass"] * 2
300
+ ay += (dy / dist) * f["bass"] * 2
301
+
302
+ # Update velocity and position
303
+ bvx += ax; bvy += ay
304
+ # Clamp speed
305
+ speed = np.sqrt(bvx**2 + bvy**2) + 1e-10
306
+ over = speed > max_speed
307
+ bvx[over] *= max_speed / speed[over]
308
+ bvy[over] *= max_speed / speed[over]
309
+ bx += bvx; by += bvy
310
+
311
+ # Wrap at edges
312
+ bx %= g.cols; by %= g.rows
313
+
314
+ S["boid_x"] = bx; S["boid_y"] = by
315
+ S["boid_vx"] = bvx; S["boid_vy"] = bvy
316
+
317
+ # Draw
318
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
319
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
320
+ for i in range(n):
321
+ r, c = int(by[i]) % g.rows, int(bx[i]) % g.cols
322
+ ch[r, c] = S["boid_ch"][i]
323
+ spd = min(1.0, speed[i] / max_speed)
324
+ R, G, B = hsv2rgb_scalar(spd * 0.3, 0.8, 0.5 + spd * 0.5)
325
+ co[r, c] = (R, G, B)
326
+ return ch, co
327
+ ```
328
+
329
+ ### Flow Field Particles
330
+
331
+ Particles that follow the gradient of a value field. Any `vf_*` function becomes a "river" that carries particles:
332
+
333
+ ```python
334
+ def update_flow_particles(S, g, f, flow_field, n=500, speed=1.0,
335
+ life_drain=0.005, emit_rate=10,
336
+ char_set=None):
337
+ """Particles steered by a value field gradient.
338
+
339
+ flow_field: float32 (rows, cols) — the field particles follow.
340
+ Particles flow from low to high values (uphill) or along
341
+ the gradient direction.
342
+ """
343
+ if char_set is None:
344
+ char_set = list("·•∘◦°⋅")
345
+ if "fp_x" not in S:
346
+ S["fp_x"] = []; S["fp_y"] = []; S["fp_vx"] = []; S["fp_vy"] = []
347
+ S["fp_life"] = []; S["fp_ch"] = []
348
+
349
+ # Emit new particles at random positions
350
+ for _ in range(emit_rate):
351
+ if len(S["fp_x"]) < n:
352
+ S["fp_x"].append(random.uniform(0, g.cols - 1))
353
+ S["fp_y"].append(random.uniform(0, g.rows - 1))
354
+ S["fp_vx"].append(0.0); S["fp_vy"].append(0.0)
355
+ S["fp_life"].append(1.0)
356
+ S["fp_ch"].append(random.choice(char_set))
357
+
358
+ # Compute gradient of flow field (central differences)
359
+ pad = np.pad(flow_field, 1, mode="wrap")
360
+ grad_x = (pad[1:-1, 2:] - pad[1:-1, :-2]) * 0.5
361
+ grad_y = (pad[2:, 1:-1] - pad[:-2, 1:-1]) * 0.5
362
+
363
+ # Update particles
364
+ i = 0
365
+ while i < len(S["fp_x"]):
366
+ px, py = S["fp_x"][i], S["fp_y"][i]
367
+ # Sample gradient at particle position
368
+ gc = int(px) % g.cols; gr = int(py) % g.rows
369
+ gx = grad_x[gr, gc]; gy = grad_y[gr, gc]
370
+ # Steer velocity toward gradient direction
371
+ S["fp_vx"][i] = S["fp_vx"][i] * 0.9 + gx * speed * 10
372
+ S["fp_vy"][i] = S["fp_vy"][i] * 0.9 + gy * speed * 10
373
+ S["fp_x"][i] += S["fp_vx"][i]
374
+ S["fp_y"][i] += S["fp_vy"][i]
375
+ S["fp_life"][i] -= life_drain
376
+
377
+ if S["fp_life"][i] <= 0:
378
+ for k in ("fp_x", "fp_y", "fp_vx", "fp_vy", "fp_life", "fp_ch"):
379
+ S[k].pop(i)
380
+ else:
381
+ i += 1
382
+
383
+ # Draw
384
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
385
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
386
+ for i in range(len(S["fp_x"])):
387
+ r = int(S["fp_y"][i]) % g.rows
388
+ c = int(S["fp_x"][i]) % g.cols
389
+ ch[r, c] = S["fp_ch"][i]
390
+ v = S["fp_life"][i]
391
+ co[r, c] = (int(v * 200), int(v * 180), int(v * 255))
392
+ return ch, co
393
+ ```
394
+
395
+ ### Particle Trails
396
+
397
+ Draw fading lines between current and previous positions:
398
+
399
+ ```python
400
+ def draw_particle_trails(S, g, trail_key="trails", max_trail=8, fade=0.7):
401
+ """Add trails to any particle system. Call after updating positions.
402
+ Stores previous positions in S[trail_key] and draws fading lines.
403
+
404
+ Expects S to have 'px', 'py' lists (standard particle keys).
405
+ max_trail: number of previous positions to remember
406
+ fade: brightness multiplier per trail step (0.7 = 70% each step back)
407
+ """
408
+ if trail_key not in S:
409
+ S[trail_key] = []
410
+
411
+ # Store current positions
412
+ current = list(zip(
413
+ [int(y) for y in S.get("py", [])],
414
+ [int(x) for x in S.get("px", [])]
415
+ ))
416
+ S[trail_key].append(current)
417
+ if len(S[trail_key]) > max_trail:
418
+ S[trail_key] = S[trail_key][-max_trail:]
419
+
420
+ # Draw trails onto char/color arrays
421
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
422
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
423
+ trail_chars = list("·∘◦°⋅.,'`")
424
+
425
+ for age, positions in enumerate(reversed(S[trail_key])):
426
+ bri = fade ** age
427
+ if bri < 0.05:
428
+ break
429
+ ci = min(age, len(trail_chars) - 1)
430
+ for r, c in positions:
431
+ if 0 <= r < g.rows and 0 <= c < g.cols and ch[r, c] == " ":
432
+ ch[r, c] = trail_chars[ci]
433
+ v = int(bri * 180)
434
+ co[r, c] = (v, v, int(v * 0.8))
435
+ return ch, co
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Rain / Matrix Effects
441
+
442
+ ### Column Rain (Vectorized)
443
+ ```python
444
+ def eff_matrix_rain(g, f, t, S, hue=0.33, bri=0.6, pal=PAL_KATA,
445
+ speed_base=0.5, speed_beat=3.0):
446
+ """Vectorized matrix rain. S dict persists column positions."""
447
+ if "ry" not in S or len(S["ry"]) != g.cols:
448
+ S["ry"] = np.random.uniform(-g.rows, g.rows, g.cols).astype(np.float32)
449
+ S["rsp"] = np.random.uniform(0.3, 2.0, g.cols).astype(np.float32)
450
+ S["rln"] = np.random.randint(8, 40, g.cols)
451
+ S["rch"] = np.random.randint(0, len(pal), (g.rows, g.cols)) # pre-assign chars
452
+
453
+ speed_mult = speed_base + f.get("bass", 0.3)*speed_beat + f.get("sub_r", 0.3)*3
454
+ if f.get("beat", 0) > 0: speed_mult *= 2.5
455
+ S["ry"] += S["rsp"] * speed_mult
456
+
457
+ # Reset columns that fall past bottom
458
+ rst = (S["ry"] - S["rln"]) > g.rows
459
+ S["ry"][rst] = np.random.uniform(-25, -2, rst.sum())
460
+
461
+ # Vectorized draw using fancy indexing
462
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
463
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
464
+ heads = S["ry"].astype(int)
465
+ for c in range(g.cols):
466
+ head = heads[c]
467
+ trail_len = S["rln"][c]
468
+ for i in range(trail_len):
469
+ row = head - i
470
+ if 0 <= row < g.rows:
471
+ fade = 1.0 - i / trail_len
472
+ ci = S["rch"][row, c] % len(pal)
473
+ ch[row, c] = pal[ci]
474
+ v = fade * bri * 255
475
+ if i == 0: # head is bright white-ish
476
+ co[row, c] = (int(v*0.9), int(min(255, v*1.1)), int(v*0.9))
477
+ else:
478
+ R, G, B = tuple(int(c * 255) for c in __import__('colorsys').hsv_to_rgb(hue % 1, 0.7, np.clip(fade * bri, 0, 1)))
479
+ co[row, c] = (R, G, B)
480
+ return ch, co, S
481
+ ```
482
+
483
+ ---
484
+
485
+ ## Glitch / Data Effects
486
+
487
+ ### Horizontal Band Displacement
488
+ ```python
489
+ def eff_glitch_displace(ch, co, f, intensity=1.0):
490
+ n_bands = int(8 + f.get("flux", 0.3)*25 + f.get("bdecay", 0)*15) * intensity
491
+ for _ in range(int(n_bands)):
492
+ y = random.randint(0, ch.shape[0]-1)
493
+ h = random.randint(1, int(3 + f.get("sub", 0.3)*8))
494
+ shift = int((random.random()-0.5) * f.get("rms", 0.3)*40 + f.get("bdecay", 0)*20*(random.random()-0.5))
495
+ if shift != 0:
496
+ for row in range(h):
497
+ rr = y + row
498
+ if 0 <= rr < ch.shape[0]:
499
+ ch[rr] = np.roll(ch[rr], shift)
500
+ co[rr] = np.roll(co[rr], shift, axis=0)
501
+ return ch, co
502
+ ```
503
+
504
+ ### Block Corruption
505
+ ```python
506
+ def eff_block_corrupt(ch, co, f, char_pool=None, count_base=20):
507
+ if char_pool is None:
508
+ char_pool = list(PAL_BLOCKS[4:] + PAL_KATA[2:8])
509
+ for _ in range(int(count_base + f.get("flux", 0.3)*60 + f.get("bdecay", 0)*40)):
510
+ bx = random.randint(0, max(1, ch.shape[1]-6))
511
+ by = random.randint(0, max(1, ch.shape[0]-4))
512
+ bw, bh = random.randint(2,6), random.randint(1,4)
513
+ block_char = random.choice(char_pool)
514
+ # Fill rectangle with single char and random color
515
+ for r in range(bh):
516
+ for c in range(bw):
517
+ rr, cc = by+r, bx+c
518
+ if 0 <= rr < ch.shape[0] and 0 <= cc < ch.shape[1]:
519
+ ch[rr, cc] = block_char
520
+ co[rr, cc] = (random.randint(100,255), random.randint(0,100), random.randint(0,80))
521
+ return ch, co
522
+ ```
523
+
524
+ ### Scan Bars (Vertical)
525
+ ```python
526
+ def eff_scanbars(ch, co, f, t, n_base=4, chars="|\u2551|!1l"):
527
+ for bi in range(int(n_base + f.get("himid_r", 0.3)*12)):
528
+ sx = int((t*50*(1+bi*0.3) + bi*37) % ch.shape[1])
529
+ for rr in range(ch.shape[0]):
530
+ if random.random() < 0.7:
531
+ ch[rr, sx] = random.choice(chars)
532
+ return ch, co
533
+ ```
534
+
535
+ ### Error Messages
536
+ ```python
537
+ # Parameterize the error vocabulary per project:
538
+ ERRORS_TECH = ["SEGFAULT","0xDEADBEEF","BUFFER_OVERRUN","PANIC!","NULL_PTR",
539
+ "CORRUPT","SIGSEGV","ERR_OVERFLOW","STACK_SMASH","BAD_ALLOC"]
540
+ ERRORS_COSMIC = ["VOID_BREACH","ENTROPY_MAX","SINGULARITY","DIMENSION_FAULT",
541
+ "REALITY_ERR","TIME_PARADOX","DARK_MATTER_LEAK","QUANTUM_DECOHERE"]
542
+ ERRORS_ORGANIC = ["CELL_DIVISION_ERR","DNA_MISMATCH","MUTATION_OVERFLOW",
543
+ "NEURAL_DEADLOCK","SYNAPSE_TIMEOUT","MEMBRANE_BREACH"]
544
+ ```
545
+
546
+ ### Hex Data Stream
547
+ ```python
548
+ hex_str = "".join(random.choice("0123456789ABCDEF") for _ in range(random.randint(8,20)))
549
+ stamp(ch, co, hex_str, rand_row, rand_col, (0, 160, 80))
550
+ ```
551
+
552
+ ---
553
+
554
+ ## Spectrum / Visualization
555
+
556
+ ### Mirrored Spectrum Bars
557
+ ```python
558
+ def eff_spectrum(g, f, t, n_bars=64, pal=PAL_BLOCKS, mirror=True):
559
+ bar_w = max(1, g.cols // n_bars); mid = g.rows // 2
560
+ band_vals = np.array([f.get("sub",0.3), f.get("bass",0.3), f.get("lomid",0.3),
561
+ f.get("mid",0.3), f.get("himid",0.3), f.get("hi",0.3)])
562
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
563
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
564
+ for b in range(n_bars):
565
+ frac = b / n_bars
566
+ fi = frac * 5; lo_i = int(fi); hi_i = min(lo_i+1, 5)
567
+ bval = min(1, (band_vals[lo_i]*(1-fi%1) + band_vals[hi_i]*(fi%1)) * 1.8)
568
+ height = int(bval * (g.rows//2 - 2))
569
+ for dy in range(height):
570
+ hue = (f.get("cent",0.5)*0.3 + frac*0.3 + dy/max(height,1)*0.15) % 1.0
571
+ ci = pal[min(int(dy/max(height,1)*len(pal)*0.7+len(pal)*0.2), len(pal)-1)]
572
+ for dc in range(bar_w - (1 if bar_w > 2 else 0)):
573
+ cc = b*bar_w + dc
574
+ if 0 <= cc < g.cols:
575
+ rows_to_draw = [mid - dy, mid + dy] if mirror else [g.rows - 1 - dy]
576
+ for row in rows_to_draw:
577
+ if 0 <= row < g.rows:
578
+ ch[row, cc] = ci
579
+ co[row, cc] = tuple(int(c * 255) for c in __import__('colorsys').hsv_to_rgb(hue % 1, 0.85, np.clip(0.5+dy/max(height,1)*0.5, 0, 1)))
580
+ return ch, co
581
+ ```
582
+
583
+ ### Waveform
584
+ ```python
585
+ def eff_waveform(g, f, t, row_offset=-5, hue=0.1):
586
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
587
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
588
+ for c in range(g.cols):
589
+ wv = (math.sin(c*0.15+t*5)*f.get("bass",0.3)*0.5
590
+ + math.sin(c*0.3+t*8)*f.get("mid",0.3)*0.3
591
+ + math.sin(c*0.6+t*12)*f.get("hi",0.3)*0.15)
592
+ wr = g.rows + row_offset + int(wv * 4)
593
+ if 0 <= wr < g.rows:
594
+ ch[wr, c] = "~"
595
+ v = int(120 + f.get("rms",0.3)*135)
596
+ co[wr, c] = [v, int(v*0.7), int(v*0.4)]
597
+ return ch, co
598
+ ```
599
+
600
+ ---
601
+
602
+ ## Fire / Lava
603
+
604
+ ### Fire Columns
605
+ ```python
606
+ def eff_fire(g, f, t, n_base=20, hue_base=0.02, hue_range=0.12, pal=PAL_BLOCKS):
607
+ n_cols = int(n_base + f.get("bass",0.3)*30 + f.get("sub_r",0.3)*20)
608
+ ch = np.full((g.rows, g.cols), " ", dtype="U1")
609
+ co = np.zeros((g.rows, g.cols, 3), dtype=np.uint8)
610
+ for fi in range(n_cols):
611
+ fx_c = int((fi*g.cols/n_cols + np.sin(t*2+fi*0.7)*3) % g.cols)
612
+ height = int((f.get("bass",0.3)*0.4 + f.get("sub_r",0.3)*0.3 + f.get("rms",0.3)*0.3) * g.rows * 0.7)
613
+ for dy in range(min(height, g.rows)):
614
+ fr = g.rows - 1 - dy
615
+ frac = dy / max(height, 1)
616
+ bri = max(0.1, (1 - frac*0.6) * (0.5 + f.get("rms",0.3)*0.5))
617
+ hue = hue_base + frac * hue_range
618
+ ci = "\u2588" if frac<0.2 else ("\u2593" if frac<0.4 else ("\u2592" if frac<0.6 else "\u2591"))
619
+ ch[fr, fx_c] = ci
620
+ R, G, B = tuple(int(c * 255) for c in __import__('colorsys').hsv_to_rgb(hue % 1, 0.9, np.clip(bri, 0, 1)))
621
+ co[fr, fx_c] = (R, G, B)
622
+ return ch, co
623
+ ```
624
+
625
+ ### Ice / Cold Fire (same structure, different hue range)
626
+ ```python
627
+ # hue_base=0.55, hue_range=0.15 -- blue to cyan
628
+ # Lower intensity, slower movement
629
+ ```
630
+
631
+ ---
632
+
633
+ ## Text Overlays
634
+
635
+ ### Scrolling Ticker
636
+ ```python
637
+ def eff_ticker(ch, co, t, text, row, speed=15, color=(80, 100, 140)):
638
+ off = int(t * speed) % max(len(text), 1)
639
+ doubled = text + " " + text
640
+ stamp(ch, co, doubled[off:off+ch.shape[1]], row, 0, color)
641
+ ```
642
+
643
+ ### Beat-Triggered Words
644
+ ```python
645
+ def eff_beat_words(ch, co, f, words, row_center=None, color=(255,240,220)):
646
+ if f.get("beat", 0) > 0:
647
+ w = random.choice(words)
648
+ r = (row_center or ch.shape[0]//2) + random.randint(-5,5)
649
+ stamp(ch, co, w, r, (ch.shape[1]-len(w))//2, color)
650
+ ```
651
+
652
+ ### Fading Message Sequence
653
+ ```python
654
+ def eff_fading_messages(ch, co, t, elapsed, messages, period=4.0, color_base=(220,220,220)):
655
+ msg_idx = int(elapsed / period) % len(messages)
656
+ phase = elapsed % period
657
+ fade = max(0, min(1.0, phase) * min(1.0, period - phase))
658
+ if fade > 0.05:
659
+ v = fade
660
+ msg = messages[msg_idx]
661
+ cr, cg, cb = [int(c * v) for c in color_base]
662
+ stamp(ch, co, msg, ch.shape[0]//2, (ch.shape[1]-len(msg))//2, (cr, cg, cb))
663
+ ```
664
+
665
+ ---
666
+
667
+ ## Screen Shake
668
+ Shift entire char/color arrays on beat:
669
+ ```python
670
+ def eff_shake(ch, co, f, x_amp=6, y_amp=3):
671
+ shake_x = int(f.get("sub",0.3)*x_amp*(random.random()-0.5)*2 + f.get("bdecay",0)*4*(random.random()-0.5)*2)
672
+ shake_y = int(f.get("bass",0.3)*y_amp*(random.random()-0.5)*2)
673
+ if abs(shake_x) > 0:
674
+ ch = np.roll(ch, shake_x, axis=1)
675
+ co = np.roll(co, shake_x, axis=1)
676
+ if abs(shake_y) > 0:
677
+ ch = np.roll(ch, shake_y, axis=0)
678
+ co = np.roll(co, shake_y, axis=0)
679
+ return ch, co
680
+ ```
681
+
682
+ ---
683
+
684
+ ## Composable Effect System
685
+
686
+ The real creative power comes from **composition**. There are three levels:
687
+
688
+ ### Level 1: Character-Level Layering
689
+
690
+ Stack multiple effects as `(chars, colors)` layers:
691
+
692
+ ```python
693
+ class LayerStack(EffectNode):
694
+ """Render effects bottom-to-top with character-level compositing."""
695
+ def add(self, effect, alpha=1.0):
696
+ """alpha < 1.0 = probabilistic override (sparse overlay)."""
697
+ self.layers.append((effect, alpha))
698
+
699
+ # Usage:
700
+ stack = LayerStack()
701
+ stack.add(bg_effect) # base — fills screen
702
+ stack.add(main_effect) # overlay on top (space chars = transparent)
703
+ stack.add(particle_effect) # sparse overlay on top of that
704
+ ch, co = stack.render(g, f, t, S)
705
+ ```
706
+
707
+ ### Level 2: Pixel-Level Blending
708
+
709
+ After rendering to canvases, blend with Photoshop-style modes:
710
+
711
+ ```python
712
+ class PixelBlendStack:
713
+ """Stack canvases with blend modes for complex compositing."""
714
+ def add(self, canvas, mode="normal", opacity=1.0)
715
+ def composite(self) -> canvas
716
+
717
+ # Usage:
718
+ pbs = PixelBlendStack()
719
+ pbs.add(canvas_a) # base
720
+ pbs.add(canvas_b, "screen", 0.7) # additive glow
721
+ pbs.add(canvas_c, "difference", 0.5) # psychedelic interference
722
+ result = pbs.composite()
723
+ ```
724
+
725
+ ### Level 3: Temporal Feedback
726
+
727
+ Feed previous frame back into current frame for recursive effects:
728
+
729
+ ```python
730
+ fb = FeedbackBuffer()
731
+ for each frame:
732
+ canvas = render_current()
733
+ canvas = fb.apply(canvas, decay=0.8, blend="screen",
734
+ transform="zoom", transform_amt=0.015, hue_shift=0.02)
735
+ ```
736
+
737
+ ### Effect Nodes — Uniform Interface
738
+
739
+ In the v2 protocol, effect nodes are used **inside** scene functions. The scene function itself returns a canvas. Effect nodes produce intermediate `(chars, colors)` that are rendered to canvas via the grid's `.render()` method or `_render_vf()`.
740
+
741
+ ```python
742
+ class EffectNode:
743
+ def render(self, g, f, t, S) -> (chars, colors)
744
+
745
+ # Concrete implementations:
746
+ class ValueFieldEffect(EffectNode):
747
+ """Wraps a value field function + hue field function + palette."""
748
+ def __init__(self, val_fn, hue_fn, pal=PAL_DEFAULT, sat=0.7)
749
+
750
+ class LambdaEffect(EffectNode):
751
+ """Wrap any (g,f,t,S) -> (ch,co) function."""
752
+ def __init__(self, fn)
753
+
754
+ class ConditionalEffect(EffectNode):
755
+ """Switch effects based on audio features."""
756
+ def __init__(self, condition, if_true, if_false=None)
757
+ ```
758
+
759
+ ### Value Field Generators (Atomic Building Blocks)
760
+
761
+ These produce float32 arrays `(rows, cols)` in range [0,1]. They are the raw visual patterns. All have signature `(g, f, t, S, **params) -> float32 array`.
762
+
763
+ #### Trigonometric Fields (sine/cosine-based)
764
+
765
+ ```python
766
+ def vf_sinefield(g, f, t, S, bri=0.5,
767
+ freq=(0.13, 0.17, 0.07, 0.09), speed=(0.5, -0.4, -0.3, 0.2)):
768
+ """Layered sine field. General purpose background/texture."""
769
+ v1 = np.sin(g.cc*freq[0] + t*speed[0]) * np.sin(g.rr*freq[1] - t*speed[1]) * 0.5 + 0.5
770
+ v2 = np.sin(g.cc*freq[2] - t*speed[2] + g.rr*freq[3]) * 0.4 + 0.5
771
+ v3 = np.sin(g.dist_n*5 + t*0.2) * 0.3 + 0.4
772
+ return np.clip((v1*0.35 + v2*0.35 + v3*0.3) * bri * (0.6 + f.get("rms",0.3)*0.6), 0, 1)
773
+
774
+ def vf_smooth_noise(g, f, t, S, octaves=3, bri=0.5):
775
+ """Multi-octave sine approximation of Perlin noise."""
776
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
777
+ for i in range(octaves):
778
+ freq = 0.05 * (2 ** i); amp = 0.5 / (i + 1)
779
+ phase = t * (0.3 + i * 0.2)
780
+ val = val + np.sin(g.cc*freq + phase) * np.cos(g.rr*freq*0.7 - phase*0.5) * amp
781
+ return np.clip(val * 0.5 + 0.5, 0, 1) * bri
782
+
783
+ def vf_rings(g, f, t, S, n_base=6, spacing_base=4):
784
+ """Concentric rings, bass-driven count and wobble."""
785
+ n = int(n_base + f.get("sub_r",0.3)*25 + f.get("bass",0.3)*10)
786
+ sp = spacing_base + f.get("bass_r",0.3)*7 + f.get("rms",0.3)*3
787
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
788
+ for ri in range(n):
789
+ rad = (ri+1)*sp + f.get("bdecay",0)*15
790
+ wobble = f.get("mid_r",0.3)*5*np.sin(g.angle*3+t*4)
791
+ rd = np.abs(g.dist - rad - wobble)
792
+ th = 1 + f.get("sub",0.3)*3
793
+ val = np.maximum(val, np.clip((1 - rd/th) * (0.4 + f.get("bass",0.3)*0.8), 0, 1))
794
+ return val
795
+
796
+ def vf_spiral(g, f, t, S, n_arms=3, tightness=2.5):
797
+ """Logarithmic spiral arms."""
798
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
799
+ for ai in range(n_arms):
800
+ offset = ai * 2*np.pi / n_arms
801
+ log_r = np.log(g.dist + 1) * tightness
802
+ arm_phase = g.angle + offset - log_r + t * 0.8
803
+ arm_val = np.clip(np.cos(arm_phase * n_arms) * 0.6 + 0.2, 0, 1)
804
+ arm_val *= (0.4 + f.get("rms",0.3)*0.6) * np.clip(1 - g.dist_n*0.5, 0.2, 1)
805
+ val = np.maximum(val, arm_val)
806
+ return val
807
+
808
+ def vf_tunnel(g, f, t, S, speed=3.0, complexity=6):
809
+ """Tunnel depth effect — infinite zoom feeling."""
810
+ tunnel_d = 1.0 / (g.dist_n + 0.1)
811
+ v1 = np.sin(tunnel_d*2 - t*speed) * 0.45 + 0.55
812
+ v2 = np.sin(g.angle*complexity + tunnel_d*1.5 - t*2) * 0.35 + 0.55
813
+ return np.clip(v1*0.5 + v2*0.5, 0, 1)
814
+
815
+ def vf_vortex(g, f, t, S, twist=3.0):
816
+ """Twisting radial pattern — distance modulates angle."""
817
+ twisted = g.angle + g.dist_n * twist * np.sin(t * 0.5)
818
+ val = np.sin(twisted * 4 - t * 2) * 0.5 + 0.5
819
+ return np.clip(val * (0.5 + f.get("bass",0.3)*0.8), 0, 1)
820
+
821
+ def vf_interference(g, f, t, S, n_waves=6):
822
+ """Overlapping sine waves creating moire patterns."""
823
+ drivers = ["mid_r", "himid_r", "bass_r", "lomid_r", "hi_r", "sub_r"]
824
+ vals = np.zeros((g.rows, g.cols), dtype=np.float32)
825
+ for i in range(min(n_waves, len(drivers))):
826
+ angle = i * np.pi / n_waves
827
+ freq = 0.06 + i * 0.03; sp = 0.5 + i * 0.3
828
+ proj = g.cc * np.cos(angle) + g.rr * np.sin(angle)
829
+ vals = vals + np.sin(proj*freq + t*sp) * f.get(drivers[i], 0.3) * 2.5
830
+ return np.clip(vals * 0.12 + 0.45, 0.1, 1)
831
+
832
+ def vf_aurora(g, f, t, S, n_bands=3):
833
+ """Horizontal aurora bands."""
834
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
835
+ for i in range(n_bands):
836
+ fr = 0.08 + i*0.04; fc = 0.012 + i*0.008
837
+ sr = 0.7 + i*0.3; sc = 0.18 + i*0.12
838
+ val = val + np.sin(g.rr*fr + t*sr) * np.sin(g.cc*fc + t*sc) * (0.6/n_bands)
839
+ return np.clip(val * (f.get("lomid_r",0.3)*3 + 0.2), 0, 0.7)
840
+
841
+ def vf_ripple(g, f, t, S, sources=None, freq=0.3, damping=0.02):
842
+ """Concentric ripples from point sources."""
843
+ if sources is None: sources = [(0.5, 0.5)]
844
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
845
+ for ry, rx in sources:
846
+ dy = g.rr - g.rows*ry; dx = g.cc - g.cols*rx
847
+ d = np.sqrt(dy**2 + dx**2)
848
+ val = val + np.sin(d*freq - t*4) * np.exp(-d*damping) * 0.5
849
+ return np.clip(val + 0.5, 0, 1)
850
+
851
+ def vf_plasma(g, f, t, S):
852
+ """Classic plasma: sum of sines at different orientations and speeds."""
853
+ v = np.sin(g.cc * 0.03 + t * 0.7) * 0.5
854
+ v = v + np.sin(g.rr * 0.04 - t * 0.5) * 0.4
855
+ v = v + np.sin((g.cc * 0.02 + g.rr * 0.03) + t * 0.3) * 0.3
856
+ v = v + np.sin(g.dist_n * 4 - t * 0.8) * 0.3
857
+ return np.clip(v * 0.5 + 0.5, 0, 1)
858
+
859
+ def vf_diamond(g, f, t, S, freq=0.15):
860
+ """Diamond/checkerboard pattern."""
861
+ val = np.abs(np.sin(g.cc * freq + t * 0.5)) * np.abs(np.sin(g.rr * freq * 1.2 - t * 0.3))
862
+ return np.clip(val * (0.6 + f.get("rms",0.3)*0.8), 0, 1)
863
+
864
+ def vf_noise_static(g, f, t, S, density=0.4):
865
+ """Random noise — different each frame. Non-deterministic."""
866
+ return np.random.random((g.rows, g.cols)).astype(np.float32) * density * (0.5 + f.get("rms",0.3)*0.5)
867
+ ```
868
+
869
+ #### Noise-Based Fields (organic, non-periodic)
870
+
871
+ These produce qualitatively different textures from sine-based fields — organic, non-repeating, without visible axis alignment. They're the foundation of high-end generative art.
872
+
873
+ ```python
874
+ def _hash2d(ix, iy):
875
+ """Integer-coordinate hash for gradient noise. Returns float32 in [0,1]."""
876
+ # Good-quality hash via large prime mixing
877
+ n = ix * 374761393 + iy * 668265263
878
+ n = (n ^ (n >> 13)) * 1274126177
879
+ return ((n ^ (n >> 16)) & 0x7fffffff).astype(np.float32) / 0x7fffffff
880
+
881
+ def _smoothstep(t):
882
+ """Hermite smoothstep: 3t^2 - 2t^3. Smooth interpolation in [0,1]."""
883
+ t = np.clip(t, 0, 1)
884
+ return t * t * (3 - 2 * t)
885
+
886
+ def _smootherstep(t):
887
+ """Perlin's improved smoothstep: 6t^5 - 15t^4 + 10t^3. C2-continuous."""
888
+ t = np.clip(t, 0, 1)
889
+ return t * t * t * (t * (t * 6 - 15) + 10)
890
+
891
+ def _value_noise_2d(x, y):
892
+ """2D value noise at arbitrary float coordinates. Returns float32 in [0,1].
893
+ x, y: float32 arrays of same shape."""
894
+ ix = np.floor(x).astype(np.int64)
895
+ iy = np.floor(y).astype(np.int64)
896
+ fx = _smootherstep(x - ix)
897
+ fy = _smootherstep(y - iy)
898
+ # 4-corner hashes
899
+ n00 = _hash2d(ix, iy)
900
+ n10 = _hash2d(ix + 1, iy)
901
+ n01 = _hash2d(ix, iy + 1)
902
+ n11 = _hash2d(ix + 1, iy + 1)
903
+ # Bilinear interpolation
904
+ nx0 = n00 * (1 - fx) + n10 * fx
905
+ nx1 = n01 * (1 - fx) + n11 * fx
906
+ return nx0 * (1 - fy) + nx1 * fy
907
+
908
+ def vf_noise(g, f, t, S, freq=0.08, speed=0.3, bri=0.7):
909
+ """Value noise. Smooth, organic, no axis alignment artifacts.
910
+ freq: spatial frequency (higher = finer detail).
911
+ speed: temporal scroll rate."""
912
+ x = g.cc * freq + t * speed
913
+ y = g.rr * freq * 0.8 - t * speed * 0.4
914
+ return np.clip(_value_noise_2d(x, y) * bri, 0, 1)
915
+
916
+ def vf_fbm(g, f, t, S, octaves=5, freq=0.06, lacunarity=2.0, gain=0.5,
917
+ speed=0.2, bri=0.8):
918
+ """Fractal Brownian Motion — octaved noise with lacunarity/gain control.
919
+ The standard building block for clouds, terrain, smoke, organic textures.
920
+
921
+ octaves: number of noise layers (more = finer detail, more cost)
922
+ freq: base spatial frequency
923
+ lacunarity: frequency multiplier per octave (2.0 = standard)
924
+ gain: amplitude multiplier per octave (0.5 = standard, <0.5 = smoother)
925
+ speed: temporal evolution rate
926
+ """
927
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
928
+ amplitude = 1.0
929
+ f_x = freq
930
+ f_y = freq * 0.85 # slight anisotropy avoids grid artifacts
931
+ for i in range(octaves):
932
+ phase = t * speed * (1 + i * 0.3)
933
+ x = g.cc * f_x + phase + i * 17.3 # offset per octave
934
+ y = g.rr * f_y - phase * 0.6 + i * 31.7
935
+ val = val + _value_noise_2d(x, y) * amplitude
936
+ amplitude *= gain
937
+ f_x *= lacunarity
938
+ f_y *= lacunarity
939
+ # Normalize to [0,1]
940
+ max_amp = (1 - gain ** octaves) / (1 - gain) if gain != 1 else octaves
941
+ return np.clip(val / max_amp * bri * (0.6 + f.get("rms", 0.3) * 0.6), 0, 1)
942
+
943
+ def vf_domain_warp(g, f, t, S, base_fn=None, warp_fn=None,
944
+ warp_strength=15.0, freq=0.06, speed=0.2):
945
+ """Domain warping — feed one noise field's output as coordinate offsets
946
+ into another noise field. Produces flowing, melting organic distortion.
947
+ Signature technique of high-end generative art (Inigo Quilez).
948
+
949
+ base_fn: value field to distort (default: fbm)
950
+ warp_fn: value field for displacement (default: noise at different freq)
951
+ warp_strength: how many grid cells to displace (higher = more warped)
952
+ """
953
+ # Custom warp returns two scalar fields in [0, 1].
954
+ wx = _value_noise_2d(g.cc * freq * 1.3 + t * speed, g.rr * freq + 7.1)
955
+ wy = _value_noise_2d(g.cc * freq + t * speed * 0.7 + 3.2, g.rr * freq * 1.1 - 11.8)
956
+ if warp_fn is not None:
957
+ wx, wy = warp_fn(g, f, t, S)
958
+ # Center warp around 0 (noise returns [0,1], shift to [-0.5, 0.5])
959
+ wx = (wx - 0.5) * warp_strength * (0.5 + f.get("rms", 0.3) * 1.0)
960
+ wy = (wy - 0.5) * warp_strength * (0.5 + f.get("bass", 0.3) * 0.8)
961
+ # Sample base field at warped coordinates
962
+ warped_cc = g.cc + wx
963
+ warped_rr = g.rr + wy
964
+ if base_fn is not None:
965
+ import copy
966
+ warped = copy.copy(g)
967
+ warped.cc, warped.rr = warped_cc, warped_rr
968
+ warped.dx = warped_cc - g.cols / 2
969
+ warped.dy = (warped_rr - g.rows / 2) * getattr(g, "ch", 1) / getattr(g, "cw", 1)
970
+ warped.dist = np.sqrt(warped.dx**2 + warped.dy**2)
971
+ warped.angle = np.arctan2(warped.dy, warped.dx)
972
+ warped.dist_n = warped.dist / max(g.cols, 1)
973
+ val = base_fn(warped, f, t, S)
974
+ else:
975
+ # Default: fbm at warped coordinates
976
+ val = np.zeros((g.rows, g.cols), dtype=np.float32)
977
+ amp = 1.0
978
+ fx, fy = freq * 0.8, freq * 0.7
979
+ for i in range(4):
980
+ val = val + _value_noise_2d(warped_cc * fx + t * speed * 0.5 + i * 13.7,
981
+ warped_rr * fy - t * speed * 0.3 + i * 27.3) * amp
982
+ amp *= 0.5; fx *= 2.0; fy *= 2.0
983
+ val = val / 1.875 # normalize 4-octave sum
984
+ return np.clip(val * 0.8, 0, 1)
985
+
986
+ def vf_voronoi(g, f, t, S, n_cells=20, speed=0.3, edge_width=1.5,
987
+ mode="distance", seed=42):
988
+ """Voronoi diagram as value field. Proper implementation with
989
+ nearest/second-nearest distance for cell interiors and edges.
990
+
991
+ mode: "distance" (bright at center, dark at edges),
992
+ "edge" (bright at cell boundaries),
993
+ "cell_id" (flat color per cell — use with discrete palette)
994
+ edge_width: thickness of edge highlight (for "edge" mode)
995
+ """
996
+ rng = np.random.RandomState(seed)
997
+ # Animated cell centers
998
+ cx = rng.rand(n_cells).astype(np.float32) * g.cols
999
+ cy = rng.rand(n_cells).astype(np.float32) * g.rows
1000
+ vx = (rng.rand(n_cells).astype(np.float32) - 0.5) * speed * 10
1001
+ vy = (rng.rand(n_cells).astype(np.float32) - 0.5) * speed * 10
1002
+ cx_t = (cx + vx * np.sin(t * 0.5 + np.arange(n_cells) * 0.8)) % g.cols
1003
+ cy_t = (cy + vy * np.cos(t * 0.4 + np.arange(n_cells) * 1.1)) % g.rows
1004
+
1005
+ # Compute nearest and second-nearest distance
1006
+ d1 = np.full((g.rows, g.cols), 1e9, dtype=np.float32)
1007
+ d2 = np.full((g.rows, g.cols), 1e9, dtype=np.float32)
1008
+ id1 = np.zeros((g.rows, g.cols), dtype=np.int32)
1009
+ for i in range(n_cells):
1010
+ d = np.sqrt((g.cc - cx_t[i]) ** 2 + (g.rr - cy_t[i]) ** 2)
1011
+ mask = d < d1
1012
+ d2 = np.where(mask, d1, np.minimum(d2, d))
1013
+ id1 = np.where(mask, i, id1)
1014
+ d1 = np.minimum(d1, d)
1015
+
1016
+ if mode == "edge":
1017
+ # Edges: where d2 - d1 is small
1018
+ edge_val = np.clip(1.0 - (d2 - d1) / edge_width, 0, 1)
1019
+ return edge_val * (0.5 + f.get("rms", 0.3) * 0.8)
1020
+ elif mode == "cell_id":
1021
+ # Flat per-cell value
1022
+ return (id1.astype(np.float32) / n_cells) % 1.0
1023
+ else:
1024
+ # Distance: bright near center, dark at edges
1025
+ max_d = g.cols * 0.15
1026
+ return np.clip(1.0 - d1 / max_d, 0, 1) * (0.5 + f.get("rms", 0.3) * 0.7)
1027
+ ```
1028
+
1029
+ #### Simulation-Based Fields (emergent, evolving)
1030
+
1031
+ These use persistent state `S` to evolve patterns frame-by-frame. They produce complexity that can't be achieved with stateless math.
1032
+
1033
+ ```python
1034
+ def vf_reaction_diffusion(g, f, t, S, feed=0.055, kill=0.062,
1035
+ da=1.0, db=0.5, dt=1.0, steps_per_frame=8,
1036
+ init_mode="spots"):
1037
+ """Gray-Scott reaction-diffusion model. Produces coral, leopard spots,
1038
+ mitosis, worm-like, and labyrinthine patterns depending on feed/kill.
1039
+
1040
+ The two chemicals A and B interact:
1041
+ A + 2B → 3B (autocatalytic)
1042
+ B → P (decay)
1043
+ feed: rate A is replenished, kill: rate B decays
1044
+ Different feed/kill ratios produce radically different patterns.
1045
+
1046
+ Presets (feed, kill):
1047
+ Spots/dots: (0.055, 0.062)
1048
+ Worms/stripes: (0.046, 0.063)
1049
+ Coral/branching: (0.037, 0.060)
1050
+ Mitosis/splitting: (0.028, 0.062)
1051
+ Labyrinth/maze: (0.029, 0.057)
1052
+ Holes/negative: (0.039, 0.058)
1053
+ Chaos/unstable: (0.026, 0.051)
1054
+
1055
+ steps_per_frame: simulation steps per video frame (more = faster evolution)
1056
+ """
1057
+ key = "rd_" + str(id(g)) # unique per grid
1058
+ if key + "_a" not in S:
1059
+ # Initialize chemical fields
1060
+ A = np.ones((g.rows, g.cols), dtype=np.float32)
1061
+ B = np.zeros((g.rows, g.cols), dtype=np.float32)
1062
+ if init_mode == "spots":
1063
+ # Random seed spots
1064
+ rng = np.random.RandomState(42)
1065
+ for _ in range(max(3, g.rows * g.cols // 200)):
1066
+ r, c = rng.randint(2, g.rows - 2), rng.randint(2, g.cols - 2)
1067
+ B[r - 1:r + 2, c - 1:c + 2] = 1.0
1068
+ elif init_mode == "center":
1069
+ cr, cc = g.rows // 2, g.cols // 2
1070
+ B[cr - 3:cr + 3, cc - 3:cc + 3] = 1.0
1071
+ elif init_mode == "ring":
1072
+ mask = (g.dist_n > 0.2) & (g.dist_n < 0.3)
1073
+ B[mask] = 1.0
1074
+ S[key + "_a"] = A
1075
+ S[key + "_b"] = B
1076
+
1077
+ A = S[key + "_a"]
1078
+ B = S[key + "_b"]
1079
+
1080
+ # Audio modulation: feed/kill shift subtly with audio
1081
+ f_mod = feed + f.get("bass", 0.3) * 0.003
1082
+ k_mod = kill + f.get("hi_r", 0.3) * 0.002
1083
+
1084
+ for _ in range(steps_per_frame):
1085
+ # Laplacian via 3x3 convolution kernel
1086
+ # [0.05, 0.2, 0.05]
1087
+ # [0.2, -1.0, 0.2]
1088
+ # [0.05, 0.2, 0.05]
1089
+ pA = np.pad(A, 1, mode="wrap")
1090
+ pB = np.pad(B, 1, mode="wrap")
1091
+ lapA = (pA[:-2, 1:-1] + pA[2:, 1:-1] + pA[1:-1, :-2] + pA[1:-1, 2:]) * 0.2 \
1092
+ + (pA[:-2, :-2] + pA[:-2, 2:] + pA[2:, :-2] + pA[2:, 2:]) * 0.05 \
1093
+ - A * 1.0
1094
+ lapB = (pB[:-2, 1:-1] + pB[2:, 1:-1] + pB[1:-1, :-2] + pB[1:-1, 2:]) * 0.2 \
1095
+ + (pB[:-2, :-2] + pB[:-2, 2:] + pB[2:, :-2] + pB[2:, 2:]) * 0.05 \
1096
+ - B * 1.0
1097
+ ABB = A * B * B
1098
+ A = A + (da * lapA - ABB + f_mod * (1 - A)) * dt
1099
+ B = B + (db * lapB + ABB - (f_mod + k_mod) * B) * dt
1100
+ A = np.clip(A, 0, 1)
1101
+ B = np.clip(B, 0, 1)
1102
+
1103
+ S[key + "_a"] = A
1104
+ S[key + "_b"] = B
1105
+ # Output B chemical as value (the visible pattern)
1106
+ return np.clip(B * 2.0, 0, 1)
1107
+
1108
+ def vf_game_of_life(g, f, t, S, rule="life", birth=None, survive=None,
1109
+ steps_per_frame=1, density=0.3, fade=0.92, seed=42):
1110
+ """Cellular automaton as value field with analog fade trails.
1111
+ Grid cells are born/die by neighbor count rules. Dead cells fade
1112
+ gradually instead of snapping to black, producing ghost trails.
1113
+
1114
+ rule presets:
1115
+ "life": B3/S23 (Conway's Game of Life)
1116
+ "coral": B3/S45678 (slow crystalline growth)
1117
+ "maze": B3/S12345 (fills to labyrinth)
1118
+ "anneal": B4678/S35678 (smooth blobs)
1119
+ "day_night": B3678/S34678 (balanced growth/decay)
1120
+ Or specify birth/survive directly as sets: birth={3}, survive={2,3}
1121
+
1122
+ fade: how fast dead cells dim (0.9 = slow trails, 0.5 = fast)
1123
+ """
1124
+ presets = {
1125
+ "life": ({3}, {2, 3}),
1126
+ "coral": ({3}, {4, 5, 6, 7, 8}),
1127
+ "maze": ({3}, {1, 2, 3, 4, 5}),
1128
+ "anneal": ({4, 6, 7, 8}, {3, 5, 6, 7, 8}),
1129
+ "day_night": ({3, 6, 7, 8}, {3, 4, 6, 7, 8}),
1130
+ }
1131
+ if birth is None or survive is None:
1132
+ birth, survive = presets.get(rule, presets["life"])
1133
+
1134
+ key = "gol_" + str(id(g))
1135
+ if key + "_grid" not in S:
1136
+ rng = np.random.RandomState(seed)
1137
+ S[key + "_grid"] = (rng.random((g.rows, g.cols)) < density).astype(np.float32)
1138
+ S[key + "_display"] = S[key + "_grid"].copy()
1139
+
1140
+ grid = S[key + "_grid"]
1141
+ display = S[key + "_display"]
1142
+
1143
+ # Beat can inject random noise
1144
+ if f.get("beat", 0) > 0.5:
1145
+ inject = np.random.random((g.rows, g.cols)) < 0.02
1146
+ grid = np.clip(grid + inject.astype(np.float32), 0, 1)
1147
+
1148
+ for _ in range(steps_per_frame):
1149
+ # Count neighbors (toroidal wrap)
1150
+ padded = np.pad(grid > 0.5, 1, mode="wrap").astype(np.int8)
1151
+ neighbors = (padded[:-2, :-2] + padded[:-2, 1:-1] + padded[:-2, 2:] +
1152
+ padded[1:-1, :-2] + padded[1:-1, 2:] +
1153
+ padded[2:, :-2] + padded[2:, 1:-1] + padded[2:, 2:])
1154
+ alive = grid > 0.5
1155
+ new_alive = np.zeros_like(grid, dtype=bool)
1156
+ for b in birth:
1157
+ new_alive |= (~alive) & (neighbors == b)
1158
+ for s in survive:
1159
+ new_alive |= alive & (neighbors == s)
1160
+ grid = new_alive.astype(np.float32)
1161
+
1162
+ # Analog display: alive cells = 1.0, dead cells fade
1163
+ display = np.where(grid > 0.5, 1.0, display * fade)
1164
+ S[key + "_grid"] = grid
1165
+ S[key + "_display"] = display
1166
+ return np.clip(display, 0, 1)
1167
+
1168
+ def vf_strange_attractor(g, f, t, S, attractor="clifford",
1169
+ n_points=50000, warmup=500, bri=0.8, seed=42,
1170
+ params=None):
1171
+ """Strange attractor projected to 2D density field.
1172
+ Iterates N points through attractor equations, bins to grid,
1173
+ produces a density map. Elegant, non-repeating curves.
1174
+
1175
+ attractor presets:
1176
+ "clifford": sin(a*y) + c*cos(a*x), sin(b*x) + d*cos(b*y)
1177
+ "de_jong": sin(a*y) - cos(b*x), sin(c*x) - cos(d*y)
1178
+ "bedhead": sin(x*y/b) + cos(a*x - y), x*sin(a*y) + cos(b*x - y)
1179
+
1180
+ params: (a, b, c, d) floats — each attractor has different sweet spots.
1181
+ If None, uses time-varying defaults for animation.
1182
+ """
1183
+ key = "attr_" + attractor
1184
+ if params is None:
1185
+ # Time-varying parameters for slow morphing
1186
+ a = -1.4 + np.sin(t * 0.05) * 0.3
1187
+ b = 1.6 + np.cos(t * 0.07) * 0.2
1188
+ c = 1.0 + np.sin(t * 0.03 + 1) * 0.3
1189
+ d = 0.7 + np.cos(t * 0.04 + 2) * 0.2
1190
+ else:
1191
+ a, b, c, d = params
1192
+
1193
+ # Iterate attractor
1194
+ rng = np.random.RandomState(seed)
1195
+ x = rng.uniform(-0.1, 0.1, n_points).astype(np.float64)
1196
+ y = rng.uniform(-0.1, 0.1, n_points).astype(np.float64)
1197
+
1198
+ # Warmup iterations (reach the attractor)
1199
+ for _ in range(warmup):
1200
+ if attractor == "clifford":
1201
+ xn = np.sin(a * y) + c * np.cos(a * x)
1202
+ yn = np.sin(b * x) + d * np.cos(b * y)
1203
+ elif attractor == "de_jong":
1204
+ xn = np.sin(a * y) - np.cos(b * x)
1205
+ yn = np.sin(c * x) - np.cos(d * y)
1206
+ elif attractor == "bedhead":
1207
+ xn = np.sin(x * y / b) + np.cos(a * x - y)
1208
+ yn = x * np.sin(a * y) + np.cos(b * x - y)
1209
+ else:
1210
+ xn = np.sin(a * y) + c * np.cos(a * x)
1211
+ yn = np.sin(b * x) + d * np.cos(b * y)
1212
+ x, y = xn, yn
1213
+
1214
+ # Bin to grid
1215
+ # Find bounds
1216
+ margin = 0.1
1217
+ x_min, x_max = x.min() - margin, x.max() + margin
1218
+ y_min, y_max = y.min() - margin, y.max() + margin
1219
+
1220
+ # Map to grid coordinates
1221
+ gx = ((x - x_min) / (x_max - x_min) * (g.cols - 1)).astype(np.int32)
1222
+ gy = ((y - y_min) / (y_max - y_min) * (g.rows - 1)).astype(np.int32)
1223
+ valid = (gx >= 0) & (gx < g.cols) & (gy >= 0) & (gy < g.rows)
1224
+ gx, gy = gx[valid], gy[valid]
1225
+
1226
+ # Accumulate density
1227
+ density = np.zeros((g.rows, g.cols), dtype=np.float32)
1228
+ np.add.at(density, (gy, gx), 1.0)
1229
+
1230
+ # Log-scale density for visibility (most bins have few hits)
1231
+ density = np.log1p(density)
1232
+ mx = density.max()
1233
+ if mx > 0:
1234
+ density = density / mx
1235
+ return np.clip(density * bri * (0.5 + f.get("rms", 0.3) * 0.8), 0, 1)
1236
+ ```
1237
+
1238
+ #### SDF-Based Fields (geometric precision)
1239
+
1240
+ Signed Distance Fields produce mathematically precise shapes. Unlike sine fields (organic, blurry), SDFs give hard geometric boundaries with controllable edge softness. Combined with domain warping, they create "melting geometry" effects.
1241
+
1242
+ All SDF primitives return a **signed distance** (negative inside, positive outside). Convert to a value field with `sdf_render()`.
1243
+
1244
+ ```python
1245
+ def sdf_render(dist, edge_width=1.5, invert=False):
1246
+ """Convert signed distance to value field [0,1].
1247
+ edge_width: controls anti-aliasing / softness of the boundary.
1248
+ invert: True = bright inside shape, False = bright outside."""
1249
+ val = 1.0 - np.clip(dist / edge_width, 0, 1) if not invert else np.clip(dist / edge_width, 0, 1)
1250
+ return np.clip(val, 0, 1)
1251
+
1252
+ def sdf_glow(dist, falloff=0.05):
1253
+ """Render SDF as glowing outline — bright at boundary, fading both directions."""
1254
+ return np.clip(np.exp(-np.abs(dist) * falloff), 0, 1)
1255
+
1256
+ # --- Primitives ---
1257
+
1258
+ def sdf_circle(g, cx_frac=0.5, cy_frac=0.5, radius=0.3):
1259
+ """Circle SDF. cx/cy/radius in normalized [0,1] coordinates."""
1260
+ dx = (g.cc / g.cols - cx_frac) * (g.cols / g.rows) # aspect correction
1261
+ dy = g.rr / g.rows - cy_frac
1262
+ return np.sqrt(dx**2 + dy**2) - radius
1263
+
1264
+ def sdf_box(g, cx_frac=0.5, cy_frac=0.5, w=0.3, h=0.2, round_r=0.0):
1265
+ """Rounded rectangle SDF."""
1266
+ dx = np.abs(g.cc / g.cols - cx_frac) * (g.cols / g.rows) - w + round_r
1267
+ dy = np.abs(g.rr / g.rows - cy_frac) - h + round_r
1268
+ outside = np.sqrt(np.maximum(dx, 0)**2 + np.maximum(dy, 0)**2)
1269
+ inside = np.minimum(np.maximum(dx, dy), 0)
1270
+ return outside + inside - round_r
1271
+
1272
+ def sdf_ring(g, cx_frac=0.5, cy_frac=0.5, radius=0.3, thickness=0.03):
1273
+ """Ring (annulus) SDF."""
1274
+ d = sdf_circle(g, cx_frac, cy_frac, radius)
1275
+ return np.abs(d) - thickness
1276
+
1277
+ def sdf_line(g, x0=0.2, y0=0.5, x1=0.8, y1=0.5, thickness=0.01):
1278
+ """Line segment SDF between two points (normalized coords)."""
1279
+ ax = g.cc / g.cols * (g.cols / g.rows) - x0 * (g.cols / g.rows)
1280
+ ay = g.rr / g.rows - y0
1281
+ bx = (x1 - x0) * (g.cols / g.rows)
1282
+ by = y1 - y0
1283
+ h = np.clip((ax * bx + ay * by) / (bx * bx + by * by + 1e-10), 0, 1)
1284
+ dx = ax - bx * h
1285
+ dy = ay - by * h
1286
+ return np.sqrt(dx**2 + dy**2) - thickness
1287
+
1288
+ def sdf_triangle(g, cx=0.5, cy=0.5, size=0.25):
1289
+ """Equilateral triangle SDF centered at (cx, cy)."""
1290
+ px = (g.cc / g.cols - cx) * (g.cols / g.rows) / size
1291
+ py = (g.rr / g.rows - cy) / size
1292
+ # Equilateral triangle math
1293
+ k = np.sqrt(3.0)
1294
+ px = np.abs(px) - 1.0
1295
+ py = py + 1.0 / k
1296
+ cond = px + k * py > 0
1297
+ px2 = np.where(cond, (px - k * py) / 2.0, px)
1298
+ py2 = np.where(cond, (-k * px - py) / 2.0, py)
1299
+ px2 = np.clip(px2, -2.0, 0.0)
1300
+ return -np.sqrt(px2**2 + py2**2) * np.sign(py2) * size
1301
+
1302
+ def sdf_star(g, cx=0.5, cy=0.5, n_points=5, outer_r=0.25, inner_r=0.12):
1303
+ """Star polygon SDF — n-pointed star."""
1304
+ px = (g.cc / g.cols - cx) * (g.cols / g.rows)
1305
+ py = g.rr / g.rows - cy
1306
+ angle = np.arctan2(py, px)
1307
+ dist = np.sqrt(px**2 + py**2)
1308
+ # Modular angle for star symmetry
1309
+ wedge = 2 * np.pi / n_points
1310
+ a = np.abs((angle % wedge) - wedge / 2)
1311
+ # Interpolate radius between inner and outer
1312
+ r_at_angle = inner_r + (outer_r - inner_r) * np.clip(np.cos(a * n_points) * 0.5 + 0.5, 0, 1)
1313
+ return dist - r_at_angle
1314
+
1315
+ def sdf_heart(g, cx=0.5, cy=0.45, size=0.25):
1316
+ """Heart shape SDF."""
1317
+ px = (g.cc / g.cols - cx) * (g.cols / g.rows) / size
1318
+ py = -(g.rr / g.rows - cy) / size + 0.3 # flip y, offset
1319
+ px = np.abs(px)
1320
+ cond = (px + py) > 1.0
1321
+ d1 = np.sqrt((px - 0.25)**2 + (py - 0.75)**2) - np.sqrt(2.0) / 4.0
1322
+ d2 = np.sqrt((px + py - 1.0)**2) / np.sqrt(2.0)
1323
+ return np.where(cond, d1, d2) * size
1324
+
1325
+ # --- Combinators ---
1326
+
1327
+ def sdf_union(d1, d2):
1328
+ """Boolean union — shape is wherever either SDF is inside."""
1329
+ return np.minimum(d1, d2)
1330
+
1331
+ def sdf_intersect(d1, d2):
1332
+ """Boolean intersection — shape is where both SDFs overlap."""
1333
+ return np.maximum(d1, d2)
1334
+
1335
+ def sdf_subtract(d1, d2):
1336
+ """Boolean subtraction — d1 minus d2."""
1337
+ return np.maximum(d1, -d2)
1338
+
1339
+ def sdf_smooth_union(d1, d2, k=0.1):
1340
+ """Smooth minimum (polynomial) — blends shapes with rounded join.
1341
+ k: smoothing radius. Higher = more rounding."""
1342
+ h = np.clip(0.5 + 0.5 * (d2 - d1) / k, 0, 1)
1343
+ return d2 * (1 - h) + d1 * h - k * h * (1 - h)
1344
+
1345
+ def sdf_smooth_subtract(d1, d2, k=0.1):
1346
+ """Smooth subtraction — d1 minus d2 with rounded edge."""
1347
+ if k <= 0:
1348
+ return np.maximum(d1, -d2)
1349
+ return -sdf_smooth_union(-d1, d2, k)
1350
+
1351
+ def sdf_repeat(g, sdf_fn, spacing_x=0.25, spacing_y=0.25, **sdf_kwargs):
1352
+ """Tile an SDF primitive infinitely. spacing in normalized coords."""
1353
+ # Modular coordinates
1354
+ mod_cc = (g.cc / g.cols) % spacing_x - spacing_x / 2
1355
+ mod_rr = (g.rr / g.rows) % spacing_y - spacing_y / 2
1356
+ # Create modified grid-like arrays for the SDF
1357
+ # This is a simplified approach — build a temporary namespace
1358
+ class ModGrid:
1359
+ pass
1360
+ mg = ModGrid()
1361
+ mg.cc = mod_cc * g.cols; mg.rr = mod_rr * g.rows
1362
+ mg.cols = g.cols; mg.rows = g.rows
1363
+ return sdf_fn(mg, **sdf_kwargs)
1364
+
1365
+ # --- SDF as Value Field ---
1366
+
1367
+ def vf_sdf(g, f, t, S, sdf_fn=sdf_circle, edge_width=1.5, glow=False,
1368
+ glow_falloff=0.03, animate=True, **sdf_kwargs):
1369
+ """Wrap any SDF primitive as a standard vf_* value field.
1370
+ If animate=True, applies slow rotation and breathing to the shape."""
1371
+ if animate:
1372
+ import copy
1373
+ animated = copy.copy(g)
1374
+ animated.cc, animated.rr = uv_rotate(g, 0.1 * t)
1375
+ scale = 1 + 0.05 * np.sin(t)
1376
+ animated.cc = g.cols / 2 + (animated.cc - g.cols / 2) / scale
1377
+ animated.rr = g.rows / 2 + (animated.rr - g.rows / 2) / scale
1378
+ g = animated
1379
+ d = sdf_fn(g, **sdf_kwargs)
1380
+ if glow:
1381
+ return sdf_glow(d, glow_falloff) * (0.5 + f.get("rms", 0.3) * 0.8)
1382
+ return sdf_render(d, edge_width) * (0.5 + f.get("rms", 0.3) * 0.8)
1383
+ ```
1384
+
1385
+ ### Hue Field Generators (Color Mapping)
1386
+
1387
+ These produce float32 hue arrays [0,1]. Independently combinable with any value field. Each is a factory returning a closure with signature `(g, f, t, S) -> float32 array`. Can also be a plain float for fixed hue.
1388
+
1389
+ ```python
1390
+ def hf_fixed(hue):
1391
+ """Single hue everywhere."""
1392
+ def fn(g, f, t, S):
1393
+ return np.full((g.rows, g.cols), hue, dtype=np.float32)
1394
+ return fn
1395
+
1396
+ def hf_angle(offset=0.0):
1397
+ """Hue mapped to angle from center — rainbow wheel."""
1398
+ def fn(g, f, t, S):
1399
+ return (g.angle / (2 * np.pi) + offset + t * 0.05) % 1.0
1400
+ return fn
1401
+
1402
+ def hf_distance(base=0.5, scale=0.02):
1403
+ """Hue mapped to distance from center."""
1404
+ def fn(g, f, t, S):
1405
+ return (base + g.dist * scale + t * 0.03) % 1.0
1406
+ return fn
1407
+
1408
+ def hf_time_cycle(speed=0.1):
1409
+ """Hue cycles uniformly over time."""
1410
+ def fn(g, f, t, S):
1411
+ return np.full((g.rows, g.cols), (t * speed) % 1.0, dtype=np.float32)
1412
+ return fn
1413
+
1414
+ def hf_audio_cent():
1415
+ """Hue follows spectral centroid — timbral color shifting."""
1416
+ def fn(g, f, t, S):
1417
+ return np.full((g.rows, g.cols), f.get("cent", 0.5) * 0.3, dtype=np.float32)
1418
+ return fn
1419
+
1420
+ def hf_gradient_h(start=0.0, end=1.0):
1421
+ """Left-to-right hue gradient."""
1422
+ def fn(g, f, t, S):
1423
+ h = np.broadcast_to(
1424
+ start + (g.cc / g.cols) * (end - start),
1425
+ (g.rows, g.cols)
1426
+ ).copy() # .copy() is CRITICAL — see troubleshooting.md
1427
+ return h % 1.0
1428
+ return fn
1429
+
1430
+ def hf_gradient_v(start=0.0, end=1.0):
1431
+ """Top-to-bottom hue gradient."""
1432
+ def fn(g, f, t, S):
1433
+ h = np.broadcast_to(
1434
+ start + (g.rr / g.rows) * (end - start),
1435
+ (g.rows, g.cols)
1436
+ ).copy()
1437
+ return h % 1.0
1438
+ return fn
1439
+
1440
+ def hf_plasma(speed=0.3):
1441
+ """Plasma-style hue field — organic color variation."""
1442
+ def fn(g, f, t, S):
1443
+ return (np.sin(g.cc*0.02 + t*speed)*0.5 + np.sin(g.rr*0.015 + t*speed*0.7)*0.5) % 1.0
1444
+ return fn
1445
+ ```
1446
+
1447
+ ---
1448
+
1449
+ ## Coordinate Transforms
1450
+
1451
+ UV-space transforms applied **before** effect evaluation. Any `vf_*` function can be rotated, zoomed, tiled, or distorted by transforming the grid coordinates it sees.
1452
+
1453
+ ### Transform Helpers
1454
+
1455
+ ```python
1456
+ def uv_rotate(g, angle):
1457
+ """Rotate UV coordinates around grid center.
1458
+ Returns (rotated_cc, rotated_rr) arrays — use in place of g.cc, g.rr."""
1459
+ cx, cy = g.cols / 2.0, g.rows / 2.0
1460
+ cos_a, sin_a = np.cos(angle), np.sin(angle)
1461
+ dx = g.cc - cx
1462
+ dy = g.rr - cy
1463
+ return cx + dx * cos_a - dy * sin_a, cy + dx * sin_a + dy * cos_a
1464
+
1465
+ def uv_scale(g, sx=1.0, sy=1.0, cx_frac=0.5, cy_frac=0.5):
1466
+ """Scale UV coordinates around a center point.
1467
+ sx, sy > 1 = zoom in (fewer repeats), < 1 = zoom out (more repeats)."""
1468
+ cx = g.cols * cx_frac; cy = g.rows * cy_frac
1469
+ return cx + (g.cc - cx) / sx, cy + (g.rr - cy) / sy
1470
+
1471
+ def uv_skew(g, kx=0.0, ky=0.0):
1472
+ """Skew UV coordinates. kx shears horizontally, ky vertically."""
1473
+ return g.cc + g.rr * kx, g.rr + g.cc * ky
1474
+
1475
+ def uv_tile(g, nx=3.0, ny=3.0, mirror=False):
1476
+ """Tile UV coordinates. nx, ny = number of repeats.
1477
+ mirror=True: alternating tiles are flipped (seamless)."""
1478
+ u = (g.cc / g.cols * nx) % 1.0
1479
+ v = (g.rr / g.rows * ny) % 1.0
1480
+ if mirror:
1481
+ flip_u = ((g.cc / g.cols * nx).astype(int) % 2) == 1
1482
+ flip_v = ((g.rr / g.rows * ny).astype(int) % 2) == 1
1483
+ u = np.where(flip_u, 1.0 - u, u)
1484
+ v = np.where(flip_v, 1.0 - v, v)
1485
+ return u * g.cols, v * g.rows
1486
+
1487
+ def uv_polar(g):
1488
+ """Convert Cartesian to polar UV. Returns (angle_as_cc, dist_as_rr).
1489
+ Use to make any linear effect radial."""
1490
+ # Angle wraps [0, cols), distance wraps [0, rows)
1491
+ return g.angle / (2 * np.pi) * g.cols, g.dist_n * g.rows
1492
+
1493
+ def uv_cartesian_from_polar(g):
1494
+ """Convert polar-addressed effects back to Cartesian.
1495
+ Treats g.cc as angle and g.rr as radius."""
1496
+ angle = g.cc / g.cols * 2 * np.pi
1497
+ radius = g.rr / g.rows
1498
+ cx, cy = g.cols / 2.0, g.rows / 2.0
1499
+ return cx + radius * np.cos(angle) * cx, cy + radius * np.sin(angle) * cy
1500
+
1501
+ def uv_twist(g, amount=2.0):
1502
+ """Twist: rotation increases with distance from center. Creates spiral distortion."""
1503
+ twist_angle = g.dist_n * amount
1504
+ return uv_rotate_raw(g.cc, g.rr, g.cols / 2, g.rows / 2, twist_angle)
1505
+
1506
+ def uv_rotate_raw(cc, rr, cx, cy, angle):
1507
+ """Raw rotation on arbitrary coordinate arrays."""
1508
+ cos_a, sin_a = np.cos(angle), np.sin(angle)
1509
+ dx = cc - cx; dy = rr - cy
1510
+ return cx + dx * cos_a - dy * sin_a, cy + dx * sin_a + dy * cos_a
1511
+
1512
+ def uv_fisheye(g, strength=1.5):
1513
+ """Fisheye / barrel distortion on UV coordinates."""
1514
+ cx, cy = g.cols / 2.0, g.rows / 2.0
1515
+ dx = (g.cc - cx) / cx
1516
+ dy = (g.rr - cy) / cy
1517
+ r = np.sqrt(dx**2 + dy**2)
1518
+ r_distort = np.power(r, strength)
1519
+ scale = np.where(r > 0, r_distort / (r + 1e-10), 1.0)
1520
+ return cx + dx * scale * cx, cy + dy * scale * cy
1521
+
1522
+ def uv_wave(g, t, freq=0.1, amp=3.0, axis="x"):
1523
+ """Sinusoidal coordinate displacement. Wobbles the UV space."""
1524
+ if axis == "x":
1525
+ return g.cc + np.sin(g.rr * freq + t * 3) * amp, g.rr
1526
+ else:
1527
+ return g.cc, g.rr + np.sin(g.cc * freq + t * 3) * amp
1528
+
1529
+ def uv_mobius(g, a=1.0, b=0.0, c=0.0, d=1.0):
1530
+ """Möbius transformation (conformal map): f(z) = (az + b) / (cz + d).
1531
+ Operates on complex plane. Produces mathematically precise, visually
1532
+ striking inversions and circular transforms."""
1533
+ cx, cy = g.cols / 2.0, g.rows / 2.0
1534
+ # Map grid to complex plane [-1, 1]
1535
+ zr = (g.cc - cx) / cx
1536
+ zi = (g.rr - cy) / cy
1537
+ # Complex division: (a*z + b) / (c*z + d)
1538
+ num_r = a * zr - 0 * zi + b # imaginary parts of a,b,c,d = 0 for real params
1539
+ num_i = a * zi + 0 * zr + 0
1540
+ den_r = c * zr - 0 * zi + d
1541
+ den_i = c * zi + 0 * zr + 0
1542
+ denom = den_r**2 + den_i**2 + 1e-10
1543
+ wr = (num_r * den_r + num_i * den_i) / denom
1544
+ wi = (num_i * den_r - num_r * den_i) / denom
1545
+ return cx + wr * cx, cy + wi * cy
1546
+ ```
1547
+
1548
+ ### Using Transforms with Value Fields
1549
+
1550
+ Transforms modify what coordinates a value field sees. Wrap the transform around the `vf_*` call:
1551
+
1552
+ ```python
1553
+ # Rotate a plasma field 45 degrees
1554
+ def vf_rotated_plasma(g, f, t, S):
1555
+ rc, rr = uv_rotate(g, np.pi / 4 + t * 0.1)
1556
+ class TG: # transformed grid
1557
+ pass
1558
+ tg = TG(); tg.cc = rc; tg.rr = rr
1559
+ tg.rows = g.rows; tg.cols = g.cols
1560
+ tg.dist_n = g.dist_n; tg.angle = g.angle; tg.dist = g.dist
1561
+ return vf_plasma(tg, f, t, S)
1562
+
1563
+ # Tile a vortex 3x3 with mirror
1564
+ def vf_tiled_vortex(g, f, t, S):
1565
+ tc, tr = uv_tile(g, 3, 3, mirror=True)
1566
+ class TG:
1567
+ pass
1568
+ tg = TG(); tg.cc = tc; tg.rr = tr
1569
+ tg.rows = g.rows; tg.cols = g.cols
1570
+ tg.dist = np.sqrt((tc - g.cols/2)**2 + (tr - g.rows/2)**2)
1571
+ tg.dist_n = tg.dist / (tg.dist.max() + 1e-10)
1572
+ tg.angle = np.arctan2(tr - g.rows/2, tc - g.cols/2)
1573
+ return vf_vortex(tg, f, t, S)
1574
+
1575
+ # Helper: create transformed grid from coordinate arrays
1576
+ def make_tgrid(g, new_cc, new_rr):
1577
+ """Build a grid-like object with transformed coordinates.
1578
+ Preserves rows/cols for sizing, recomputes polar coords."""
1579
+ class TG:
1580
+ pass
1581
+ tg = TG()
1582
+ tg.cc = new_cc; tg.rr = new_rr
1583
+ tg.rows = g.rows; tg.cols = g.cols
1584
+ cx, cy = g.cols / 2.0, g.rows / 2.0
1585
+ dx = new_cc - cx; dy = new_rr - cy
1586
+ tg.dist = np.sqrt(dx**2 + dy**2)
1587
+ tg.dist_n = tg.dist / (max(cx, cy) + 1e-10)
1588
+ tg.angle = np.arctan2(dy, dx)
1589
+ tg.dx = dx; tg.dy = dy
1590
+ tg.dx_n = dx / max(g.cols, 1)
1591
+ tg.dy_n = dy / max(g.rows, 1)
1592
+ return tg
1593
+ ```
1594
+
1595
+ ---
1596
+
1597
+ ## Temporal Coherence
1598
+
1599
+ Tools for smooth, intentional parameter evolution over time. Replaces the default pattern of either static parameters or raw audio reactivity.
1600
+
1601
+ ### Easing Functions
1602
+
1603
+ Standard animation easing curves. All take `t` in [0,1] and return [0,1]:
1604
+
1605
+ ```python
1606
+ def ease_linear(t): return t
1607
+ def ease_in_quad(t): return t * t
1608
+ def ease_out_quad(t): return t * (2 - t)
1609
+ def ease_in_out_quad(t): return np.where(t < 0.5, 2*t*t, -1 + (4-2*t)*t)
1610
+ def ease_in_cubic(t): return t**3
1611
+ def ease_out_cubic(t): return (t - 1)**3 + 1
1612
+ def ease_in_out_cubic(t):
1613
+ return np.where(t < 0.5, 4*t**3, 1 - (-2*t + 2)**3 / 2)
1614
+ def ease_in_expo(t): return np.where(t == 0, 0, 2**(10*(t-1)))
1615
+ def ease_out_expo(t): return np.where(t == 1, 1, 1 - 2**(-10*t))
1616
+ def ease_elastic(t):
1617
+ """Elastic ease-out — overshoots then settles."""
1618
+ return np.where(t == 0, 0, np.where(t == 1, 1,
1619
+ 2**(-10*t) * np.sin((t*10 - 0.75) * (2*np.pi) / 3) + 1))
1620
+ def ease_bounce(t):
1621
+ """Bounce ease-out — bounces at the end."""
1622
+ t = np.asarray(t, dtype=np.float64)
1623
+ result = np.empty_like(t)
1624
+ m1 = t < 1/2.75
1625
+ m2 = (~m1) & (t < 2/2.75)
1626
+ m3 = (~m1) & (~m2) & (t < 2.5/2.75)
1627
+ m4 = ~(m1 | m2 | m3)
1628
+ result[m1] = 7.5625 * t[m1]**2
1629
+ t2 = t[m2] - 1.5/2.75; result[m2] = 7.5625 * t2**2 + 0.75
1630
+ t3 = t[m3] - 2.25/2.75; result[m3] = 7.5625 * t3**2 + 0.9375
1631
+ t4 = t[m4] - 2.625/2.75; result[m4] = 7.5625 * t4**2 + 0.984375
1632
+ return result
1633
+ ```
1634
+
1635
+ ### Keyframe Interpolation
1636
+
1637
+ Define parameter values at specific times. Interpolates between them with easing:
1638
+
1639
+ ```python
1640
+ def keyframe(t, points, ease_fn=ease_in_out_cubic, loop=False):
1641
+ """Interpolate between keyframed values.
1642
+
1643
+ Args:
1644
+ t: current time (float, seconds)
1645
+ points: list of (time, value) tuples, sorted by time
1646
+ ease_fn: easing function for interpolation
1647
+ loop: if True, wraps around after last keyframe
1648
+
1649
+ Returns:
1650
+ interpolated value at time t
1651
+
1652
+ Example:
1653
+ twist = keyframe(t, [(0, 1.0), (5, 6.0), (10, 2.0)], ease_out_cubic)
1654
+ """
1655
+ if not points:
1656
+ return 0.0
1657
+ if loop:
1658
+ period = points[-1][0] - points[0][0]
1659
+ if period > 0:
1660
+ t = points[0][0] + (t - points[0][0]) % period
1661
+
1662
+ # Clamp to range
1663
+ if t <= points[0][0]:
1664
+ return points[0][1]
1665
+ if t >= points[-1][0]:
1666
+ return points[-1][1]
1667
+
1668
+ # Find surrounding keyframes
1669
+ for i in range(len(points) - 1):
1670
+ t0, v0 = points[i]
1671
+ t1, v1 = points[i + 1]
1672
+ if t0 <= t <= t1:
1673
+ progress = (t - t0) / (t1 - t0)
1674
+ eased = ease_fn(progress)
1675
+ return v0 + (v1 - v0) * eased
1676
+
1677
+ return points[-1][1]
1678
+
1679
+ def keyframe_array(t, points, ease_fn=ease_in_out_cubic):
1680
+ """Keyframe interpolation that works with numpy arrays as values.
1681
+ points: list of (time, np.array) tuples."""
1682
+ if t <= points[0][0]: return points[0][1].copy()
1683
+ if t >= points[-1][0]: return points[-1][1].copy()
1684
+ for i in range(len(points) - 1):
1685
+ t0, v0 = points[i]
1686
+ t1, v1 = points[i + 1]
1687
+ if t0 <= t <= t1:
1688
+ progress = ease_fn((t - t0) / (t1 - t0))
1689
+ return v0 * (1 - progress) + v1 * progress
1690
+ return points[-1][1].copy()
1691
+ ```
1692
+
1693
+ ### Value Field Morphing
1694
+
1695
+ Smooth transition between two different value fields:
1696
+
1697
+ ```python
1698
+ def vf_morph(g, f, t, S, vf_a, vf_b, t_start, t_end,
1699
+ ease_fn=ease_in_out_cubic):
1700
+ """Morph between two value fields over a time range.
1701
+
1702
+ Usage:
1703
+ val = vf_morph(g, f, t, S,
1704
+ lambda g,f,t,S: vf_plasma(g,f,t,S),
1705
+ lambda g,f,t,S: vf_vortex(g,f,t,S, twist=5),
1706
+ t_start=10.0, t_end=15.0)
1707
+ """
1708
+ if t <= t_start:
1709
+ return vf_a(g, f, t, S)
1710
+ if t >= t_end:
1711
+ return vf_b(g, f, t, S)
1712
+ progress = ease_fn((t - t_start) / (t_end - t_start))
1713
+ a = vf_a(g, f, t, S)
1714
+ b = vf_b(g, f, t, S)
1715
+ return a * (1 - progress) + b * progress
1716
+
1717
+ def vf_sequence(g, f, t, S, fields, durations, crossfade=1.0,
1718
+ ease_fn=ease_in_out_cubic):
1719
+ """Cycle through a sequence of value fields with crossfades.
1720
+
1721
+ fields: list of vf_* callables
1722
+ durations: list of float seconds per field
1723
+ crossfade: seconds of overlap between adjacent fields
1724
+ """
1725
+ if not fields or len(fields) != len(durations) or any(d <= 0 for d in durations) or crossfade < 0:
1726
+ raise ValueError('Positive durations and matching fields required')
1727
+ total = sum(durations)
1728
+ t_local = t % total # loop
1729
+ elapsed = 0
1730
+ for i, dur in enumerate(durations):
1731
+ if t_local < elapsed + dur:
1732
+ # Current field
1733
+ base = fields[i](g, f, t, S)
1734
+ # Check if we're in a crossfade zone
1735
+ time_in = t_local - elapsed
1736
+ time_left = dur - time_in
1737
+ fade = min(crossfade, dur)
1738
+ if fade > 0 and time_left < fade:
1739
+ # Fading out to next
1740
+ nxt = fields[(i + 1) % len(fields)](g, f, t, S)
1741
+ blend = ease_fn(1 - time_left / fade)
1742
+ return base * (1 - blend) + nxt * blend
1743
+ return base
1744
+ elapsed += dur
1745
+ return fields[-1](g, f, t, S)
1746
+ ```
1747
+
1748
+ ### Temporal Noise
1749
+
1750
+ 3D noise sampled at `(x, y, t)` — patterns evolve smoothly in time without per-frame discontinuities:
1751
+
1752
+ ```python
1753
+ def vf_temporal_noise(g, f, t, S, freq=0.06, t_freq=0.3, octaves=4,
1754
+ bri=0.8):
1755
+ """Noise field that evolves smoothly in time. Uses 3D noise via
1756
+ two 2D noise lookups combined with temporal interpolation.
1757
+
1758
+ Unlike vf_fbm which scrolls noise (creating directional motion),
1759
+ this morphs the pattern in-place — cells brighten and dim without
1760
+ the field moving in any direction."""
1761
+ # Two noise samples at floor/ceil of temporal coordinate
1762
+ t_scaled = t * t_freq
1763
+ t_lo = np.floor(t_scaled)
1764
+ t_frac = _smootherstep(np.full((g.rows, g.cols), t_scaled - t_lo, dtype=np.float32))
1765
+
1766
+ val_lo = np.zeros((g.rows, g.cols), dtype=np.float32)
1767
+ val_hi = np.zeros((g.rows, g.cols), dtype=np.float32)
1768
+ amp = 1.0; fx = freq
1769
+ for i in range(octaves):
1770
+ val_lo = val_lo + _value_noise_2d(
1771
+ g.cc * fx + t_lo * 7.3 + i * 13, g.rr * fx + t_lo * 3.1 + i * 29) * amp
1772
+ val_hi = val_hi + _value_noise_2d(
1773
+ g.cc * fx + (t_lo + 1) * 7.3 + i * 13, g.rr * fx + (t_lo + 1) * 3.1 + i * 29) * amp
1774
+ amp *= 0.5; fx *= 2.0
1775
+ max_amp = (1 - 0.5 ** octaves) / 0.5
1776
+ val = (val_lo * (1 - t_frac) + val_hi * t_frac) / max_amp
1777
+ return np.clip(val * bri * (0.6 + f.get("rms", 0.3) * 0.6), 0, 1)
1778
+ ```
1779
+
1780
+ ---
1781
+
1782
+ ### Combining Value Fields
1783
+
1784
+ The combinatorial explosion comes from mixing value fields with math:
1785
+
1786
+ ```python
1787
+ # Multiplication = intersection (only shows where both have brightness)
1788
+ combined = vf_plasma(g,f,t,S) * vf_vortex(g,f,t,S)
1789
+
1790
+ # Addition = union (shows both, clips at 1.0)
1791
+ combined = np.clip(vf_rings(g,f,t,S) + vf_spiral(g,f,t,S), 0, 1)
1792
+
1793
+ # Interference = beat pattern (shows XOR-like patterns)
1794
+ combined = np.abs(vf_plasma(g,f,t,S) - vf_tunnel(g,f,t,S))
1795
+
1796
+ # Modulation = one effect shapes the other
1797
+ combined = vf_rings(g,f,t,S) * (0.3 + 0.7 * vf_plasma(g,f,t,S))
1798
+
1799
+ # Maximum = shows the brightest of two effects
1800
+ combined = np.maximum(vf_spiral(g,f,t,S), vf_aurora(g,f,t,S))
1801
+ ```
1802
+
1803
+ ### Full Scene Example (v2 — Canvas Return)
1804
+
1805
+ A v2 scene function composes effects internally and returns a pixel canvas:
1806
+
1807
+ ```python
1808
+ def scene_complex(r, f, t, S):
1809
+ """v2 scene function: returns canvas (uint8 H,W,3).
1810
+ r = Renderer, f = audio features, t = time, S = persistent state dict."""
1811
+ g = r.grids["md"]
1812
+ rows, cols = g.rows, g.cols
1813
+
1814
+ # 1. Value field composition
1815
+ plasma = vf_plasma(g, f, t, S)
1816
+ vortex = vf_vortex(g, f, t, S, twist=4.0)
1817
+ combined = np.clip(plasma * 0.6 + vortex * 0.5 + plasma * vortex * 0.4, 0, 1)
1818
+
1819
+ # 2. Color from hue field
1820
+ h = (hf_angle(0.3)(g,f,t,S) * 0.5 + hf_time_cycle(0.08)(g,f,t,S) * 0.5) % 1.0
1821
+
1822
+ # 3. Render to canvas via _render_vf helper
1823
+ canvas = _render_vf(r, "md", lambda g,f,t,S: combined, lambda g,f,t,S: h,
1824
+ PAL_DENSE, f, t, S, sat=0.75)
1825
+
1826
+ # 4. Optional: blend a second layer
1827
+ overlay = _render_vf(r, "sm", vf_rings, hf_fixed(0.6), PAL_BLOCKS, f, t, S)
1828
+ canvas = blend_canvas(canvas, overlay, "screen", 0.4)
1829
+
1830
+ return canvas
1831
+
1832
+ # In the render_clip() loop (handled by the framework):
1833
+ # canvas = scene_fn(r, f, t, S)
1834
+ # canvas = tonemap(canvas, gamma=scene_gamma)
1835
+ # canvas = feedback.apply(canvas, ...)
1836
+ # canvas = shader_chain.apply(canvas, f=f, t=t)
1837
+ # pipe.stdin.write(canvas.tobytes())
1838
+ ```
1839
+
1840
+ Vary the **value field combo**, **hue field**, **palette**, **blend modes**, **feedback config**, and **shader chain** per section for maximum visual variety. With 12 value fields × 8 hue fields × 14 palettes × 20 blend modes × 7 feedback transforms × 38 shaders, the combinations are effectively infinite.
1841
+
1842
+ ---
1843
+
1844
+ ## Combining Effects — Creative Guide
1845
+
1846
+ The catalog above is vocabulary. Here's how to compose it into something that looks intentional.
1847
+
1848
+ ### Layering for Depth
1849
+ Every scene should have at least two layers at different grid densities:
1850
+ - **Background** (sm or xs): dense, dim texture that prevents flat black. fBM, smooth noise, or domain warp at low brightness (bri=0.15-0.25).
1851
+ - **Content** (md): the main visual — rings, voronoi, spirals, tunnel. Full brightness.
1852
+ - **Accent** (lg or xl): sparse highlights — particles, text stencil, glow pulse. Screen-blended on top.
1853
+
1854
+ ### Interesting Effect Pairs
1855
+ | Pair | Blend | Why it works |
1856
+ |------|-------|-------------|
1857
+ | fBM + voronoi edges | `screen` | Organic fills the cells, edges add structure |
1858
+ | Domain warp + plasma | `difference` | Psychedelic organic interference |
1859
+ | Tunnel + vortex | `screen` | Depth perspective + rotational energy |
1860
+ | Spiral + interference | `exclusion` | Moire patterns from different spatial frequencies |
1861
+ | Reaction-diffusion + fire | `add` | Living organic base + dynamic foreground |
1862
+ | SDF geometry + domain warp | `screen` | Clean shapes floating in organic texture |
1863
+
1864
+ ### Effects as Masks
1865
+ Any value field can be used as a mask for another effect via `mask_from_vf()`:
1866
+ - Voronoi cells masking fire (fire visible only inside cells)
1867
+ - fBM masking a solid color layer (organic color clouds)
1868
+ - SDF shapes masking a reaction-diffusion field
1869
+ - Animated iris/wipe revealing one effect over another
1870
+
1871
+ ### Inventing New Effects
1872
+ For every project, create at least one effect that isn't in the catalog:
1873
+ - **Combine two vf_* functions** with math: `np.clip(vf_fbm(...) * vf_rings(...), 0, 1)`
1874
+ - **Apply coordinate transforms** before evaluation: `vf_plasma(twisted_grid, ...)`
1875
+ - **Use one field to modulate another's parameters**: `vf_spiral(..., tightness=2 + vf_fbm(...) * 5)`
1876
+ - **Stack time offsets**: render the same field at `t` and `t - 0.5`, difference-blend for motion trails
1877
+ - **Mirror a value field** through an SDF boundary for kaleidoscopic geometry