@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,966 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REAL Holographic System - Modified for holo-* canvas IDs
|
|
3
|
+
* Uses the elaborate effects from active-holographic-systems-FIXED
|
|
4
|
+
* Audio reactive only - no mouse/touch/scroll interference
|
|
5
|
+
*/
|
|
6
|
+
import { HolographicVisualizer } from './HolographicVisualizer.js';
|
|
7
|
+
import { MultiCanvasBridge } from '../render/MultiCanvasBridge.js';
|
|
8
|
+
import { shaderLoader } from '../render/ShaderLoader.js';
|
|
9
|
+
|
|
10
|
+
export class RealHolographicSystem {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.visualizers = [];
|
|
13
|
+
this.currentVariant = 0;
|
|
14
|
+
this.baseVariants = 30; // Original 30 variations
|
|
15
|
+
this.totalVariants = 30;
|
|
16
|
+
this.isActive = false;
|
|
17
|
+
|
|
18
|
+
// Canvas override for single-canvas multi-instance mode
|
|
19
|
+
/** @type {HTMLCanvasElement|null} */
|
|
20
|
+
this.canvasOverride = options.canvas || null;
|
|
21
|
+
|
|
22
|
+
// Bridge rendering state
|
|
23
|
+
/** @type {MultiCanvasBridge|null} */
|
|
24
|
+
this._multiCanvasBridge = null;
|
|
25
|
+
/** @type {'direct'|'bridge'} */
|
|
26
|
+
this._renderMode = 'direct';
|
|
27
|
+
/** @type {number} time accumulator for bridge rendering (ms) */
|
|
28
|
+
this._bridgeTime = 0;
|
|
29
|
+
|
|
30
|
+
// Conditional reactivity: Use built-in only if ReactivityManager not active
|
|
31
|
+
this.useBuiltInReactivity = !window.reactivityManager;
|
|
32
|
+
|
|
33
|
+
// Audio reactivity system
|
|
34
|
+
this.audioEnabled = false;
|
|
35
|
+
this.audioContext = null;
|
|
36
|
+
this.analyser = null;
|
|
37
|
+
this.frequencyData = null;
|
|
38
|
+
this.audioData = { bass: 0, mid: 0, high: 0 };
|
|
39
|
+
|
|
40
|
+
// Variant names for display - SEQUENTIAL ORDER
|
|
41
|
+
this.variantNames = [
|
|
42
|
+
// 0-3: TETRAHEDRON variations
|
|
43
|
+
'TETRAHEDRON LATTICE', 'TETRAHEDRON FIELD', 'TETRAHEDRON MATRIX', 'TETRAHEDRON RESONANCE',
|
|
44
|
+
// 4-7: HYPERCUBE variations
|
|
45
|
+
'HYPERCUBE LATTICE', 'HYPERCUBE FIELD', 'HYPERCUBE MATRIX', 'HYPERCUBE QUANTUM',
|
|
46
|
+
// 8-11: SPHERE variations
|
|
47
|
+
'SPHERE LATTICE', 'SPHERE FIELD', 'SPHERE MATRIX', 'SPHERE RESONANCE',
|
|
48
|
+
// 12-15: TORUS variations
|
|
49
|
+
'TORUS LATTICE', 'TORUS FIELD', 'TORUS MATRIX', 'TORUS QUANTUM',
|
|
50
|
+
// 16-19: KLEIN BOTTLE variations
|
|
51
|
+
'KLEIN BOTTLE LATTICE', 'KLEIN BOTTLE FIELD', 'KLEIN BOTTLE MATRIX', 'KLEIN BOTTLE QUANTUM',
|
|
52
|
+
// 20-22: FRACTAL variations
|
|
53
|
+
'FRACTAL LATTICE', 'FRACTAL FIELD', 'FRACTAL QUANTUM',
|
|
54
|
+
// 23-25: WAVE variations
|
|
55
|
+
'WAVE LATTICE', 'WAVE FIELD', 'WAVE QUANTUM',
|
|
56
|
+
// 26-29: CRYSTAL variations
|
|
57
|
+
'CRYSTAL LATTICE', 'CRYSTAL FIELD', 'CRYSTAL MATRIX', 'CRYSTAL QUANTUM'
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
this.initialize();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
initialize() {
|
|
64
|
+
console.log('๐จ Initializing REAL Holographic System for Active Holograms tab...');
|
|
65
|
+
this.createVisualizers();
|
|
66
|
+
this.setupCenterDistanceReactivity(); // NEW: Center-distance grid density changes
|
|
67
|
+
this.updateVariantDisplay();
|
|
68
|
+
this.startRenderLoop();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
createVisualizers() {
|
|
72
|
+
// Single-canvas override mode: lightweight content-only layer
|
|
73
|
+
if (this.canvasOverride) {
|
|
74
|
+
try {
|
|
75
|
+
const visualizer = new HolographicVisualizer(
|
|
76
|
+
this.canvasOverride, 'content', 1.0, this.currentVariant
|
|
77
|
+
);
|
|
78
|
+
if (visualizer.gl) {
|
|
79
|
+
this.visualizers.push(visualizer);
|
|
80
|
+
console.log('โ
Created holographic single-canvas visualizer (content layer)');
|
|
81
|
+
} else {
|
|
82
|
+
console.warn('โ ๏ธ No WebGL context for holographic canvasOverride');
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn('Failed to create holographic single-canvas visualizer:', error);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Create the 5 visualizers using HOLO canvas IDs
|
|
91
|
+
const layers = [
|
|
92
|
+
{ id: 'holo-background-canvas', role: 'background', reactivity: 0.5 },
|
|
93
|
+
{ id: 'holo-shadow-canvas', role: 'shadow', reactivity: 0.7 },
|
|
94
|
+
{ id: 'holo-content-canvas', role: 'content', reactivity: 0.9 },
|
|
95
|
+
{ id: 'holo-highlight-canvas', role: 'highlight', reactivity: 1.1 },
|
|
96
|
+
{ id: 'holo-accent-canvas', role: 'accent', reactivity: 1.5 }
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
let successfulLayers = 0;
|
|
100
|
+
layers.forEach(layer => {
|
|
101
|
+
try {
|
|
102
|
+
// Check if canvas element exists
|
|
103
|
+
const canvas = document.getElementById(layer.id);
|
|
104
|
+
if (!canvas) {
|
|
105
|
+
console.error(`โ Canvas not found: ${layer.id}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(`๐ Creating holographic visualizer for: ${layer.id}`);
|
|
110
|
+
const visualizer = new HolographicVisualizer(layer.id, layer.role, layer.reactivity, this.currentVariant);
|
|
111
|
+
|
|
112
|
+
if (visualizer.gl) {
|
|
113
|
+
this.visualizers.push(visualizer);
|
|
114
|
+
successfulLayers++;
|
|
115
|
+
console.log(`โ
Created REAL holographic layer: ${layer.role} (${layer.id})`);
|
|
116
|
+
} else {
|
|
117
|
+
console.error(`โ No WebGL context for: ${layer.id}`);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`โ Failed to create REAL holographic layer ${layer.id}:`, error);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
console.log(`โ
Created ${successfulLayers}/5 REAL holographic layers`);
|
|
125
|
+
|
|
126
|
+
if (successfulLayers === 0) {
|
|
127
|
+
console.error('๐จ NO HOLOGRAPHIC VISUALIZERS CREATED! Check canvas elements and WebGL support.');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Initialize the Holographic system through UnifiedRenderBridge / MultiCanvasBridge.
|
|
133
|
+
* Wires all 5 layers through the bridge for WebGL/WebGPU abstraction.
|
|
134
|
+
* Falls back to direct WebGL if bridge initialization fails.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} [options]
|
|
137
|
+
* @param {boolean} [options.preferWebGPU=true]
|
|
138
|
+
* @param {boolean} [options.debug=false]
|
|
139
|
+
* @returns {Promise<boolean>} True if bridge mode activated
|
|
140
|
+
*/
|
|
141
|
+
async initWithBridge(options = {}) {
|
|
142
|
+
try {
|
|
143
|
+
const bridge = await this.createMultiCanvasBridge(options);
|
|
144
|
+
if (bridge && bridge.initialized) {
|
|
145
|
+
this._renderMode = 'bridge';
|
|
146
|
+
this._bridgeTime = 0;
|
|
147
|
+
console.log(`Holographic System initialized via ${bridge.backendType} bridge (${bridge.layerCount} layers)`);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
console.warn('Holographic bridge init returned no bridge, staying in direct mode');
|
|
151
|
+
return false;
|
|
152
|
+
} catch (e) {
|
|
153
|
+
console.error('Holographic bridge init failed, staying in direct mode:', e);
|
|
154
|
+
this._renderMode = 'direct';
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create a MultiCanvasBridge for WebGPU rendering.
|
|
161
|
+
* Returns a configured bridge with holographic shaders compiled on all layers.
|
|
162
|
+
*
|
|
163
|
+
* @param {object} [options]
|
|
164
|
+
* @param {boolean} [options.preferWebGPU=true]
|
|
165
|
+
* @returns {Promise<MultiCanvasBridge|null>}
|
|
166
|
+
*/
|
|
167
|
+
async createMultiCanvasBridge(options = {}) {
|
|
168
|
+
const canvasMap = {};
|
|
169
|
+
const layerIds = {
|
|
170
|
+
background: 'holo-background-canvas',
|
|
171
|
+
shadow: 'holo-shadow-canvas',
|
|
172
|
+
content: 'holo-content-canvas',
|
|
173
|
+
highlight: 'holo-highlight-canvas',
|
|
174
|
+
accent: 'holo-accent-canvas'
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
for (const [role, id] of Object.entries(layerIds)) {
|
|
178
|
+
const el = document.getElementById(id);
|
|
179
|
+
if (el) canvasMap[role] = el;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (Object.keys(canvasMap).length === 0) return null;
|
|
183
|
+
|
|
184
|
+
const bridge = new MultiCanvasBridge();
|
|
185
|
+
await bridge.initialize({ canvases: canvasMap, preferWebGPU: options.preferWebGPU !== false });
|
|
186
|
+
|
|
187
|
+
// Load external shader files, fall back to inline if unavailable
|
|
188
|
+
let sources = {
|
|
189
|
+
glslVertex: 'attribute vec2 a_position;\nvoid main() { gl_Position = vec4(a_position, 0.0, 1.0); }',
|
|
190
|
+
glslFragment: null,
|
|
191
|
+
wgslFragment: null
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const external = await shaderLoader.loadShaderPair('holographic', 'holographic/holographic.frag');
|
|
196
|
+
if (external.glslVertex) sources.glslVertex = external.glslVertex;
|
|
197
|
+
if (external.glslFragment) sources.glslFragment = external.glslFragment;
|
|
198
|
+
if (external.wgslFragment) sources.wgslFragment = external.wgslFragment;
|
|
199
|
+
} catch (loadErr) {
|
|
200
|
+
console.warn('Holographic external shader load failed, using inline fallback');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (sources.glslFragment || sources.wgslFragment) {
|
|
204
|
+
const result = bridge.compileShaderAll('holographic', sources);
|
|
205
|
+
if (result.failed.length > 0) {
|
|
206
|
+
console.warn(`Holographic shader compilation failed on layers: ${result.failed.join(', ')}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this._multiCanvasBridge = bridge;
|
|
211
|
+
return bridge;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Build the VIB3+ standard uniform object from current parameters and audio data.
|
|
216
|
+
* Used by bridge rendering to send uniforms to external shader programs.
|
|
217
|
+
* @private
|
|
218
|
+
* @returns {object}
|
|
219
|
+
*/
|
|
220
|
+
_buildSharedUniforms() {
|
|
221
|
+
const params = this.getParameters();
|
|
222
|
+
const audio = this.audioData || { bass: 0, mid: 0, high: 0 };
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
u_time: this._bridgeTime,
|
|
226
|
+
u_resolution: null, // Set per-layer by MultiCanvasBridge
|
|
227
|
+
u_geometry: params.geometry || 0,
|
|
228
|
+
u_rot4dXY: params.rot4dXY || 0,
|
|
229
|
+
u_rot4dXZ: params.rot4dXZ || 0,
|
|
230
|
+
u_rot4dYZ: params.rot4dYZ || 0,
|
|
231
|
+
u_rot4dXW: params.rot4dXW || 0,
|
|
232
|
+
u_rot4dYW: params.rot4dYW || 0,
|
|
233
|
+
u_rot4dZW: params.rot4dZW || 0,
|
|
234
|
+
u_dimension: params.dimension || 3.5,
|
|
235
|
+
u_gridDensity: params.gridDensity || 15,
|
|
236
|
+
u_morphFactor: params.morphFactor || 1.0,
|
|
237
|
+
u_chaos: params.chaos || 0.2,
|
|
238
|
+
u_speed: params.speed || 1.0,
|
|
239
|
+
u_hue: params.hue || 320,
|
|
240
|
+
u_intensity: params.intensity || 0.6,
|
|
241
|
+
u_saturation: params.saturation || 0.8,
|
|
242
|
+
u_mouseIntensity: 0,
|
|
243
|
+
u_clickIntensity: this.colorBurstIntensity || 0,
|
|
244
|
+
u_bass: audio.bass || 0,
|
|
245
|
+
u_mid: audio.mid || 0,
|
|
246
|
+
u_high: audio.high || 0
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Render a single frame via the MultiCanvasBridge.
|
|
252
|
+
* Sets shared uniforms and renders all 5 layers with the external holographic shader.
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
_renderBridgeFrame() {
|
|
256
|
+
if (!this._multiCanvasBridge || !this._multiCanvasBridge.initialized) return;
|
|
257
|
+
|
|
258
|
+
this._bridgeTime += 16; // ~60fps increment
|
|
259
|
+
|
|
260
|
+
const uniforms = this._buildSharedUniforms();
|
|
261
|
+
|
|
262
|
+
// Set canvas resolution per layer before rendering
|
|
263
|
+
for (const layerName of this._multiCanvasBridge.layerNames) {
|
|
264
|
+
const bridge = this._multiCanvasBridge.getBridge(layerName);
|
|
265
|
+
if (bridge && bridge.canvas) {
|
|
266
|
+
this._multiCanvasBridge.setLayerUniforms(layerName, {
|
|
267
|
+
u_resolution: [bridge.canvas.width, bridge.canvas.height]
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this._multiCanvasBridge.setSharedUniforms(uniforms);
|
|
273
|
+
this._multiCanvasBridge.renderAll('holographic', { clearColor: [0, 0, 0, 0] });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
setActive(active) {
|
|
277
|
+
this.isActive = active;
|
|
278
|
+
|
|
279
|
+
if (active) {
|
|
280
|
+
// Show holographic layers (skip in single-canvas override mode)
|
|
281
|
+
if (!this.canvasOverride) {
|
|
282
|
+
const holoLayers = document.getElementById('holographicLayers');
|
|
283
|
+
if (holoLayers) {
|
|
284
|
+
holoLayers.style.display = 'block';
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Start audio only if globally enabled and not already started
|
|
289
|
+
if (!this.audioEnabled && window.audioEnabled === true) {
|
|
290
|
+
this.initAudio();
|
|
291
|
+
}
|
|
292
|
+
console.log('๐ REAL Active Holograms ACTIVATED with audio reactivity');
|
|
293
|
+
} else {
|
|
294
|
+
// Hide holographic layers (skip in single-canvas override mode)
|
|
295
|
+
if (!this.canvasOverride) {
|
|
296
|
+
const holoLayers = document.getElementById('holographicLayers');
|
|
297
|
+
if (holoLayers) {
|
|
298
|
+
holoLayers.style.display = 'none';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
console.log('๐ REAL Active Holograms DEACTIVATED');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
updateVariantDisplay() {
|
|
307
|
+
// This will be called by the main UI system
|
|
308
|
+
const variantName = this.variantNames[this.currentVariant];
|
|
309
|
+
return {
|
|
310
|
+
variant: this.currentVariant,
|
|
311
|
+
name: variantName,
|
|
312
|
+
geometryType: Math.floor(this.currentVariant / 4)
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
nextVariant() {
|
|
317
|
+
this.updateVariant(this.currentVariant + 1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
previousVariant() {
|
|
321
|
+
this.updateVariant(this.currentVariant - 1);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
randomVariant() {
|
|
325
|
+
const randomIndex = Math.floor(Math.random() * this.totalVariants);
|
|
326
|
+
this.updateVariant(randomIndex);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
setVariant(variant) {
|
|
330
|
+
this.updateVariant(variant);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
updateParameter(param, value) {
|
|
334
|
+
// Store custom parameter overrides
|
|
335
|
+
if (!this.customParams) {
|
|
336
|
+
this.customParams = {};
|
|
337
|
+
}
|
|
338
|
+
this.customParams[param] = value;
|
|
339
|
+
|
|
340
|
+
console.log(`๐ Updating holographic ${param}: ${value} (${this.visualizers.length} visualizers)`);
|
|
341
|
+
|
|
342
|
+
// CRITICAL FIX: Call updateParameters method on ALL visualizers for immediate render
|
|
343
|
+
this.visualizers.forEach((visualizer, index) => {
|
|
344
|
+
try {
|
|
345
|
+
if (visualizer.updateParameters) {
|
|
346
|
+
// Use new updateParameters method with proper parameter mapping
|
|
347
|
+
const params = {};
|
|
348
|
+
params[param] = value;
|
|
349
|
+
visualizer.updateParameters(params);
|
|
350
|
+
console.log(`โ
Updated holographic layer ${index} (${visualizer.role}) with ${param}=${value}`);
|
|
351
|
+
} else {
|
|
352
|
+
console.warn(`โ ๏ธ Holographic layer ${index} missing updateParameters method, using fallback`);
|
|
353
|
+
// Fallback for older method (direct parameter setting)
|
|
354
|
+
if (visualizer.variantParams) {
|
|
355
|
+
visualizer.variantParams[param] = value;
|
|
356
|
+
|
|
357
|
+
// If it's a geometry type change, regenerate role params with new geometry
|
|
358
|
+
if (param === 'geometryType') {
|
|
359
|
+
visualizer.roleParams = visualizer.generateRoleParams(visualizer.role);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Force manual render for older visualizers
|
|
363
|
+
if (visualizer.render) {
|
|
364
|
+
visualizer.render();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.error(`โ Failed to update holographic layer ${index}:`, error);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
console.log(`๐ Holographic parameter update complete: ${param}=${value}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Override updateVariant to preserve custom parameters
|
|
377
|
+
updateVariant(newVariant) {
|
|
378
|
+
if (newVariant < 0) newVariant = this.totalVariants - 1;
|
|
379
|
+
if (newVariant >= this.totalVariants) newVariant = 0;
|
|
380
|
+
|
|
381
|
+
this.currentVariant = newVariant;
|
|
382
|
+
|
|
383
|
+
// Update all visualizers with new variant parameters
|
|
384
|
+
this.visualizers.forEach(visualizer => {
|
|
385
|
+
visualizer.variant = this.currentVariant;
|
|
386
|
+
visualizer.variantParams = visualizer.generateVariantParams(this.currentVariant);
|
|
387
|
+
visualizer.roleParams = visualizer.generateRoleParams(visualizer.role);
|
|
388
|
+
|
|
389
|
+
// Apply any custom parameter overrides
|
|
390
|
+
if (this.customParams) {
|
|
391
|
+
Object.keys(this.customParams).forEach(param => {
|
|
392
|
+
visualizer.variantParams[param] = this.customParams[param];
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
this.updateVariantDisplay();
|
|
398
|
+
console.log(`๐ REAL Holograms switched to variant ${this.currentVariant + 1}: ${this.variantNames[this.currentVariant]}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
getCurrentVariantInfo() {
|
|
402
|
+
return {
|
|
403
|
+
variant: this.currentVariant,
|
|
404
|
+
name: this.variantNames[this.currentVariant],
|
|
405
|
+
geometryType: Math.floor(this.currentVariant / 4)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get current parameters for saving/export (CRITICAL for gallery saving)
|
|
411
|
+
*/
|
|
412
|
+
getParameters() {
|
|
413
|
+
// Collect parameters from UI sliders - same as other systems
|
|
414
|
+
const params = {
|
|
415
|
+
geometry: Math.floor(this.currentVariant / 4), // Extract geometry from variant
|
|
416
|
+
gridDensity: parseFloat(document.getElementById('gridDensity')?.value || 15),
|
|
417
|
+
morphFactor: parseFloat(document.getElementById('morphFactor')?.value || 1.0),
|
|
418
|
+
chaos: parseFloat(document.getElementById('chaos')?.value || 0.2),
|
|
419
|
+
speed: parseFloat(document.getElementById('speed')?.value || 1.0),
|
|
420
|
+
hue: parseFloat(document.getElementById('hue')?.value || 320),
|
|
421
|
+
intensity: parseFloat(document.getElementById('intensity')?.value || 0.6),
|
|
422
|
+
saturation: parseFloat(document.getElementById('saturation')?.value || 0.8),
|
|
423
|
+
rot4dXW: parseFloat(document.getElementById('rot4dXW')?.value || 0.0),
|
|
424
|
+
rot4dYW: parseFloat(document.getElementById('rot4dYW')?.value || 0.0),
|
|
425
|
+
rot4dZW: parseFloat(document.getElementById('rot4dZW')?.value || 0.0),
|
|
426
|
+
variant: this.currentVariant
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// Apply any custom parameter overrides
|
|
430
|
+
if (this.customParams) {
|
|
431
|
+
Object.assign(params, this.customParams);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
console.log('๐ Holographic system getParameters:', params);
|
|
435
|
+
return params;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async initAudio() {
|
|
439
|
+
try {
|
|
440
|
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
441
|
+
|
|
442
|
+
if (this.audioContext.state === 'suspended') {
|
|
443
|
+
await this.audioContext.resume();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
this.analyser = this.audioContext.createAnalyser();
|
|
447
|
+
this.analyser.fftSize = 256;
|
|
448
|
+
this.frequencyData = new Uint8Array(this.analyser.frequencyBinCount);
|
|
449
|
+
|
|
450
|
+
const constraints = {
|
|
451
|
+
audio: {
|
|
452
|
+
echoCancellation: false,
|
|
453
|
+
noiseSuppression: false,
|
|
454
|
+
autoGainControl: false,
|
|
455
|
+
sampleRate: 44100
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
460
|
+
this._audioStream = stream;
|
|
461
|
+
const source = this.audioContext.createMediaStreamSource(stream);
|
|
462
|
+
source.connect(this.analyser);
|
|
463
|
+
|
|
464
|
+
this.audioEnabled = true;
|
|
465
|
+
console.log('REAL Holograms audio reactivity enabled');
|
|
466
|
+
} catch (error) {
|
|
467
|
+
console.error('REAL Holograms audio initialization failed:', error);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// NEW: Disable holographic audio (respects global audio toggle)
|
|
472
|
+
disableAudio() {
|
|
473
|
+
if (this.audioEnabled) {
|
|
474
|
+
this.audioEnabled = false;
|
|
475
|
+
// Stop audio stream tracks to release microphone
|
|
476
|
+
if (this._audioStream) {
|
|
477
|
+
this._audioStream.getTracks().forEach(track => track.stop());
|
|
478
|
+
this._audioStream = null;
|
|
479
|
+
}
|
|
480
|
+
if (this.audioContext) {
|
|
481
|
+
this.audioContext.close().catch(() => {});
|
|
482
|
+
this.audioContext = null;
|
|
483
|
+
}
|
|
484
|
+
this.analyser = null;
|
|
485
|
+
this.frequencyData = null;
|
|
486
|
+
this.audioData = { bass: 0, mid: 0, high: 0 };
|
|
487
|
+
console.log('REAL Holograms audio reactivity disabled');
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
updateAudio() {
|
|
492
|
+
if (!this.audioEnabled || !this.analyser || !this.isActive || window.audioEnabled === false) return;
|
|
493
|
+
|
|
494
|
+
this.analyser.getByteFrequencyData(this.frequencyData);
|
|
495
|
+
|
|
496
|
+
const bassEnd = Math.floor(this.frequencyData.length * 0.1);
|
|
497
|
+
const midEnd = Math.floor(this.frequencyData.length * 0.4);
|
|
498
|
+
|
|
499
|
+
let bass = 0, mid = 0, high = 0;
|
|
500
|
+
|
|
501
|
+
for (let i = 0; i < bassEnd; i++) {
|
|
502
|
+
bass += this.frequencyData[i];
|
|
503
|
+
}
|
|
504
|
+
bass /= (bassEnd * 255);
|
|
505
|
+
|
|
506
|
+
for (let i = bassEnd; i < midEnd; i++) {
|
|
507
|
+
mid += this.frequencyData[i];
|
|
508
|
+
}
|
|
509
|
+
mid /= ((midEnd - bassEnd) * 255);
|
|
510
|
+
|
|
511
|
+
for (let i = midEnd; i < this.frequencyData.length; i++) {
|
|
512
|
+
high += this.frequencyData[i];
|
|
513
|
+
}
|
|
514
|
+
high /= ((this.frequencyData.length - midEnd) * 255);
|
|
515
|
+
|
|
516
|
+
// Enhanced audio processing for better visual response
|
|
517
|
+
const smoothedAudio = {
|
|
518
|
+
bass: this.smoothAudioValue(bass, 'bass'),
|
|
519
|
+
mid: this.smoothAudioValue(mid, 'mid'),
|
|
520
|
+
high: this.smoothAudioValue(high, 'high'),
|
|
521
|
+
energy: (bass + mid + high) / 3,
|
|
522
|
+
rhythm: this.detectRhythm(bass),
|
|
523
|
+
melody: this.detectMelody(mid, high)
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
this.audioData = smoothedAudio;
|
|
527
|
+
|
|
528
|
+
// Apply NEW AUDIO REACTIVITY GRID SETTINGS if available
|
|
529
|
+
if (window.audioReactivitySettings) {
|
|
530
|
+
this.applyAudioReactivityGrid(smoothedAudio);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Apply audio reactivity to all visualizers
|
|
534
|
+
this.visualizers.forEach(visualizer => {
|
|
535
|
+
visualizer.updateAudio(this.audioData);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
smoothAudioValue(currentValue, type) {
|
|
540
|
+
if (!this.audioSmoothing) {
|
|
541
|
+
this.audioSmoothing = { bass: 0, mid: 0, high: 0 };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const smoothingFactor = 0.4;
|
|
545
|
+
this.audioSmoothing[type] = this.audioSmoothing[type] * smoothingFactor + currentValue * (1 - smoothingFactor);
|
|
546
|
+
|
|
547
|
+
const threshold = 0.05;
|
|
548
|
+
return this.audioSmoothing[type] > threshold ? this.audioSmoothing[type] : 0;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
detectRhythm(bassLevel) {
|
|
552
|
+
if (!this.previousBass) this.previousBass = 0;
|
|
553
|
+
const beatDetected = bassLevel > this.previousBass + 0.2;
|
|
554
|
+
this.previousBass = bassLevel;
|
|
555
|
+
return beatDetected ? 1.0 : 0.0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
detectMelody(midLevel, highLevel) {
|
|
559
|
+
const melodicActivity = (midLevel + highLevel) / 2;
|
|
560
|
+
return melodicActivity > 0.3 ? melodicActivity : 0.0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
applyAudioReactivityGrid(audioData) {
|
|
564
|
+
const settings = window.audioReactivitySettings;
|
|
565
|
+
if (!settings || settings.activeVisualModes.size === 0) return;
|
|
566
|
+
|
|
567
|
+
// Get sensitivity multiplier
|
|
568
|
+
const sensitivityMultiplier = settings.sensitivity[settings.activeSensitivity];
|
|
569
|
+
|
|
570
|
+
// Apply audio changes to different visual modes
|
|
571
|
+
settings.activeVisualModes.forEach(modeKey => {
|
|
572
|
+
const [sensitivity, visualMode] = modeKey.split('-');
|
|
573
|
+
const paramList = settings.visualModes[visualMode];
|
|
574
|
+
|
|
575
|
+
if (!paramList) return;
|
|
576
|
+
|
|
577
|
+
// Calculate audio intensity with sensitivity
|
|
578
|
+
const audioIntensity = (audioData.energy * sensitivityMultiplier);
|
|
579
|
+
const bassIntensity = (audioData.bass * sensitivityMultiplier);
|
|
580
|
+
const rhythmIntensity = (audioData.rhythm * sensitivityMultiplier);
|
|
581
|
+
|
|
582
|
+
paramList.forEach(param => {
|
|
583
|
+
let currentValue = 0;
|
|
584
|
+
|
|
585
|
+
switch (param) {
|
|
586
|
+
case 'hue':
|
|
587
|
+
// Color: Audio-reactive hue cycling
|
|
588
|
+
if (!this.audioHueBase) this.audioHueBase = 320;
|
|
589
|
+
this.audioHueBase += audioIntensity * 5; // Smooth color shifts
|
|
590
|
+
currentValue = this.audioHueBase % 360;
|
|
591
|
+
break;
|
|
592
|
+
|
|
593
|
+
case 'saturation':
|
|
594
|
+
// Color: Beat-responsive saturation
|
|
595
|
+
currentValue = Math.min(1.0, 0.6 + (rhythmIntensity * 0.4));
|
|
596
|
+
break;
|
|
597
|
+
|
|
598
|
+
case 'intensity':
|
|
599
|
+
// Color: Energy-responsive brightness
|
|
600
|
+
currentValue = Math.min(1.0, 0.4 + (audioIntensity * 0.6));
|
|
601
|
+
break;
|
|
602
|
+
|
|
603
|
+
case 'morphFactor':
|
|
604
|
+
// Geometry: Audio-morphing shapes
|
|
605
|
+
currentValue = Math.min(2.0, 1.0 + (bassIntensity * 1.0));
|
|
606
|
+
break;
|
|
607
|
+
|
|
608
|
+
case 'gridDensity':
|
|
609
|
+
// Geometry: Beat-responsive density
|
|
610
|
+
currentValue = Math.min(100, 15 + (rhythmIntensity * 50));
|
|
611
|
+
break;
|
|
612
|
+
|
|
613
|
+
case 'chaos':
|
|
614
|
+
// Geometry: Energy-chaos correlation
|
|
615
|
+
currentValue = Math.min(1.0, audioIntensity * 0.8);
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
case 'speed':
|
|
619
|
+
// Movement: Tempo-responsive animation
|
|
620
|
+
currentValue = Math.min(3.0, 1.0 + (audioIntensity * 2.0));
|
|
621
|
+
break;
|
|
622
|
+
|
|
623
|
+
case 'rot4dXW':
|
|
624
|
+
// Movement: Bass-driven rotation
|
|
625
|
+
if (!this.audioRotationXW) this.audioRotationXW = 0;
|
|
626
|
+
this.audioRotationXW += bassIntensity * 0.1;
|
|
627
|
+
currentValue = this.audioRotationXW % (Math.PI * 2);
|
|
628
|
+
break;
|
|
629
|
+
|
|
630
|
+
case 'rot4dYW':
|
|
631
|
+
// Movement: Mid-frequency rotation
|
|
632
|
+
if (!this.audioRotationYW) this.audioRotationYW = 0;
|
|
633
|
+
this.audioRotationYW += audioData.mid * sensitivityMultiplier * 0.08;
|
|
634
|
+
currentValue = this.audioRotationYW % (Math.PI * 2);
|
|
635
|
+
break;
|
|
636
|
+
|
|
637
|
+
case 'rot4dZW':
|
|
638
|
+
// Movement: High-frequency rotation
|
|
639
|
+
if (!this.audioRotationZW) this.audioRotationZW = 0;
|
|
640
|
+
this.audioRotationZW += audioData.high * sensitivityMultiplier * 0.06;
|
|
641
|
+
currentValue = this.audioRotationZW % (Math.PI * 2);
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Apply the parameter change
|
|
646
|
+
if (window.updateParameter && currentValue !== undefined) {
|
|
647
|
+
window.updateParameter(param, currentValue.toFixed(2));
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
setupCenterDistanceReactivity() {
|
|
654
|
+
// AUDIO ONLY - No mouse/touch/scroll interference
|
|
655
|
+
// Holographic system is purely audio-reactive now
|
|
656
|
+
console.log('โจ Holographic system: AUDIO-ONLY mode (no mouse/touch reactivity)');
|
|
657
|
+
|
|
658
|
+
if (this.canvasOverride) {
|
|
659
|
+
console.log('โจ Holographic reactivity skipped (single-canvas override mode)');
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// If ReactivityManager is active, it handles all interactivity
|
|
664
|
+
if (!this.useBuiltInReactivity) {
|
|
665
|
+
console.log('โจ Holographic built-in reactivity DISABLED - ReactivityManager active');
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// NO EVENT LISTENERS - Audio reactivity only
|
|
670
|
+
console.log('๐ต Holographic system will respond to audio input only');
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
updateHolographicShimmer(x, y) {
|
|
674
|
+
// HOLOGRAPHIC TRADING CARD SHIMMER EFFECTS
|
|
675
|
+
// Calculate position-based shimmer like real holographic cards
|
|
676
|
+
|
|
677
|
+
// Create iridescent shimmer based on viewing angle (mouse position)
|
|
678
|
+
const angleX = (x - 0.5) * Math.PI; // -ฯ/2 to ฯ/2
|
|
679
|
+
const angleY = (y - 0.5) * Math.PI;
|
|
680
|
+
|
|
681
|
+
// Holographic rainbow shimmer - shifts through spectrum based on angle
|
|
682
|
+
const baseHue = 320; // Start with magenta-pink
|
|
683
|
+
const shimmerRange = 120; // Cover 120 degrees of spectrum
|
|
684
|
+
const hueShimmer = Math.sin(angleX * 2) * Math.cos(angleY * 2) * shimmerRange;
|
|
685
|
+
const shimmerHue = (baseHue + hueShimmer + 360) % 360;
|
|
686
|
+
|
|
687
|
+
// Iridescent intensity - varies with viewing angle like real holograms
|
|
688
|
+
const shimmerIntensity = 0.4 + (0.5 * Math.abs(Math.sin(angleX) * Math.cos(angleY)));
|
|
689
|
+
|
|
690
|
+
// Holographic saturation pulse - high saturation for vivid colors
|
|
691
|
+
const saturationPulse = 0.7 + (0.3 * Math.abs(Math.cos(angleX * 1.5) * Math.sin(angleY * 1.5)));
|
|
692
|
+
|
|
693
|
+
// Subtle depth illusion - slight morph based on angle (much more subtle than faceted)
|
|
694
|
+
const depthMorph = 1.0 + (0.15 * Math.sin(angleX * 0.8) * Math.cos(angleY * 0.8));
|
|
695
|
+
|
|
696
|
+
// Update holographic shimmer parameters
|
|
697
|
+
if (window.updateParameter) {
|
|
698
|
+
window.updateParameter('hue', Math.round(shimmerHue));
|
|
699
|
+
window.updateParameter('intensity', shimmerIntensity.toFixed(2));
|
|
700
|
+
window.updateParameter('saturation', saturationPulse.toFixed(2));
|
|
701
|
+
window.updateParameter('morphFactor', depthMorph.toFixed(2));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
console.log(`โจ Holographic shimmer: angle=(${angleX.toFixed(2)}, ${angleY.toFixed(2)}) โ Hue=${Math.round(shimmerHue)}, Intensity=${shimmerIntensity.toFixed(2)}`);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
triggerHolographicColorBurst(x, y) {
|
|
708
|
+
// DRAMATIC HOLOGRAPHIC COLOR BURST (like Quantum's dramatic click effects)
|
|
709
|
+
|
|
710
|
+
// Calculate energy based on click position
|
|
711
|
+
const centerX = 0.5;
|
|
712
|
+
const centerY = 0.5;
|
|
713
|
+
const distanceFromCenter = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2);
|
|
714
|
+
const normalizedDistance = Math.min(distanceFromCenter / 0.707, 1.0);
|
|
715
|
+
|
|
716
|
+
// QUANTUM-STYLE DRAMATIC EFFECTS
|
|
717
|
+
this.colorBurstIntensity = 1.0; // Full burst intensity
|
|
718
|
+
|
|
719
|
+
// Multi-parameter dramatic effects that decay back
|
|
720
|
+
this.burstHueShift = 180; // Dramatic hue shift across spectrum
|
|
721
|
+
this.burstIntensityBoost = 0.7; // Major intensity boost
|
|
722
|
+
this.burstSaturationSpike = 0.8; // Vivid color spike
|
|
723
|
+
this.burstChaosEffect = 0.6; // Chaos/morph burst effect
|
|
724
|
+
this.burstSpeedBoost = 1.8; // Animation speed burst
|
|
725
|
+
|
|
726
|
+
console.log(`๐๐ฅ HOLOGRAPHIC COLOR BURST: position=(${x.toFixed(2)}, ${y.toFixed(2)}), distance=${distanceFromCenter.toFixed(3)}`);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
startHolographicColorBurstLoop() {
|
|
730
|
+
const burstAnimation = () => {
|
|
731
|
+
// DRAMATIC HOLOGRAPHIC COLOR BURST ANIMATION (like Quantum's multi-parameter effects)
|
|
732
|
+
let hasActiveEffects = false;
|
|
733
|
+
|
|
734
|
+
if (this.colorBurstIntensity > 0.01) {
|
|
735
|
+
hasActiveEffects = true;
|
|
736
|
+
|
|
737
|
+
// Phase-based burst animation with color cycling
|
|
738
|
+
const burstPhase = this.colorBurstIntensity;
|
|
739
|
+
|
|
740
|
+
// HUE BURST: Cycle through rainbow spectrum
|
|
741
|
+
if (this.burstHueShift > 1) {
|
|
742
|
+
const baseHue = 320; // Holographic magenta base
|
|
743
|
+
const currentHueShift = this.burstHueShift * Math.sin(burstPhase * Math.PI * 2);
|
|
744
|
+
const burstHue = (baseHue + currentHueShift) % 360;
|
|
745
|
+
|
|
746
|
+
if (window.updateParameter) {
|
|
747
|
+
window.updateParameter('hue', Math.round(burstHue));
|
|
748
|
+
}
|
|
749
|
+
this.burstHueShift *= 0.93; // Smooth decay
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// INTENSITY BURST: Dramatic brightness flash
|
|
753
|
+
if (this.burstIntensityBoost > 0.01) {
|
|
754
|
+
const baseIntensity = 0.5;
|
|
755
|
+
const burstIntensity = Math.min(1.0, baseIntensity + this.burstIntensityBoost * burstPhase);
|
|
756
|
+
|
|
757
|
+
if (window.updateParameter) {
|
|
758
|
+
window.updateParameter('intensity', burstIntensity.toFixed(2));
|
|
759
|
+
}
|
|
760
|
+
this.burstIntensityBoost *= 0.92;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// SATURATION SPIKE: Vivid color explosion
|
|
764
|
+
if (this.burstSaturationSpike > 0.01) {
|
|
765
|
+
const baseSaturation = 0.8;
|
|
766
|
+
const burstSaturation = Math.min(1.0, baseSaturation + this.burstSaturationSpike * burstPhase);
|
|
767
|
+
|
|
768
|
+
if (window.updateParameter) {
|
|
769
|
+
window.updateParameter('saturation', burstSaturation.toFixed(2));
|
|
770
|
+
}
|
|
771
|
+
this.burstSaturationSpike *= 0.91;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// CHAOS/MORPH EFFECT: Geometric distortion burst
|
|
775
|
+
if (this.burstChaosEffect > 0.01) {
|
|
776
|
+
const baseChaos = 0.2;
|
|
777
|
+
const burstChaos = baseChaos + this.burstChaosEffect * burstPhase;
|
|
778
|
+
|
|
779
|
+
if (window.updateParameter) {
|
|
780
|
+
window.updateParameter('chaos', burstChaos.toFixed(2));
|
|
781
|
+
}
|
|
782
|
+
this.burstChaosEffect *= 0.90;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// SPEED BOOST: Animation acceleration
|
|
786
|
+
if (this.burstSpeedBoost > 0.01) {
|
|
787
|
+
const baseSpeed = 1.0;
|
|
788
|
+
const burstSpeed = baseSpeed + this.burstSpeedBoost * burstPhase;
|
|
789
|
+
|
|
790
|
+
if (window.updateParameter) {
|
|
791
|
+
window.updateParameter('speed', burstSpeed.toFixed(2));
|
|
792
|
+
}
|
|
793
|
+
this.burstSpeedBoost *= 0.89;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Master intensity decay
|
|
797
|
+
this.colorBurstIntensity *= 0.94;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (this.isActive) {
|
|
801
|
+
requestAnimationFrame(burstAnimation);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
burstAnimation();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
startRenderLoop() {
|
|
809
|
+
const render = () => {
|
|
810
|
+
if (this.isActive) {
|
|
811
|
+
// Update audio reactivity
|
|
812
|
+
this.updateAudio();
|
|
813
|
+
|
|
814
|
+
if (this._renderMode === 'bridge') {
|
|
815
|
+
this._renderBridgeFrame();
|
|
816
|
+
} else {
|
|
817
|
+
// Direct mode: render all visualizers
|
|
818
|
+
this.visualizers.forEach(visualizer => {
|
|
819
|
+
visualizer.render();
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
requestAnimationFrame(render);
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
render();
|
|
828
|
+
console.log(`๐ฌ REAL Holographic render loop started (${this._renderMode} mode)`);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
getVariantName(variant = this.currentVariant) {
|
|
832
|
+
return this.variantNames[variant] || 'UNKNOWN';
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
destroy() {
|
|
836
|
+
// Dispose bridge if active
|
|
837
|
+
if (this._multiCanvasBridge) {
|
|
838
|
+
this._multiCanvasBridge.dispose();
|
|
839
|
+
this._multiCanvasBridge = null;
|
|
840
|
+
}
|
|
841
|
+
this._renderMode = 'direct';
|
|
842
|
+
|
|
843
|
+
this.visualizers.forEach(visualizer => {
|
|
844
|
+
if (visualizer.destroy) {
|
|
845
|
+
visualizer.destroy();
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
this.visualizers = [];
|
|
849
|
+
|
|
850
|
+
if (this.audioContext) {
|
|
851
|
+
this.audioContext.close();
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
console.log('๐งน REAL Holographic System destroyed');
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// ============================================
|
|
858
|
+
// RendererContract Compliance Methods
|
|
859
|
+
// ============================================
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Initialize the renderer (RendererContract.init)
|
|
863
|
+
* For Holographic, initialization happens in constructor.
|
|
864
|
+
* This method allows re-initialization with a new context, including
|
|
865
|
+
* a canvas override for single-canvas multi-instance mode.
|
|
866
|
+
*
|
|
867
|
+
* @param {Object} [context] - Optional context
|
|
868
|
+
* @param {HTMLCanvasElement} [context.canvas] - Canvas element override
|
|
869
|
+
* @param {string} [context.canvasId] - Canvas ID to look up
|
|
870
|
+
* @returns {boolean} Success status
|
|
871
|
+
*/
|
|
872
|
+
init(context = {}) {
|
|
873
|
+
const canvasEl = context.canvas ||
|
|
874
|
+
(context.canvasId ? document.getElementById(context.canvasId) : null);
|
|
875
|
+
|
|
876
|
+
if (canvasEl) {
|
|
877
|
+
this.canvasOverride = canvasEl;
|
|
878
|
+
// Tear down existing visualizers before re-init
|
|
879
|
+
this.visualizers.forEach(v => v.destroy && v.destroy());
|
|
880
|
+
this.visualizers = [];
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// If already initialized (and no new canvas), just return success
|
|
884
|
+
if (this.visualizers.length > 0) {
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
// Otherwise initialize
|
|
888
|
+
this.initialize();
|
|
889
|
+
return this.visualizers.length > 0;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Handle canvas resize (RendererContract.resize)
|
|
894
|
+
* @param {number} width - New width in pixels
|
|
895
|
+
* @param {number} height - New height in pixels
|
|
896
|
+
* @param {number} [pixelRatio=1] - Device pixel ratio
|
|
897
|
+
*/
|
|
898
|
+
resize(width, height, pixelRatio = 1) {
|
|
899
|
+
if (this._renderMode === 'bridge' && this._multiCanvasBridge) {
|
|
900
|
+
this._multiCanvasBridge.resizeAll(width, height, pixelRatio);
|
|
901
|
+
} else {
|
|
902
|
+
this.visualizers.forEach(visualizer => {
|
|
903
|
+
if (visualizer.canvas && visualizer.gl) {
|
|
904
|
+
visualizer.canvas.width = width * pixelRatio;
|
|
905
|
+
visualizer.canvas.height = height * pixelRatio;
|
|
906
|
+
visualizer.canvas.style.width = `${width}px`;
|
|
907
|
+
visualizer.canvas.style.height = `${height}px`;
|
|
908
|
+
visualizer.gl.viewport(0, 0, visualizer.canvas.width, visualizer.canvas.height);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
console.log(`๐ Holographic resized to ${width}x${height} @${pixelRatio}x`);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Render a single frame (RendererContract.render)
|
|
917
|
+
* @param {Object} [frameState] - Frame state with time, params, audio
|
|
918
|
+
*/
|
|
919
|
+
render(frameState = {}) {
|
|
920
|
+
// Apply frameState parameters if provided
|
|
921
|
+
if (frameState.params) {
|
|
922
|
+
Object.keys(frameState.params).forEach(param => {
|
|
923
|
+
this.updateParameter(param, frameState.params[param]);
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Apply audio data if provided
|
|
928
|
+
if (frameState.audio) {
|
|
929
|
+
this.audioData = frameState.audio;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (this._renderMode === 'bridge') {
|
|
933
|
+
this._renderBridgeFrame();
|
|
934
|
+
} else {
|
|
935
|
+
// Render all visualizers in direct mode
|
|
936
|
+
this.visualizers.forEach(visualizer => {
|
|
937
|
+
if (visualizer.render) {
|
|
938
|
+
visualizer.render();
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Get the current rendering backend type.
|
|
946
|
+
* @returns {'direct-webgl'|string}
|
|
947
|
+
*/
|
|
948
|
+
getBackendType() {
|
|
949
|
+
if (this._renderMode === 'bridge' && this._multiCanvasBridge) {
|
|
950
|
+
return this._multiCanvasBridge.backendType || 'bridge';
|
|
951
|
+
}
|
|
952
|
+
return 'direct-webgl';
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Clean up all resources (RendererContract.dispose)
|
|
957
|
+
* Alias for destroy() for contract compliance
|
|
958
|
+
*/
|
|
959
|
+
dispose() {
|
|
960
|
+
// Disable audio first
|
|
961
|
+
this.disableAudio();
|
|
962
|
+
|
|
963
|
+
// Call existing destroy
|
|
964
|
+
this.destroy();
|
|
965
|
+
}
|
|
966
|
+
}
|