Spaces:
Sleeping
Sleeping
| // Configuration loader and manager for the visualizer | |
| import type { ParticleSystemParams } from '../types/visualization' | |
| export interface VisualizerConfig { | |
| visualization: { | |
| general: { | |
| maxSpheres: number | |
| defaultActiveSpheres: number | |
| performanceMode: boolean | |
| debugLogging: boolean | |
| debugLogFrequency: number | |
| } | |
| particles: { | |
| defaultParticleCount: number | |
| minParticleCount: number | |
| maxParticleCount: number | |
| particleSize: number | |
| particleLifetime: number | |
| particleSizeVariation: number | |
| particleOpacity: number | |
| } | |
| spheres: { | |
| baseRadius: number | |
| radiusIncrement: number | |
| innerSphereRadius: number | |
| maxSphereRadius: number | |
| containmentPullback: number | |
| overflowDamping: number | |
| } | |
| physics: { | |
| turbulenceStrength: number | |
| noiseScale: number | |
| noiseScaleVariation: number | |
| noiseSpeed: number | |
| defaultDamping: number | |
| velocityDecay: number | |
| beatForceMultiplier: number | |
| maxVelocity: number | |
| stabilityThreshold: number | |
| emergencyDamping: number | |
| } | |
| rotation: { | |
| rotationSpeedMin: number | |
| rotationSpeedMax: number | |
| rotationSpeedIncrement: number | |
| rotationSmoothness: number | |
| autoRotateSpeed: number | |
| } | |
| } | |
| audio: { | |
| frequencyRanges: Array<{ | |
| name: string | |
| minFrequency: number | |
| maxFrequency: number | |
| beatThreshold: number | |
| volumeChangeThreshold: number | |
| }> | |
| beatDetection: { | |
| energyHistoryLength: number | |
| minTimeBetweenPeaks: number | |
| beatStrength: number | |
| waveDecayRate: number | |
| peakThresholdMultiplier: number | |
| } | |
| volumeSmoothing: { | |
| enabled: boolean | |
| smoothingFactor: number | |
| changeThresholdDefault: number | |
| } | |
| } | |
| entronaut: { | |
| enabled: boolean | |
| sefaAnalysis: { | |
| fieldSize: number | |
| fieldWidth: number | |
| fieldHeight: number | |
| updateFrequency: number | |
| temporalSmoothing: number | |
| intensitySmoothing: number | |
| } | |
| weights: { | |
| alpha_amplitude: number | |
| alpha_phase: number | |
| alpha_entropy: number | |
| alpha_curvature: number | |
| alpha_spectral: number | |
| } | |
| adaptiveCoupling: { | |
| enabled: boolean | |
| updateFrequency: number | |
| dampingBase: number | |
| dampingVariation: number | |
| diffusionBase: number | |
| diffusionVariation: number | |
| couplingBase: number | |
| couplingVariation: number | |
| flockingRadius: number | |
| flockingInfluence: number | |
| metabolicPulseStrength: number | |
| neighborCheckStep: number | |
| neighborRange: number | |
| } | |
| biomimeticBehavior: { | |
| collectiveMemory: number | |
| emergenceThreshold: number | |
| coherenceWeight: number | |
| complexityWeight: number | |
| networkInfluence: number | |
| syncPulseStrength: number | |
| } | |
| } | |
| tendrils: { | |
| enabled: boolean | |
| maxTendrils: number | |
| defaultDensity: number | |
| maxTendrilLength: number | |
| tendrilSegments: number | |
| updateInterval: number | |
| sampleParticlesPerSphere: number | |
| particleSampleStep: number | |
| curvature: { | |
| midPointMultiplier: number | |
| undulationStrength: number | |
| timeFactorBase: number | |
| timeFactorCurve: number | |
| spatialFrequency: number | |
| } | |
| visual: { | |
| baseOpacity: number | |
| glowIntensity: number | |
| colorIntensityBase: number | |
| colorIntensityVariation: number | |
| blendingMode: string | |
| } | |
| } | |
| cymatics: { | |
| enabled: boolean | |
| intensity: number | |
| patterns: { | |
| radialWaves: { | |
| frequencyFactor: number | |
| petalCount: number | |
| amplitudeZ: number | |
| } | |
| sphericalHarmonics: { | |
| lMultiplier: number | |
| mMultiplier: number | |
| timeMultiplier: number | |
| } | |
| geometricLattice: { | |
| frequencyFactor: number | |
| } | |
| flowerOfLife: { | |
| frequencyFactor: number | |
| basePetals: number | |
| petalVariation: number | |
| amplitudeZ: number | |
| } | |
| } | |
| breathing: { | |
| enabled: boolean | |
| speed: number | |
| intensityBase: number | |
| intensityVariation: number | |
| } | |
| } | |
| colors: { | |
| sphereColorSchemes: Array<{ | |
| name: string | |
| start: string | |
| end: string | |
| }> | |
| tendrilColors: { | |
| baseColor: [number, number, number] | |
| frequencyColorMap: { | |
| lowFreq: [number, number, number] | |
| midFreq: [number, number, number] | |
| highFreq: [number, number, number] | |
| } | |
| } | |
| } | |
| camera: { | |
| defaultPosition: [number, number, number] | |
| fov: number | |
| near: number | |
| far: number | |
| controls: { | |
| enableDamping: boolean | |
| dampingFactor: number | |
| maxDistance: number | |
| minDistance: number | |
| maxPolarAngle: number | |
| autoRotateSpeed: number | |
| } | |
| presets: { | |
| [key: string]: [number, number, number] | |
| } | |
| } | |
| fog: { | |
| enabled: boolean | |
| color: string | |
| near: number | |
| far: number | |
| density: number | |
| } | |
| rendering: { | |
| antialias: boolean | |
| background: string | |
| particleBlending: string | |
| targetFPS: number | |
| adaptiveQuality: { | |
| enabled: boolean | |
| minFPS: number | |
| qualityReductionStep: number | |
| qualityRecoveryDelay: number | |
| } | |
| } | |
| ui: { | |
| controls: { | |
| defaultCollapsed: boolean | |
| position: string | |
| maxWidth: string | |
| opacity: number | |
| } | |
| about: { | |
| defaultCollapsed: boolean | |
| position: string | |
| width: string | |
| } | |
| animation: { | |
| transitionDuration: string | |
| hoverScale: number | |
| backdropBlur: string | |
| } | |
| } | |
| performance: { | |
| adaptiveSettings: { | |
| enabled: boolean | |
| thresholds: { | |
| particleReduction: number | |
| tendrilReduction: number | |
| sefaDisable: number | |
| couplingDisable: number | |
| } | |
| } | |
| optimizations: { | |
| particleUpdateStep: number | |
| tendrilUpdateStep: number | |
| sefaUpdateStep: number | |
| couplingUpdateStep: number | |
| neighborCheckReduction: number | |
| } | |
| } | |
| presets: { | |
| [key: string]: Partial<VisualizerConfig> | |
| } | |
| } | |
| class ConfigLoader { | |
| private config: VisualizerConfig | null = null | |
| private loadPromise: Promise<VisualizerConfig> | null = null | |
| async loadConfig(): Promise<VisualizerConfig> { | |
| if (this.config) { | |
| return this.config | |
| } | |
| if (this.loadPromise) { | |
| return this.loadPromise | |
| } | |
| this.loadPromise = this.fetchConfig() | |
| this.config = await this.loadPromise | |
| return this.config | |
| } | |
| private async fetchConfig(): Promise<VisualizerConfig> { | |
| try { | |
| const response = await fetch('/config.json') | |
| if (!response.ok) { | |
| throw new Error(`Failed to load config: ${response.statusText}`) | |
| } | |
| const config = await response.json() | |
| console.log('✅ Configuration loaded successfully') | |
| return config | |
| } catch (error) { | |
| console.error('❌ Failed to load configuration, using defaults:', error) | |
| return this.getDefaultConfig() | |
| } | |
| } | |
| private getDefaultConfig(): VisualizerConfig { | |
| // Fallback default configuration | |
| return { | |
| visualization: { | |
| general: { | |
| maxSpheres: 5, | |
| defaultActiveSpheres: 5, | |
| performanceMode: false, | |
| debugLogging: true, | |
| debugLogFrequency: 0.005 | |
| }, | |
| particles: { | |
| defaultParticleCount: 15000, | |
| minParticleCount: 1000, | |
| maxParticleCount: 30000, | |
| particleSize: 1.0, | |
| particleLifetime: 20.0, | |
| particleSizeVariation: 0.2, | |
| particleOpacity: 0.8 | |
| }, | |
| spheres: { | |
| baseRadius: 1.0, | |
| radiusIncrement: 0.08, | |
| innerSphereRadius: 0.7, | |
| maxSphereRadius: 2.0, | |
| containmentPullback: 0.1, | |
| overflowDamping: 0.9 | |
| }, | |
| physics: { | |
| turbulenceStrength: 0.008, | |
| noiseScale: 3.0, | |
| noiseScaleVariation: 0.4, | |
| noiseSpeed: 0.5, | |
| defaultDamping: 0.95, | |
| velocityDecay: 0.95, | |
| beatForceMultiplier: 0.001, | |
| maxVelocity: 0.1, | |
| stabilityThreshold: 10.0, | |
| emergencyDamping: 0.1 | |
| }, | |
| rotation: { | |
| rotationSpeedMin: 0.001, | |
| rotationSpeedMax: 0.06, | |
| rotationSpeedIncrement: 0.008, | |
| rotationSmoothness: 0.1, | |
| autoRotateSpeed: 0.5 | |
| } | |
| }, | |
| audio: { | |
| frequencyRanges: [ | |
| { name: "sub-bass", minFrequency: 20, maxFrequency: 80, beatThreshold: 0.6, volumeChangeThreshold: 0.05 }, | |
| { name: "bass", minFrequency: 120, maxFrequency: 250, beatThreshold: 0.65, volumeChangeThreshold: 0.08 }, | |
| { name: "mid", minFrequency: 250, maxFrequency: 800, beatThreshold: 0.7, volumeChangeThreshold: 0.1 }, | |
| { name: "high-mid", minFrequency: 1000, maxFrequency: 4000, beatThreshold: 0.75, volumeChangeThreshold: 0.12 }, | |
| { name: "high", minFrequency: 5000, maxFrequency: 10000, beatThreshold: 0.8, volumeChangeThreshold: 0.15 } | |
| ], | |
| beatDetection: { | |
| energyHistoryLength: 30, | |
| minTimeBetweenPeaks: 200, | |
| beatStrength: 0.02, | |
| waveDecayRate: 0.98, | |
| peakThresholdMultiplier: 1.3 | |
| }, | |
| volumeSmoothing: { | |
| enabled: true, | |
| smoothingFactor: 0.1, | |
| changeThresholdDefault: 0.1 | |
| } | |
| }, | |
| entronaut: { | |
| enabled: true, | |
| sefaAnalysis: { | |
| fieldSize: 1024, | |
| fieldWidth: 32, | |
| fieldHeight: 32, | |
| updateFrequency: 3, | |
| temporalSmoothing: 0.9, | |
| intensitySmoothing: 0.1 | |
| }, | |
| weights: { | |
| alpha_amplitude: 1.2, | |
| alpha_phase: 0.8, | |
| alpha_entropy: 1.5, | |
| alpha_curvature: 1.0, | |
| alpha_spectral: 1.0 | |
| }, | |
| adaptiveCoupling: { | |
| enabled: true, | |
| updateFrequency: 4, | |
| dampingBase: 0.9, | |
| dampingVariation: 0.3, | |
| diffusionBase: 0.01, | |
| diffusionVariation: 0.6, | |
| couplingBase: 0.02, | |
| couplingVariation: 0.04, | |
| flockingRadius: 0.25, | |
| flockingInfluence: 0.2, | |
| metabolicPulseStrength: 0.5, | |
| neighborCheckStep: 5, | |
| neighborRange: 20 | |
| }, | |
| biomimeticBehavior: { | |
| collectiveMemory: 0.9, | |
| emergenceThreshold: 0.5, | |
| coherenceWeight: 1.0, | |
| complexityWeight: 1.0, | |
| networkInfluence: 0.1, | |
| syncPulseStrength: 0.2 | |
| } | |
| }, | |
| tendrils: { | |
| enabled: true, | |
| maxTendrils: 2000, | |
| defaultDensity: 0.3, | |
| maxTendrilLength: 0.8, | |
| tendrilSegments: 8, | |
| updateInterval: 100, | |
| sampleParticlesPerSphere: 10, | |
| particleSampleStep: 1000, | |
| curvature: { | |
| midPointMultiplier: 0.2, | |
| undulationStrength: 0.05, | |
| timeFactorBase: 0.001, | |
| timeFactorCurve: 0.0015, | |
| spatialFrequency: 10 | |
| }, | |
| visual: { | |
| baseOpacity: 0.3, | |
| glowIntensity: 1.0, | |
| colorIntensityBase: 0.3, | |
| colorIntensityVariation: 0.7, | |
| blendingMode: "additive" | |
| } | |
| }, | |
| cymatics: { | |
| enabled: true, | |
| intensity: 0.002, | |
| patterns: { | |
| radialWaves: { frequencyFactor: 0.1, petalCount: 6, amplitudeZ: 0.5 }, | |
| sphericalHarmonics: { lMultiplier: 0.5, mMultiplier: 0.3, timeMultiplier: 2 }, | |
| geometricLattice: { frequencyFactor: 0.2 }, | |
| flowerOfLife: { frequencyFactor: 0.15, basePetals: 6, petalVariation: 0.1, amplitudeZ: 0.3 } | |
| }, | |
| breathing: { enabled: true, speed: 0.5, intensityBase: 0.3, intensityVariation: 0.7 } | |
| }, | |
| colors: { | |
| sphereColorSchemes: [ | |
| { name: "sub-bass", start: "#ff3366", end: "#3366ff" }, | |
| { name: "bass", start: "#ff6b35", end: "#c44569" }, | |
| { name: "mid", start: "#f7931e", end: "#f8b500" }, | |
| { name: "high-mid", start: "#ffe66d", end: "#feca57" }, | |
| { name: "high", start: "#4ecdc4", end: "#45b7d1" } | |
| ], | |
| tendrilColors: { | |
| baseColor: [0.3, 0.3, 0.3], | |
| frequencyColorMap: { | |
| lowFreq: [0.8, 0.2, 0.2], | |
| midFreq: [0.2, 0.8, 0.2], | |
| highFreq: [0.2, 0.2, 0.8] | |
| } | |
| } | |
| }, | |
| camera: { | |
| defaultPosition: [0, 0, 2.5], | |
| fov: 75, | |
| near: 0.1, | |
| far: 1000, | |
| controls: { | |
| enableDamping: true, | |
| dampingFactor: 0.05, | |
| maxDistance: 50, | |
| minDistance: 0.1, | |
| maxPolarAngle: 3.14159, | |
| autoRotateSpeed: 0.5 | |
| }, | |
| presets: { | |
| default: [0, 0, 2.5], | |
| inside: [0, 0, 0.5], | |
| far: [0, 0, 8], | |
| top: [0, 5, 0], | |
| side: [5, 0, 0] | |
| } | |
| }, | |
| fog: { | |
| enabled: true, | |
| color: "#000000", | |
| near: 2.7, | |
| far: 3.7, | |
| density: 0.1 | |
| }, | |
| rendering: { | |
| antialias: true, | |
| background: "#000000", | |
| particleBlending: "additive", | |
| targetFPS: 60, | |
| adaptiveQuality: { | |
| enabled: true, | |
| minFPS: 30, | |
| qualityReductionStep: 0.1, | |
| qualityRecoveryDelay: 2000 | |
| } | |
| }, | |
| ui: { | |
| controls: { | |
| defaultCollapsed: true, | |
| position: "bottom-right", | |
| maxWidth: "250px", | |
| opacity: 0.8 | |
| }, | |
| about: { | |
| defaultCollapsed: true, | |
| position: "bottom-center", | |
| width: "350px" | |
| }, | |
| animation: { | |
| transitionDuration: "0.3s", | |
| hoverScale: 1.1, | |
| backdropBlur: "10px" | |
| } | |
| }, | |
| performance: { | |
| adaptiveSettings: { | |
| enabled: true, | |
| thresholds: { | |
| particleReduction: 25, | |
| tendrilReduction: 20, | |
| sefaDisable: 15, | |
| couplingDisable: 10 | |
| } | |
| }, | |
| optimizations: { | |
| particleUpdateStep: 1, | |
| tendrilUpdateStep: 4, | |
| sefaUpdateStep: 10, | |
| couplingUpdateStep: 4, | |
| neighborCheckReduction: 5 | |
| } | |
| }, | |
| presets: {} | |
| } | |
| } | |
| applyPreset(presetName: string): void { | |
| if (!this.config) { | |
| console.warn('Config not loaded, cannot apply preset') | |
| return | |
| } | |
| const preset = this.config.presets[presetName] | |
| if (!preset) { | |
| console.warn(`Preset '${presetName}' not found`) | |
| return | |
| } | |
| // Deep merge preset into current config | |
| this.config = this.deepMerge(this.config, preset) | |
| console.log(`✅ Applied preset: ${presetName}`) | |
| } | |
| private deepMerge(target: any, source: any): any { | |
| const result = { ...target } | |
| for (const key in source) { | |
| if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { | |
| result[key] = this.deepMerge(target[key] || {}, source[key]) | |
| } else { | |
| result[key] = source[key] | |
| } | |
| } | |
| return result | |
| } | |
| getConfig(): VisualizerConfig | null { | |
| return this.config | |
| } | |
| // Helper methods to get specific config sections | |
| getParticleParams(sphereIndex: number): Partial<ParticleSystemParams> { | |
| if (!this.config) return {} | |
| const audioRange = this.config.audio.frequencyRanges[sphereIndex] || this.config.audio.frequencyRanges[0] | |
| const colors = this.config.colors.sphereColorSchemes[sphereIndex] || this.config.colors.sphereColorSchemes[0] | |
| return { | |
| enabled: true, | |
| particleCount: this.config.visualization.particles.defaultParticleCount, | |
| particleSize: this.config.visualization.particles.particleSize, | |
| particleLifetime: this.config.visualization.particles.particleLifetime, | |
| sphereRadius: this.config.visualization.spheres.baseRadius + (sphereIndex * this.config.visualization.spheres.radiusIncrement), | |
| innerSphereRadius: this.config.visualization.spheres.innerSphereRadius, | |
| turbulenceStrength: this.config.visualization.physics.turbulenceStrength, | |
| noiseScale: this.config.visualization.physics.noiseScale + (sphereIndex * this.config.visualization.physics.noiseScaleVariation), | |
| noiseSpeed: this.config.visualization.physics.noiseSpeed, | |
| rotationSpeedMin: this.config.visualization.rotation.rotationSpeedMin, | |
| rotationSpeedMax: this.config.visualization.rotation.rotationSpeedMax + (sphereIndex * this.config.visualization.rotation.rotationSpeedIncrement), | |
| rotationSmoothness: this.config.visualization.rotation.rotationSmoothness, | |
| beatStrength: this.config.audio.beatDetection.beatStrength, | |
| beatThreshold: audioRange.beatThreshold, | |
| volumeChangeThreshold: audioRange.volumeChangeThreshold, | |
| minFrequency: audioRange.minFrequency, | |
| maxFrequency: audioRange.maxFrequency, | |
| colorStart: colors.start, | |
| colorEnd: colors.end, | |
| dynamicNoiseScale: true | |
| } | |
| } | |
| getCymateIntensity(): number { | |
| return this.config?.cymatics.intensity || 0.002 | |
| } | |
| getTendrilConfig(): any { | |
| return this.config?.tendrils || {} | |
| } | |
| getEntronautConfig(): any { | |
| return this.config?.entronaut || {} | |
| } | |
| getFogConfig(): any { | |
| return this.config?.fog || {} | |
| } | |
| getCameraConfig(): any { | |
| return this.config?.camera || {} | |
| } | |
| // Hot reload support for development | |
| async reloadConfig(): Promise<VisualizerConfig> { | |
| this.config = null | |
| this.loadPromise = null | |
| return this.loadConfig() | |
| } | |
| } | |
| // Singleton instance | |
| export const configLoader = new ConfigLoader() | |
| // Export for use in components | |
| export default configLoader |