@vib3code/sdk 2.0.1

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,1051 @@
1
+ /**
2
+ * VIB3+ OffscreenCanvas Worker Rendering
3
+ * Moves visualization rendering to Web Workers for main-thread performance.
4
+ *
5
+ * Architecture:
6
+ * - OffscreenCanvasManager (main thread): coordinates canvas transfer, worker
7
+ * lifecycle, and parameter distribution.
8
+ * - Worker script (generated via getWorkerScript()): self-contained renderer
9
+ * that receives an OffscreenCanvas and runs the VIB3 render loop.
10
+ * - SharedParameterChannel (optional): uses SharedArrayBuffer for zero-copy
11
+ * parameter updates between main thread and workers.
12
+ *
13
+ * @module advanced/OffscreenWorker
14
+ */
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Main Thread Coordinator
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /**
21
+ * Manages OffscreenCanvas transfers to Web Workers and coordinates
22
+ * parameter updates across all active worker renderers.
23
+ */
24
+ export class OffscreenCanvasManager {
25
+ constructor() {
26
+ /**
27
+ * Active workers keyed by canvas ID.
28
+ * @type {Map<string, Worker>}
29
+ */
30
+ this.workers = new Map();
31
+
32
+ /**
33
+ * Transferred OffscreenCanvas references keyed by canvas ID.
34
+ * @type {Map<string, OffscreenCanvas>}
35
+ */
36
+ this.canvases = new Map();
37
+
38
+ /**
39
+ * Shared parameter channel (optional, for SAB path).
40
+ * @type {SharedParameterChannel|null}
41
+ */
42
+ this.sharedChannel = null;
43
+
44
+ /**
45
+ * Current parameter state, distributed to all workers.
46
+ * @type {Object}
47
+ */
48
+ this._currentParams = {};
49
+
50
+ /**
51
+ * Message handlers registered per canvas ID.
52
+ * @type {Map<string, Function>}
53
+ */
54
+ this._messageHandlers = new Map();
55
+
56
+ /**
57
+ * Worker script Blob URL (cached).
58
+ * @type {string|null}
59
+ */
60
+ this._workerBlobURL = null;
61
+
62
+ /** @type {boolean} */
63
+ this._destroyed = false;
64
+ }
65
+
66
+ // -----------------------------------------------------------------------
67
+ // Worker Renderer Creation
68
+ // -----------------------------------------------------------------------
69
+
70
+ /**
71
+ * Create a worker renderer for a given canvas element.
72
+ * Transfers the canvas to an OffscreenCanvas and sends it to a new
73
+ * Web Worker that runs the VIB3 render loop.
74
+ *
75
+ * @param {string} canvasId - DOM ID of the canvas element
76
+ * @param {string} [system='faceted'] - Initial rendering system
77
+ * @returns {Promise<Worker>} The created worker
78
+ * @throws {Error} If OffscreenCanvas is not supported or canvas not found
79
+ */
80
+ async createWorkerRenderer(canvasId, system = 'faceted') {
81
+ if (this._destroyed) {
82
+ throw new Error('OffscreenCanvasManager has been destroyed.');
83
+ }
84
+
85
+ if (typeof OffscreenCanvas === 'undefined') {
86
+ throw new Error('OffscreenCanvas is not supported in this browser.');
87
+ }
88
+
89
+ // Get canvas element
90
+ const canvasElement = document.getElementById(canvasId);
91
+ if (!canvasElement) {
92
+ throw new Error(`Canvas element with id "${canvasId}" not found.`);
93
+ }
94
+
95
+ if (!(canvasElement instanceof HTMLCanvasElement)) {
96
+ throw new Error(`Element "${canvasId}" is not a canvas element.`);
97
+ }
98
+
99
+ // Terminate existing worker for this canvas
100
+ if (this.workers.has(canvasId)) {
101
+ this._terminateWorker(canvasId);
102
+ }
103
+
104
+ // Transfer control to OffscreenCanvas
105
+ const offscreen = canvasElement.transferControlToOffscreen();
106
+ this.canvases.set(canvasId, offscreen);
107
+
108
+ // Create worker from blob URL
109
+ const worker = new Worker(this._getWorkerBlobURL(), { type: 'module' });
110
+
111
+ // Set up message handling
112
+ worker.onmessage = (event) => {
113
+ this._onWorkerMessage(canvasId, event.data);
114
+ };
115
+
116
+ worker.onerror = (error) => {
117
+ console.error(`VIB3 Worker [${canvasId}]:`, error.message);
118
+ };
119
+
120
+ this.workers.set(canvasId, worker);
121
+
122
+ // Build initialization message
123
+ const initMessage = {
124
+ type: 'init',
125
+ canvas: offscreen,
126
+ system,
127
+ width: canvasElement.width,
128
+ height: canvasElement.height,
129
+ params: { ...this._currentParams },
130
+ pixelRatio: window.devicePixelRatio || 1
131
+ };
132
+
133
+ // Optionally attach shared buffer
134
+ if (this.sharedChannel) {
135
+ initMessage.sharedBuffer = this.sharedChannel.getBuffer();
136
+ }
137
+
138
+ // Transfer the OffscreenCanvas
139
+ worker.postMessage(initMessage, [offscreen]);
140
+
141
+ return worker;
142
+ }
143
+
144
+ // -----------------------------------------------------------------------
145
+ // Parameter Updates
146
+ // -----------------------------------------------------------------------
147
+
148
+ /**
149
+ * Send parameter updates to all active workers.
150
+ *
151
+ * @param {Object} params - Key-value parameter updates
152
+ */
153
+ updateParameters(params) {
154
+ if (this._destroyed) return;
155
+
156
+ // Merge into current state
157
+ Object.assign(this._currentParams, params);
158
+
159
+ // If using shared buffer, write there (workers read continuously)
160
+ if (this.sharedChannel) {
161
+ this._writeParamsToSharedBuffer(params);
162
+ }
163
+
164
+ // Also post message (for non-SAB path or for discrete events)
165
+ const message = { type: 'params', params };
166
+ for (const [, worker] of this.workers) {
167
+ worker.postMessage(message);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Set a single parameter on all workers.
173
+ *
174
+ * @param {string} name - Parameter name
175
+ * @param {number} value - Parameter value
176
+ */
177
+ setParameter(name, value) {
178
+ this.updateParameters({ [name]: value });
179
+ }
180
+
181
+ // -----------------------------------------------------------------------
182
+ // System Switching
183
+ // -----------------------------------------------------------------------
184
+
185
+ /**
186
+ * Tell all workers to switch their rendering system.
187
+ *
188
+ * @param {string} system - 'quantum' | 'faceted' | 'holographic'
189
+ */
190
+ switchSystem(system) {
191
+ if (this._destroyed) return;
192
+
193
+ const message = { type: 'switchSystem', system };
194
+ for (const [, worker] of this.workers) {
195
+ worker.postMessage(message);
196
+ }
197
+ }
198
+
199
+ // -----------------------------------------------------------------------
200
+ // Canvas Resize
201
+ // -----------------------------------------------------------------------
202
+
203
+ /**
204
+ * Notify a worker of a canvas size change.
205
+ *
206
+ * @param {string} canvasId - Canvas ID
207
+ * @param {number} width - New width in CSS pixels
208
+ * @param {number} height - New height in CSS pixels
209
+ */
210
+ resize(canvasId, width, height) {
211
+ const worker = this.workers.get(canvasId);
212
+ if (!worker) return;
213
+
214
+ worker.postMessage({
215
+ type: 'resize',
216
+ width,
217
+ height,
218
+ pixelRatio: window.devicePixelRatio || 1
219
+ });
220
+ }
221
+
222
+ // -----------------------------------------------------------------------
223
+ // Event Forwarding
224
+ // -----------------------------------------------------------------------
225
+
226
+ /**
227
+ * Forward a mouse/touch event to a specific worker.
228
+ *
229
+ * @param {string} canvasId
230
+ * @param {string} eventType - 'mousemove', 'mousedown', 'mouseup', 'touchmove', etc.
231
+ * @param {number} x - Normalized X coordinate (0-1)
232
+ * @param {number} y - Normalized Y coordinate (0-1)
233
+ * @param {boolean} [pressed=false] - Whether a button/finger is pressed
234
+ */
235
+ forwardInput(canvasId, eventType, x, y, pressed = false) {
236
+ const worker = this.workers.get(canvasId);
237
+ if (!worker) return;
238
+
239
+ worker.postMessage({
240
+ type: 'input',
241
+ eventType,
242
+ x, y, pressed
243
+ });
244
+ }
245
+
246
+ // -----------------------------------------------------------------------
247
+ // SharedParameterChannel Integration
248
+ // -----------------------------------------------------------------------
249
+
250
+ /**
251
+ * Attach a SharedParameterChannel for zero-copy parameter transfer.
252
+ * Must be called before creating any workers.
253
+ *
254
+ * @param {SharedParameterChannel} channel
255
+ */
256
+ attachSharedChannel(channel) {
257
+ this.sharedChannel = channel;
258
+ }
259
+
260
+ /**
261
+ * Write parameter values to the shared buffer.
262
+ * @param {Object} params
263
+ * @private
264
+ */
265
+ _writeParamsToSharedBuffer(params) {
266
+ if (!this.sharedChannel) return;
267
+
268
+ const paramOrder = SharedParameterChannel.PARAM_ORDER;
269
+ for (const [name, value] of Object.entries(params)) {
270
+ const index = paramOrder.indexOf(name);
271
+ if (index >= 0) {
272
+ this.sharedChannel.setParameter(index, value);
273
+ }
274
+ }
275
+ }
276
+
277
+ // -----------------------------------------------------------------------
278
+ // Message Handling
279
+ // -----------------------------------------------------------------------
280
+
281
+ /**
282
+ * Register a message handler for a specific worker.
283
+ *
284
+ * @param {string} canvasId
285
+ * @param {Function} handler - Receives message data object
286
+ */
287
+ onWorkerMessage(canvasId, handler) {
288
+ this._messageHandlers.set(canvasId, handler);
289
+ }
290
+
291
+ /**
292
+ * Handle messages from workers.
293
+ * @param {string} canvasId
294
+ * @param {Object} data
295
+ * @private
296
+ */
297
+ _onWorkerMessage(canvasId, data) {
298
+ const handler = this._messageHandlers.get(canvasId);
299
+ if (handler) {
300
+ handler(data);
301
+ }
302
+ }
303
+
304
+ // -----------------------------------------------------------------------
305
+ // Worker Script Generation
306
+ // -----------------------------------------------------------------------
307
+
308
+ /**
309
+ * Get the Blob URL for the worker script. Cached after first creation.
310
+ * @returns {string}
311
+ * @private
312
+ */
313
+ _getWorkerBlobURL() {
314
+ if (!this._workerBlobURL) {
315
+ const source = getWorkerScript();
316
+ const blob = new Blob([source], { type: 'application/javascript' });
317
+ this._workerBlobURL = URL.createObjectURL(blob);
318
+ }
319
+ return this._workerBlobURL;
320
+ }
321
+
322
+ // -----------------------------------------------------------------------
323
+ // Cleanup
324
+ // -----------------------------------------------------------------------
325
+
326
+ /**
327
+ * Terminate a specific worker.
328
+ * @param {string} canvasId
329
+ * @private
330
+ */
331
+ _terminateWorker(canvasId) {
332
+ const worker = this.workers.get(canvasId);
333
+ if (worker) {
334
+ worker.postMessage({ type: 'destroy' });
335
+ worker.terminate();
336
+ }
337
+ this.workers.delete(canvasId);
338
+ this.canvases.delete(canvasId);
339
+ this._messageHandlers.delete(canvasId);
340
+ }
341
+
342
+ /**
343
+ * Terminate all workers and release all resources.
344
+ */
345
+ destroy() {
346
+ this._destroyed = true;
347
+
348
+ for (const canvasId of Array.from(this.workers.keys())) {
349
+ this._terminateWorker(canvasId);
350
+ }
351
+
352
+ if (this._workerBlobURL) {
353
+ URL.revokeObjectURL(this._workerBlobURL);
354
+ this._workerBlobURL = null;
355
+ }
356
+
357
+ this.sharedChannel = null;
358
+ this._currentParams = {};
359
+ }
360
+ }
361
+
362
+ // ---------------------------------------------------------------------------
363
+ // Worker Script Generator
364
+ // ---------------------------------------------------------------------------
365
+
366
+ /**
367
+ * Returns the self-contained Web Worker script source code as a string.
368
+ * The worker receives an OffscreenCanvas via postMessage and runs a
369
+ * VIB3-compatible WebGL render loop.
370
+ *
371
+ * The worker handles:
372
+ * - WebGL context creation on OffscreenCanvas
373
+ * - Parameter updates (via message or SharedArrayBuffer)
374
+ * - System switching (faceted, quantum, holographic fragment shaders)
375
+ * - Render loop with requestAnimationFrame
376
+ * - Canvas resize
377
+ * - Input forwarding (mouse/touch)
378
+ * - Graceful shutdown
379
+ *
380
+ * @returns {string} Complete worker JavaScript source code
381
+ */
382
+ export function getWorkerScript() {
383
+ return `
384
+ // =========================================================================
385
+ // VIB3+ OffscreenCanvas Worker
386
+ // Self-contained WebGL renderer for VIB3 visualizations.
387
+ // =========================================================================
388
+
389
+ 'use strict';
390
+
391
+ // -- State --
392
+ let canvas = null;
393
+ let gl = null;
394
+ let program = null;
395
+ let running = false;
396
+ let currentSystem = 'faceted';
397
+ let sharedView = null; // Float32Array on SharedArrayBuffer
398
+ let animFrameId = 0;
399
+ let startTime = performance.now() / 1000.0;
400
+
401
+ // -- Parameters --
402
+ const params = {
403
+ time: 0,
404
+ geometry: 0,
405
+ rot4dXY: 0, rot4dXZ: 0, rot4dYZ: 0,
406
+ rot4dXW: 0, rot4dYW: 0, rot4dZW: 0,
407
+ gridDensity: 20,
408
+ morphFactor: 0,
409
+ chaos: 0,
410
+ speed: 1,
411
+ hue: 180,
412
+ intensity: 0.5,
413
+ saturation: 0.7,
414
+ dimension: 4.0,
415
+ mouseX: 0.5, mouseY: 0.5, mousePressed: 0,
416
+ bass: 0, mid: 0, high: 0
417
+ };
418
+
419
+ // Parameter order for SharedArrayBuffer reading
420
+ const PARAM_ORDER = [
421
+ 'geometry', 'rot4dXY', 'rot4dXZ', 'rot4dYZ',
422
+ 'rot4dXW', 'rot4dYW', 'rot4dZW', 'gridDensity',
423
+ 'morphFactor', 'chaos', 'speed', 'hue',
424
+ 'intensity', 'saturation', 'dimension',
425
+ 'bass', 'mid', 'high', 'mouseX', 'mouseY'
426
+ ];
427
+
428
+ // -- Uniform locations cache --
429
+ let uniforms = {};
430
+
431
+ // =========================================================================
432
+ // Fragment Shader Sources
433
+ // =========================================================================
434
+
435
+ const VERTEX_SHADER = \`
436
+ attribute vec2 a_position;
437
+ void main() {
438
+ gl_Position = vec4(a_position, 0.0, 1.0);
439
+ }
440
+ \`;
441
+
442
+ // --- Shared GLSL preamble (4D rotation + projection) ---
443
+ const SHARED_GLSL = \`
444
+ precision highp float;
445
+
446
+ uniform float u_time;
447
+ uniform vec2 u_resolution;
448
+ uniform float u_geometry;
449
+ uniform float u_rot4dXY, u_rot4dXZ, u_rot4dYZ;
450
+ uniform float u_rot4dXW, u_rot4dYW, u_rot4dZW;
451
+ uniform float u_gridDensity;
452
+ uniform float u_morphFactor;
453
+ uniform float u_chaos;
454
+ uniform float u_speed;
455
+ uniform float u_hue;
456
+ uniform float u_intensity;
457
+ uniform float u_saturation;
458
+ uniform float u_dimension;
459
+ uniform float u_mouseIntensity;
460
+ uniform float u_bass, u_mid, u_high;
461
+
462
+ mat4 rotateXY(float a) {
463
+ float c = cos(a), s = sin(a);
464
+ return mat4(c,-s,0,0, s,c,0,0, 0,0,1,0, 0,0,0,1);
465
+ }
466
+ mat4 rotateXZ(float a) {
467
+ float c = cos(a), s = sin(a);
468
+ return mat4(c,0,-s,0, 0,1,0,0, s,0,c,0, 0,0,0,1);
469
+ }
470
+ mat4 rotateYZ(float a) {
471
+ float c = cos(a), s = sin(a);
472
+ return mat4(1,0,0,0, 0,c,-s,0, 0,s,c,0, 0,0,0,1);
473
+ }
474
+ mat4 rotateXW(float a) {
475
+ float c = cos(a), s = sin(a);
476
+ return mat4(c,0,0,-s, 0,1,0,0, 0,0,1,0, s,0,0,c);
477
+ }
478
+ mat4 rotateYW(float a) {
479
+ float c = cos(a), s = sin(a);
480
+ return mat4(1,0,0,0, 0,c,0,-s, 0,0,1,0, 0,s,0,c);
481
+ }
482
+ mat4 rotateZW(float a) {
483
+ float c = cos(a), s = sin(a);
484
+ return mat4(1,0,0,0, 0,1,0,0, 0,0,c,-s, 0,0,s,c);
485
+ }
486
+
487
+ mat4 fullRotation4D() {
488
+ return rotateXY(u_rot4dXY) * rotateXZ(u_rot4dXZ) * rotateYZ(u_rot4dYZ)
489
+ * rotateXW(u_rot4dXW) * rotateYW(u_rot4dYW) * rotateZW(u_rot4dZW);
490
+ }
491
+
492
+ vec3 project4Dto3D(vec4 p) {
493
+ float d = u_dimension - p.w;
494
+ return p.xyz / max(d, 0.01);
495
+ }
496
+
497
+ vec3 hsv2rgb(vec3 c) {
498
+ vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
499
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
500
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
501
+ }
502
+ \`;
503
+
504
+ // --- Faceted system fragment shader ---
505
+ const FACETED_FRAGMENT = SHARED_GLSL + \`
506
+ void main() {
507
+ vec2 uv = gl_FragCoord.xy / u_resolution;
508
+ vec2 p = (uv * 2.0 - 1.0) * vec2(u_resolution.x / u_resolution.y, 1.0);
509
+
510
+ float t = u_time * u_speed;
511
+ float geom = u_geometry;
512
+ float gi = mod(geom, 8.0);
513
+
514
+ vec4 p4 = vec4(p, 0.0, 0.0);
515
+ p4 = fullRotation4D() * p4;
516
+ vec3 proj = project4Dto3D(p4);
517
+
518
+ float density = u_gridDensity;
519
+ float pattern = 0.0;
520
+
521
+ // Geometry-dependent pattern
522
+ if (gi < 1.0) {
523
+ // Tetrahedron lattice
524
+ pattern = sin(proj.x * density + t) * sin(proj.y * density - t * 0.7);
525
+ } else if (gi < 2.0) {
526
+ // Hypercube grid
527
+ vec2 grid = fract(proj.xy * density * 0.1) - 0.5;
528
+ pattern = step(0.4, max(abs(grid.x), abs(grid.y)));
529
+ } else if (gi < 3.0) {
530
+ // Sphere
531
+ float r = length(proj.xy);
532
+ pattern = sin(r * density - t * 2.0);
533
+ } else if (gi < 4.0) {
534
+ // Torus
535
+ float r = length(proj.xy) - 0.5;
536
+ pattern = sin(atan(proj.y, proj.x) * density + t) * cos(r * density * 3.0);
537
+ } else if (gi < 5.0) {
538
+ // Klein
539
+ float twist = sin(proj.x * 3.14159 + t);
540
+ pattern = sin((proj.y + twist * 0.3) * density);
541
+ } else if (gi < 6.0) {
542
+ // Fractal
543
+ vec2 z = proj.xy * 2.0;
544
+ for (int i = 0; i < 5; i++) {
545
+ z = abs(z) * 2.0 - 1.0;
546
+ z *= mat2(cos(t*0.1), -sin(t*0.1), sin(t*0.1), cos(t*0.1));
547
+ }
548
+ pattern = length(z) * 0.5;
549
+ } else if (gi < 7.0) {
550
+ // Wave
551
+ pattern = sin(proj.x * density + t) + sin(proj.y * density * 0.7 - t * 1.3);
552
+ pattern *= 0.5;
553
+ } else {
554
+ // Crystal
555
+ float hex = abs(proj.x) + abs(proj.y) + abs(proj.x + proj.y);
556
+ pattern = sin(hex * density * 0.5 - t);
557
+ }
558
+
559
+ // Morph and chaos
560
+ pattern += sin(length(proj.xy) * 10.0 - t * 3.0) * u_morphFactor * 0.3;
561
+ pattern += (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453) - 0.5) * u_chaos;
562
+
563
+ // Color
564
+ float h = u_hue / 360.0 + pattern * 0.1;
565
+ vec3 color = hsv2rgb(vec3(h, u_saturation, u_intensity * (0.5 + pattern * 0.5)));
566
+
567
+ // Audio reactivity
568
+ color += vec3(u_bass * 0.2, u_mid * 0.15, u_high * 0.1) * pattern;
569
+
570
+ gl_FragColor = vec4(color, 1.0);
571
+ }
572
+ \`;
573
+
574
+ // --- Quantum system fragment shader ---
575
+ const QUANTUM_FRAGMENT = SHARED_GLSL + \`
576
+ void main() {
577
+ vec2 uv = gl_FragCoord.xy / u_resolution;
578
+ vec2 p = (uv * 2.0 - 1.0) * vec2(u_resolution.x / u_resolution.y, 1.0);
579
+
580
+ float t = u_time * u_speed;
581
+ float gi = mod(u_geometry, 8.0);
582
+
583
+ vec4 p4 = vec4(p, sin(t * 0.3) * 0.5, cos(t * 0.2) * 0.5);
584
+ p4 = fullRotation4D() * p4;
585
+ vec3 proj = project4Dto3D(p4);
586
+
587
+ // Quantum lattice pattern
588
+ float density = u_gridDensity;
589
+ float wave1 = sin(proj.x * density + t) * cos(proj.y * density * 0.8 - t * 0.6);
590
+ float wave2 = cos(length(proj.xy) * density * 0.5 - t * 1.5);
591
+ float interference = wave1 * wave2;
592
+
593
+ // Geometry variation
594
+ float geomMod = 0.0;
595
+ if (gi < 2.0) {
596
+ geomMod = sin(proj.x * proj.y * density);
597
+ } else if (gi < 4.0) {
598
+ geomMod = sin(length(proj) * density * 2.0);
599
+ } else if (gi < 6.0) {
600
+ geomMod = sin(atan(proj.y, proj.x) * floor(gi + 3.0));
601
+ } else {
602
+ geomMod = sin(abs(proj.x) * density) * sin(abs(proj.y) * density);
603
+ }
604
+
605
+ float pattern = interference + geomMod * 0.3;
606
+ pattern += u_morphFactor * sin(pattern * 6.28) * 0.4;
607
+
608
+ // Quantum probability color mapping
609
+ float probability = abs(pattern);
610
+ float phase = atan(wave1, wave2) / 6.28318 + 0.5;
611
+
612
+ float h = u_hue / 360.0 + phase * 0.3;
613
+ float s = u_saturation * (0.5 + probability * 0.5);
614
+ float v = u_intensity * (0.3 + probability * 0.7);
615
+
616
+ vec3 color = hsv2rgb(vec3(h, s, v));
617
+
618
+ // Chaos noise
619
+ float noise = fract(sin(dot(uv + t * 0.01, vec2(12.9898, 78.233))) * 43758.5453);
620
+ color += (noise - 0.5) * u_chaos * 0.3;
621
+
622
+ // Audio
623
+ color.r += u_bass * probability * 0.3;
624
+ color.g += u_mid * (1.0 - probability) * 0.2;
625
+ color.b += u_high * phase * 0.25;
626
+
627
+ gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);
628
+ }
629
+ \`;
630
+
631
+ // --- Holographic system fragment shader ---
632
+ const HOLOGRAPHIC_FRAGMENT = SHARED_GLSL + \`
633
+ void main() {
634
+ vec2 uv = gl_FragCoord.xy / u_resolution;
635
+ vec2 p = (uv * 2.0 - 1.0) * vec2(u_resolution.x / u_resolution.y, 1.0);
636
+
637
+ float t = u_time * u_speed;
638
+ float gi = mod(u_geometry, 8.0);
639
+
640
+ // 5 virtual layers composited
641
+ vec3 totalColor = vec3(0.0);
642
+ float totalAlpha = 0.0;
643
+
644
+ for (int layer = 0; layer < 5; layer++) {
645
+ float layerF = float(layer);
646
+ float layerOffset = layerF * 0.1;
647
+ float layerScale = 1.0 + layerF * 0.15;
648
+ float layerOpacity = 1.0 - layerF * 0.15;
649
+
650
+ vec4 p4 = vec4(p * layerScale, layerOffset, sin(t * 0.1 + layerF));
651
+ p4 = fullRotation4D() * p4;
652
+ vec3 proj = project4Dto3D(p4);
653
+
654
+ float density = u_gridDensity * (1.0 + layerF * 0.2);
655
+ float pattern = sin(proj.x * density + t + layerF)
656
+ * cos(proj.y * density * 0.9 - t * 0.7 + layerF * 0.5);
657
+
658
+ pattern += sin(length(proj.xy) * density * 0.3 - t * 2.0) * 0.5;
659
+ pattern *= (1.0 + u_morphFactor * sin(pattern * 3.14159));
660
+
661
+ // Layer-specific hue shift
662
+ float h = u_hue / 360.0 + layerF * 0.08 + pattern * 0.05;
663
+ float s = u_saturation * (0.6 + layerF * 0.08);
664
+ float v = u_intensity * (0.2 + abs(pattern) * 0.6) * layerOpacity;
665
+
666
+ vec3 layerColor = hsv2rgb(vec3(h, s, v));
667
+
668
+ // Glassmorphic blend
669
+ float layerAlpha = (0.3 + abs(pattern) * 0.4) * layerOpacity;
670
+ totalColor = mix(totalColor, layerColor, layerAlpha);
671
+ totalAlpha = max(totalAlpha, layerAlpha);
672
+ }
673
+
674
+ // Holographic iridescence
675
+ float iridescence = sin(p.x * 20.0 + t) * sin(p.y * 20.0 - t * 0.8) * 0.1;
676
+ totalColor += vec3(iridescence, iridescence * 0.7, iridescence * 1.3);
677
+
678
+ // Audio glow
679
+ totalColor += vec3(u_bass, u_mid, u_high) * 0.15;
680
+
681
+ // Chaos
682
+ float noise = fract(sin(dot(uv + t * 0.005, vec2(12.9898, 78.233))) * 43758.5453);
683
+ totalColor += (noise - 0.5) * u_chaos * 0.15;
684
+
685
+ gl_FragColor = vec4(clamp(totalColor, 0.0, 1.0), 1.0);
686
+ }
687
+ \`;
688
+
689
+ // =========================================================================
690
+ // Shader map by system name
691
+ // =========================================================================
692
+
693
+ const SYSTEM_SHADERS = {
694
+ faceted: FACETED_FRAGMENT,
695
+ quantum: QUANTUM_FRAGMENT,
696
+ holographic: HOLOGRAPHIC_FRAGMENT
697
+ };
698
+
699
+ // =========================================================================
700
+ // WebGL Initialization
701
+ // =========================================================================
702
+
703
+ function initGL(offscreen, width, height, pixelRatio) {
704
+ canvas = offscreen;
705
+ canvas.width = width * pixelRatio;
706
+ canvas.height = height * pixelRatio;
707
+
708
+ gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
709
+ if (!gl) {
710
+ throw new Error('WebGL not available on OffscreenCanvas.');
711
+ }
712
+
713
+ buildProgram(currentSystem);
714
+ }
715
+
716
+ function buildProgram(system) {
717
+ if (program) {
718
+ gl.deleteProgram(program);
719
+ program = null;
720
+ }
721
+
722
+ const fragSource = SYSTEM_SHADERS[system] || SYSTEM_SHADERS.faceted;
723
+
724
+ const vs = compileShader(gl.VERTEX_SHADER, VERTEX_SHADER);
725
+ const fs = compileShader(gl.FRAGMENT_SHADER, fragSource);
726
+ if (!vs || !fs) return;
727
+
728
+ program = gl.createProgram();
729
+ gl.attachShader(program, vs);
730
+ gl.attachShader(program, fs);
731
+ gl.linkProgram(program);
732
+
733
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
734
+ console.error('Shader link error:', gl.getProgramInfoLog(program));
735
+ gl.deleteProgram(program);
736
+ program = null;
737
+ return;
738
+ }
739
+
740
+ gl.deleteShader(vs);
741
+ gl.deleteShader(fs);
742
+
743
+ // Full-screen quad
744
+ const quadVerts = new Float32Array([-1,-1, 1,-1, -1,1, 1,1]);
745
+ const vbo = gl.createBuffer();
746
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
747
+ gl.bufferData(gl.ARRAY_BUFFER, quadVerts, gl.STATIC_DRAW);
748
+
749
+ const posLoc = gl.getAttribLocation(program, 'a_position');
750
+ gl.enableVertexAttribArray(posLoc);
751
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
752
+
753
+ // Cache uniform locations
754
+ uniforms = {};
755
+ const uniformNames = [
756
+ 'u_time', 'u_resolution', 'u_geometry',
757
+ 'u_rot4dXY', 'u_rot4dXZ', 'u_rot4dYZ',
758
+ 'u_rot4dXW', 'u_rot4dYW', 'u_rot4dZW',
759
+ 'u_gridDensity', 'u_morphFactor', 'u_chaos',
760
+ 'u_speed', 'u_hue', 'u_intensity', 'u_saturation',
761
+ 'u_dimension', 'u_mouseIntensity',
762
+ 'u_bass', 'u_mid', 'u_high'
763
+ ];
764
+ for (const name of uniformNames) {
765
+ uniforms[name] = gl.getUniformLocation(program, name);
766
+ }
767
+ }
768
+
769
+ function compileShader(type, source) {
770
+ const shader = gl.createShader(type);
771
+ gl.shaderSource(shader, source);
772
+ gl.compileShader(shader);
773
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
774
+ console.error('Shader compile error:', gl.getShaderInfoLog(shader));
775
+ gl.deleteShader(shader);
776
+ return null;
777
+ }
778
+ return shader;
779
+ }
780
+
781
+ // =========================================================================
782
+ // Render Loop
783
+ // =========================================================================
784
+
785
+ function render() {
786
+ if (!running || !gl || !program) return;
787
+
788
+ // Read shared buffer if available
789
+ if (sharedView) {
790
+ for (let i = 0; i < PARAM_ORDER.length && i < sharedView.length; i++) {
791
+ params[PARAM_ORDER[i]] = sharedView[i];
792
+ }
793
+ }
794
+
795
+ params.time = (performance.now() / 1000.0) - startTime;
796
+
797
+ gl.viewport(0, 0, canvas.width, canvas.height);
798
+ gl.clearColor(0, 0, 0, 1);
799
+ gl.clear(gl.COLOR_BUFFER_BIT);
800
+
801
+ gl.useProgram(program);
802
+
803
+ // Set uniforms
804
+ if (uniforms.u_time != null) gl.uniform1f(uniforms.u_time, params.time);
805
+ if (uniforms.u_resolution != null) gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);
806
+ if (uniforms.u_geometry != null) gl.uniform1f(uniforms.u_geometry, params.geometry);
807
+
808
+ if (uniforms.u_rot4dXY != null) gl.uniform1f(uniforms.u_rot4dXY, params.rot4dXY);
809
+ if (uniforms.u_rot4dXZ != null) gl.uniform1f(uniforms.u_rot4dXZ, params.rot4dXZ);
810
+ if (uniforms.u_rot4dYZ != null) gl.uniform1f(uniforms.u_rot4dYZ, params.rot4dYZ);
811
+ if (uniforms.u_rot4dXW != null) gl.uniform1f(uniforms.u_rot4dXW, params.rot4dXW);
812
+ if (uniforms.u_rot4dYW != null) gl.uniform1f(uniforms.u_rot4dYW, params.rot4dYW);
813
+ if (uniforms.u_rot4dZW != null) gl.uniform1f(uniforms.u_rot4dZW, params.rot4dZW);
814
+
815
+ if (uniforms.u_gridDensity != null) gl.uniform1f(uniforms.u_gridDensity, params.gridDensity);
816
+ if (uniforms.u_morphFactor != null) gl.uniform1f(uniforms.u_morphFactor, params.morphFactor);
817
+ if (uniforms.u_chaos != null) gl.uniform1f(uniforms.u_chaos, params.chaos);
818
+ if (uniforms.u_speed != null) gl.uniform1f(uniforms.u_speed, params.speed);
819
+ if (uniforms.u_hue != null) gl.uniform1f(uniforms.u_hue, params.hue);
820
+ if (uniforms.u_intensity != null) gl.uniform1f(uniforms.u_intensity, params.intensity);
821
+ if (uniforms.u_saturation != null) gl.uniform1f(uniforms.u_saturation, params.saturation);
822
+ if (uniforms.u_dimension != null) gl.uniform1f(uniforms.u_dimension, params.dimension);
823
+ if (uniforms.u_mouseIntensity != null) gl.uniform1f(uniforms.u_mouseIntensity, params.mousePressed ? 1.0 : 0.0);
824
+ if (uniforms.u_bass != null) gl.uniform1f(uniforms.u_bass, params.bass);
825
+ if (uniforms.u_mid != null) gl.uniform1f(uniforms.u_mid, params.mid);
826
+ if (uniforms.u_high != null) gl.uniform1f(uniforms.u_high, params.high);
827
+
828
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
829
+
830
+ animFrameId = requestAnimationFrame(render);
831
+ }
832
+
833
+ // =========================================================================
834
+ // Message Handler
835
+ // =========================================================================
836
+
837
+ self.onmessage = function(event) {
838
+ const msg = event.data;
839
+
840
+ switch (msg.type) {
841
+ case 'init':
842
+ currentSystem = msg.system || 'faceted';
843
+ Object.assign(params, msg.params || {});
844
+
845
+ if (msg.sharedBuffer) {
846
+ sharedView = new Float32Array(msg.sharedBuffer);
847
+ }
848
+
849
+ initGL(msg.canvas, msg.width, msg.height, msg.pixelRatio || 1);
850
+ running = true;
851
+ startTime = performance.now() / 1000.0;
852
+ render();
853
+
854
+ self.postMessage({ type: 'ready', system: currentSystem });
855
+ break;
856
+
857
+ case 'params':
858
+ Object.assign(params, msg.params);
859
+ break;
860
+
861
+ case 'switchSystem':
862
+ currentSystem = msg.system || 'faceted';
863
+ if (gl) {
864
+ buildProgram(currentSystem);
865
+ }
866
+ self.postMessage({ type: 'systemChanged', system: currentSystem });
867
+ break;
868
+
869
+ case 'resize':
870
+ if (canvas) {
871
+ const pr = msg.pixelRatio || 1;
872
+ canvas.width = msg.width * pr;
873
+ canvas.height = msg.height * pr;
874
+ }
875
+ break;
876
+
877
+ case 'input':
878
+ if (msg.eventType === 'mousemove' || msg.eventType === 'touchmove') {
879
+ params.mouseX = msg.x;
880
+ params.mouseY = msg.y;
881
+ }
882
+ if (msg.eventType === 'mousedown' || msg.eventType === 'touchstart') {
883
+ params.mousePressed = 1;
884
+ }
885
+ if (msg.eventType === 'mouseup' || msg.eventType === 'touchend') {
886
+ params.mousePressed = 0;
887
+ }
888
+ break;
889
+
890
+ case 'destroy':
891
+ running = false;
892
+ if (animFrameId) {
893
+ cancelAnimationFrame(animFrameId);
894
+ }
895
+ if (gl && program) {
896
+ gl.deleteProgram(program);
897
+ }
898
+ gl = null;
899
+ canvas = null;
900
+ program = null;
901
+ self.postMessage({ type: 'destroyed' });
902
+ break;
903
+
904
+ default:
905
+ break;
906
+ }
907
+ };
908
+
909
+ self.postMessage({ type: 'loaded' });
910
+ `;
911
+ }
912
+
913
+ // ---------------------------------------------------------------------------
914
+ // SharedArrayBuffer-based Parameter Channel
915
+ // ---------------------------------------------------------------------------
916
+
917
+ /**
918
+ * Zero-copy parameter channel using SharedArrayBuffer.
919
+ * Allows the main thread to write parameter values that workers read
920
+ * directly from shared memory without message-passing overhead.
921
+ *
922
+ * Requirements:
923
+ * - Page served with cross-origin isolation headers:
924
+ * Cross-Origin-Opener-Policy: same-origin
925
+ * Cross-Origin-Embedder-Policy: require-corp
926
+ */
927
+ export class SharedParameterChannel {
928
+ /**
929
+ * Standard parameter order for indexing into the shared buffer.
930
+ * @type {string[]}
931
+ */
932
+ static PARAM_ORDER = [
933
+ 'geometry', 'rot4dXY', 'rot4dXZ', 'rot4dYZ',
934
+ 'rot4dXW', 'rot4dYW', 'rot4dZW', 'gridDensity',
935
+ 'morphFactor', 'chaos', 'speed', 'hue',
936
+ 'intensity', 'saturation', 'dimension',
937
+ 'bass', 'mid', 'high', 'mouseX', 'mouseY'
938
+ ];
939
+
940
+ /**
941
+ * @param {number} [paramCount=20] - Number of float32 parameter slots
942
+ */
943
+ constructor(paramCount = 20) {
944
+ if (typeof SharedArrayBuffer === 'undefined') {
945
+ throw new Error(
946
+ 'SharedArrayBuffer is not available. '
947
+ + 'Ensure the page has cross-origin isolation headers.'
948
+ );
949
+ }
950
+
951
+ /** @type {number} */
952
+ this.paramCount = paramCount;
953
+
954
+ /** @type {SharedArrayBuffer} */
955
+ this.buffer = new SharedArrayBuffer(paramCount * 4); // Float32
956
+
957
+ /** @type {Float32Array} */
958
+ this.view = new Float32Array(this.buffer);
959
+ }
960
+
961
+ /**
962
+ * Set a parameter value by index.
963
+ * @param {number} index - Parameter index (see PARAM_ORDER)
964
+ * @param {number} value - Parameter value
965
+ */
966
+ setParameter(index, value) {
967
+ if (index >= 0 && index < this.paramCount) {
968
+ Atomics.store(
969
+ new Int32Array(this.buffer),
970
+ index,
971
+ this._floatToInt(value)
972
+ );
973
+ }
974
+ }
975
+
976
+ /**
977
+ * Get a parameter value by index.
978
+ * @param {number} index
979
+ * @returns {number}
980
+ */
981
+ getParameter(index) {
982
+ if (index >= 0 && index < this.paramCount) {
983
+ return this.view[index];
984
+ }
985
+ return 0;
986
+ }
987
+
988
+ /**
989
+ * Set a parameter by name.
990
+ * @param {string} name - Parameter name
991
+ * @param {number} value
992
+ */
993
+ setByName(name, value) {
994
+ const index = SharedParameterChannel.PARAM_ORDER.indexOf(name);
995
+ if (index >= 0) {
996
+ this.setParameter(index, value);
997
+ }
998
+ }
999
+
1000
+ /**
1001
+ * Get a parameter by name.
1002
+ * @param {string} name
1003
+ * @returns {number}
1004
+ */
1005
+ getByName(name) {
1006
+ const index = SharedParameterChannel.PARAM_ORDER.indexOf(name);
1007
+ return index >= 0 ? this.getParameter(index) : 0;
1008
+ }
1009
+
1010
+ /**
1011
+ * Bulk-set parameters from an object.
1012
+ * @param {Object} params - Key-value parameter pairs
1013
+ */
1014
+ setAll(params) {
1015
+ for (const [name, value] of Object.entries(params)) {
1016
+ this.setByName(name, value);
1017
+ }
1018
+ }
1019
+
1020
+ /**
1021
+ * Get all parameters as an object.
1022
+ * @returns {Object}
1023
+ */
1024
+ getAll() {
1025
+ const result = {};
1026
+ for (let i = 0; i < SharedParameterChannel.PARAM_ORDER.length; i++) {
1027
+ result[SharedParameterChannel.PARAM_ORDER[i]] = this.view[i];
1028
+ }
1029
+ return result;
1030
+ }
1031
+
1032
+ /**
1033
+ * Get the underlying SharedArrayBuffer for transfer to a worker.
1034
+ * @returns {SharedArrayBuffer}
1035
+ */
1036
+ getBuffer() {
1037
+ return this.buffer;
1038
+ }
1039
+
1040
+ /**
1041
+ * Convert float to int bits for Atomics.store compatibility.
1042
+ * @param {number} f
1043
+ * @returns {number}
1044
+ * @private
1045
+ */
1046
+ _floatToInt(f) {
1047
+ const buf = new ArrayBuffer(4);
1048
+ new Float32Array(buf)[0] = f;
1049
+ return new Int32Array(buf)[0];
1050
+ }
1051
+ }