// 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 } } class ConfigLoader { private config: VisualizerConfig | null = null private loadPromise: Promise | null = null async loadConfig(): Promise { 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 { 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 { 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 { this.config = null this.loadPromise = null return this.loadConfig() } } // Singleton instance export const configLoader = new ConfigLoader() // Export for use in components export default configLoader