@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.
- package/CHANGELOG.md +118 -0
- package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
- package/DOCS/CI_TESTING.md +38 -0
- package/DOCS/CLI_ONBOARDING.md +75 -0
- package/DOCS/CONTROL_REFERENCE.md +64 -0
- package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
- package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
- package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
- package/DOCS/ENV_SETUP.md +189 -0
- package/DOCS/EXPORT_FORMATS.md +417 -0
- package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
- package/DOCS/LICENSING_TIERS.md +275 -0
- package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
- package/DOCS/OBS_SETUP_GUIDE.md +98 -0
- package/DOCS/PROJECT_SETUP.md +66 -0
- package/DOCS/RENDERER_LIFECYCLE.md +40 -0
- package/DOCS/REPO_MANIFEST.md +121 -0
- package/DOCS/SESSION_014_PLAN.md +195 -0
- package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
- package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
- package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
- package/DOCS/SYSTEM_INVENTORY.md +520 -0
- package/DOCS/TELEMETRY_EXPORTS.md +34 -0
- package/DOCS/WEBGPU_STATUS.md +38 -0
- package/DOCS/XR_BENCHMARKS.md +608 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/docs/.nojekyll +0 -0
- package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/02-hyperspatial_ego_death.html +346 -0
- package/docs/03-post_cartesian_sublime.html +346 -0
- package/docs/04-crystalline_void_meditation.html +346 -0
- package/docs/05-quantum_decoherence_ballet.html +346 -0
- package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/07-hyperspatial_ego_death.html +346 -0
- package/docs/08-post_cartesian_sublime.html +346 -0
- package/docs/09-crystalline_void_meditation.html +346 -0
- package/docs/10-quantum_decoherence_ballet.html +346 -0
- package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/12-hyperspatial_ego_death.html +346 -0
- package/docs/13-post_cartesian_sublime.html +346 -0
- package/docs/index.html +794 -0
- package/docs/test-hub.html +441 -0
- package/docs/url-state.js +102 -0
- package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
- package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
- package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
- package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
- package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
- package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
- package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
- package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
- package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
- package/docs/vib3-exports/index.html +238 -0
- package/docs/webgpu-live.html +702 -0
- package/package.json +367 -0
- package/src/advanced/AIPresetGenerator.js +777 -0
- package/src/advanced/MIDIController.js +703 -0
- package/src/advanced/OffscreenWorker.js +1051 -0
- package/src/advanced/WebGPUCompute.js +1051 -0
- package/src/advanced/WebXRRenderer.js +680 -0
- package/src/agent/cli/AgentCLI.js +615 -0
- package/src/agent/cli/index.js +14 -0
- package/src/agent/index.js +73 -0
- package/src/agent/mcp/MCPServer.js +950 -0
- package/src/agent/mcp/index.js +9 -0
- package/src/agent/mcp/tools.js +548 -0
- package/src/agent/telemetry/EventStream.js +669 -0
- package/src/agent/telemetry/Instrumentation.js +618 -0
- package/src/agent/telemetry/TelemetryExporters.js +427 -0
- package/src/agent/telemetry/TelemetryService.js +464 -0
- package/src/agent/telemetry/index.js +52 -0
- package/src/benchmarks/BenchmarkRunner.js +381 -0
- package/src/benchmarks/MetricsCollector.js +299 -0
- package/src/benchmarks/index.js +9 -0
- package/src/benchmarks/scenes.js +259 -0
- package/src/cli/index.js +675 -0
- package/src/config/ApiConfig.js +88 -0
- package/src/core/CanvasManager.js +217 -0
- package/src/core/ErrorReporter.js +117 -0
- package/src/core/ParameterMapper.js +333 -0
- package/src/core/Parameters.js +396 -0
- package/src/core/RendererContracts.js +200 -0
- package/src/core/UnifiedResourceManager.js +370 -0
- package/src/core/VIB3Engine.js +636 -0
- package/src/core/renderers/FacetedRendererAdapter.js +32 -0
- package/src/core/renderers/HolographicRendererAdapter.js +29 -0
- package/src/core/renderers/QuantumRendererAdapter.js +29 -0
- package/src/core/renderers/RendererLifecycleManager.js +63 -0
- package/src/creative/ColorPresetsSystem.js +980 -0
- package/src/creative/ParameterTimeline.js +1061 -0
- package/src/creative/PostProcessingPipeline.js +1113 -0
- package/src/creative/TransitionAnimator.js +683 -0
- package/src/export/CSSExporter.js +226 -0
- package/src/export/CardGeneratorBase.js +279 -0
- package/src/export/ExportManager.js +580 -0
- package/src/export/FacetedCardGenerator.js +279 -0
- package/src/export/HolographicCardGenerator.js +543 -0
- package/src/export/LottieExporter.js +552 -0
- package/src/export/QuantumCardGenerator.js +315 -0
- package/src/export/SVGExporter.js +519 -0
- package/src/export/ShaderExporter.js +903 -0
- package/src/export/TradingCardGenerator.js +3055 -0
- package/src/export/TradingCardManager.js +181 -0
- package/src/export/VIB3PackageExporter.js +559 -0
- package/src/export/index.js +14 -0
- package/src/export/systems/TradingCardSystemFaceted.js +494 -0
- package/src/export/systems/TradingCardSystemHolographic.js +452 -0
- package/src/export/systems/TradingCardSystemQuantum.js +411 -0
- package/src/faceted/FacetedSystem.js +963 -0
- package/src/features/CollectionManager.js +433 -0
- package/src/gallery/CollectionManager.js +240 -0
- package/src/gallery/GallerySystem.js +485 -0
- package/src/geometry/GeometryFactory.js +314 -0
- package/src/geometry/GeometryLibrary.js +72 -0
- package/src/geometry/buffers/BufferBuilder.js +338 -0
- package/src/geometry/buffers/index.js +18 -0
- package/src/geometry/generators/Crystal.js +420 -0
- package/src/geometry/generators/Fractal.js +298 -0
- package/src/geometry/generators/KleinBottle.js +197 -0
- package/src/geometry/generators/Sphere.js +192 -0
- package/src/geometry/generators/Tesseract.js +160 -0
- package/src/geometry/generators/Tetrahedron.js +225 -0
- package/src/geometry/generators/Torus.js +304 -0
- package/src/geometry/generators/Wave.js +341 -0
- package/src/geometry/index.js +142 -0
- package/src/geometry/warp/HypersphereCore.js +211 -0
- package/src/geometry/warp/HypertetraCore.js +386 -0
- package/src/geometry/warp/index.js +57 -0
- package/src/holograms/HolographicVisualizer.js +1073 -0
- package/src/holograms/RealHolographicSystem.js +966 -0
- package/src/holograms/variantRegistry.js +69 -0
- package/src/integrations/FigmaPlugin.js +854 -0
- package/src/integrations/OBSMode.js +754 -0
- package/src/integrations/ThreeJsPackage.js +660 -0
- package/src/integrations/TouchDesignerExport.js +552 -0
- package/src/integrations/frameworks/Vib3React.js +591 -0
- package/src/integrations/frameworks/Vib3Svelte.js +654 -0
- package/src/integrations/frameworks/Vib3Vue.js +628 -0
- package/src/llm/LLMParameterInterface.js +240 -0
- package/src/llm/LLMParameterUI.js +577 -0
- package/src/math/Mat4x4.js +708 -0
- package/src/math/Projection.js +341 -0
- package/src/math/Rotor4D.js +637 -0
- package/src/math/Vec4.js +476 -0
- package/src/math/constants.js +164 -0
- package/src/math/index.js +68 -0
- package/src/math/projections.js +54 -0
- package/src/math/rotations.js +196 -0
- package/src/quantum/QuantumEngine.js +906 -0
- package/src/quantum/QuantumVisualizer.js +1103 -0
- package/src/reactivity/ReactivityConfig.js +499 -0
- package/src/reactivity/ReactivityManager.js +586 -0
- package/src/reactivity/SpatialInputSystem.js +1783 -0
- package/src/reactivity/index.js +93 -0
- package/src/render/CommandBuffer.js +465 -0
- package/src/render/MultiCanvasBridge.js +340 -0
- package/src/render/RenderCommand.js +514 -0
- package/src/render/RenderResourceRegistry.js +523 -0
- package/src/render/RenderState.js +552 -0
- package/src/render/RenderTarget.js +512 -0
- package/src/render/ShaderLoader.js +253 -0
- package/src/render/ShaderProgram.js +599 -0
- package/src/render/UnifiedRenderBridge.js +496 -0
- package/src/render/backends/WebGLBackend.js +1108 -0
- package/src/render/backends/WebGPUBackend.js +1409 -0
- package/src/render/commands/CommandBufferExecutor.js +607 -0
- package/src/render/commands/RenderCommandBuffer.js +661 -0
- package/src/render/commands/index.js +17 -0
- package/src/render/index.js +367 -0
- package/src/scene/Disposable.js +498 -0
- package/src/scene/MemoryPool.js +618 -0
- package/src/scene/Node4D.js +697 -0
- package/src/scene/ResourceManager.js +599 -0
- package/src/scene/Scene4D.js +540 -0
- package/src/scene/index.js +98 -0
- package/src/schemas/error.schema.json +84 -0
- package/src/schemas/extension.schema.json +88 -0
- package/src/schemas/index.js +214 -0
- package/src/schemas/parameters.schema.json +142 -0
- package/src/schemas/tool-pack.schema.json +44 -0
- package/src/schemas/tool-response.schema.json +127 -0
- package/src/shaders/common/fullscreen.vert.glsl +5 -0
- package/src/shaders/common/fullscreen.vert.wgsl +17 -0
- package/src/shaders/common/geometry24.glsl +65 -0
- package/src/shaders/common/geometry24.wgsl +54 -0
- package/src/shaders/common/rotation4d.glsl +85 -0
- package/src/shaders/common/rotation4d.wgsl +86 -0
- package/src/shaders/common/uniforms.glsl +44 -0
- package/src/shaders/common/uniforms.wgsl +48 -0
- package/src/shaders/faceted/faceted.frag.glsl +129 -0
- package/src/shaders/faceted/faceted.frag.wgsl +164 -0
- package/src/shaders/holographic/holographic.frag.glsl +406 -0
- package/src/shaders/holographic/holographic.frag.wgsl +185 -0
- package/src/shaders/quantum/quantum.frag.glsl +513 -0
- package/src/shaders/quantum/quantum.frag.wgsl +361 -0
- package/src/testing/ParallelTestFramework.js +519 -0
- package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
- package/src/testing/exportFormats.test.js +8 -0
- package/src/testing/projections.test.js +14 -0
- package/src/testing/rotations.test.js +37 -0
- package/src/ui/InteractivityMenu.js +516 -0
- package/src/ui/StatusManager.js +96 -0
- package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
- package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
- package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
- package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
- package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
- package/src/variations/VariationManager.js +431 -0
- package/src/viewer/AudioReactivity.js +505 -0
- package/src/viewer/CardBending.js +481 -0
- package/src/viewer/GalleryUI.js +832 -0
- package/src/viewer/ReactivityManager.js +590 -0
- package/src/viewer/TradingCardExporter.js +600 -0
- package/src/viewer/ViewerPortal.js +374 -0
- package/src/viewer/index.js +12 -0
- package/src/wasm/WasmLoader.js +296 -0
- package/src/wasm/index.js +132 -0
- package/tools/agentic/mcpTools.js +88 -0
- package/tools/cli/agent-cli.js +92 -0
- package/tools/export/formats.js +24 -0
- package/tools/math/rotation-baseline.mjs +64 -0
- package/tools/shader-sync-verify.js +937 -0
- package/tools/telemetry/manifestPipeline.js +141 -0
- package/tools/telemetry/telemetryEvents.js +35 -0
- package/types/adaptive-sdk.d.ts +185 -0
- package/types/advanced/AIPresetGenerator.d.ts +81 -0
- package/types/advanced/MIDIController.d.ts +100 -0
- package/types/advanced/OffscreenWorker.d.ts +82 -0
- package/types/advanced/WebGPUCompute.d.ts +52 -0
- package/types/advanced/WebXRRenderer.d.ts +77 -0
- package/types/advanced/index.d.ts +46 -0
- package/types/core/ErrorReporter.d.ts +50 -0
- package/types/core/VIB3Engine.d.ts +204 -0
- package/types/creative/ColorPresetsSystem.d.ts +91 -0
- package/types/creative/ParameterTimeline.d.ts +74 -0
- package/types/creative/PostProcessingPipeline.d.ts +109 -0
- package/types/creative/TransitionAnimator.d.ts +71 -0
- package/types/creative/index.d.ts +35 -0
- package/types/integrations/FigmaPlugin.d.ts +46 -0
- package/types/integrations/OBSMode.d.ts +74 -0
- package/types/integrations/ThreeJsPackage.d.ts +62 -0
- package/types/integrations/TouchDesignerExport.d.ts +36 -0
- package/types/integrations/Vib3React.d.ts +74 -0
- package/types/integrations/Vib3Svelte.d.ts +63 -0
- package/types/integrations/Vib3Vue.d.ts +55 -0
- package/types/integrations/index.d.ts +52 -0
- package/types/reactivity/SpatialInputSystem.d.ts +173 -0
- package/types/reactivity/index.d.ts +394 -0
- package/types/render/CommandBuffer.d.ts +169 -0
- package/types/render/RenderCommand.d.ts +312 -0
- package/types/render/RenderState.d.ts +279 -0
- package/types/render/RenderTarget.d.ts +254 -0
- package/types/render/ShaderProgram.d.ts +277 -0
- package/types/render/UnifiedRenderBridge.d.ts +143 -0
- package/types/render/WebGLBackend.d.ts +168 -0
- package/types/render/WebGPUBackend.d.ts +186 -0
- 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
|
+
}
|