diff --git a/js/app.js b/js/app.js index fcfe877..84d4751 100644 --- a/js/app.js +++ b/js/app.js @@ -298,12 +298,8 @@ document.addEventListener('DOMContentLoaded', () => { } // Phase 2 audio_gaps.md rows #25-#45 - if (activeUniverseId === 'whoniverse' && presetId.indexOf('tardis-') === 0) { - whoniverseAudio.startConsoleTypeTelemetry(2800, 6500); // #25 - } if (presetId === 'bio-vorlon') expandedAudio.startVorlonSong(); // #26 if (presetId === 'bio-wraith') expandedAudio.startHullRegen(); // #28 - if (presetId === 'sta-ceres' || presetId === 'sta-tycho') expandedAudio.startBelterTelemetry(); // #29 if (presetId === 'deep-event-horizon' || presetId === 'deep-icarus-ii') expandedAudio.startHullStrainMoans(); // #30 if (presetId === 'deep-icarus-ii') expandedAudio.startIcarusBeacon(); // #31 if (presetId === 'deep-event-horizon' || presetId === 'deep-nightflyer') expandedAudio.startVoidWhispers(); // #32 diff --git a/js/audio.js b/js/audio.js index 5c6c227..7800281 100644 --- a/js/audio.js +++ b/js/audio.js @@ -811,7 +811,7 @@ class TelemetrySynth { this.params = { volume: 0.4, density: 0.5, // How often background telemetry chirps occur (0 = off, 1 = busy bridge) - era: 'tng', // 'tng', 'voyager', 'tos', 'ds9', 'nx' + era: 'tng', // 'tng', 'voyager', 'tos', 'ds9', 'nx', 'who', 'bioship', 'belter' reverbMix: 0.25 }; @@ -877,6 +877,15 @@ class TelemetrySynth { if (this.isMuted || this.params.volume <= 0.01 || !this.am.ctx) return; switch (this.params.era) { + case 'who': + this.synthesizeWhoMechanicalTelemetry_(); + break; + case 'bioship': + this.synthesizeBioshipOrganicTelemetry_(); + break; + case 'belter': + this.synthesizeBelterJuryRiggedTelemetry_(); + break; case 'tos': Math.random() > 0.4 ? this.synthesizeTOSWarble() : this.synthesizeTOSRelayClick(); break; @@ -1253,6 +1262,312 @@ class TelemetrySynth { osc2.stop(now + 0.7); } + /** + * Whoniverse Mechanical Telemetry Engine (audio_gaps.md #25, Phase 3 era) + * The TARDIS console never chirps like Starfleet gear: each tick emits a + * 0.25-0.6 s cluster of mechanical foley - dual-filtered toggle snaps, + * low 60-120 Hz spring-lever clicks and damped rotary selector pings + * (no electronic beeps). All transients scheduled on absolute ctx time so + * the single scheduler timeout can never orphan nodes. + */ + synthesizeWhoMechanicalTelemetry_() { + const ctx = this.am.ctx; + if (!ctx || !this.gainNode) return; + + const now = ctx.currentTime; + const strikeCount = 2 + Math.floor(Math.random() * 4); // 2-5 transients + let cursor = Math.random() * 0.12; + + // Toggle snap: dual-filtered short noise pulse (20-45 ms body) + const toggleSnap = (t, peak) => { + const buf = this.am.createNoiseBuffer('white', 0.06); + const noise = ctx.createBufferSource(); + noise.buffer = buf; + const bpLo = ctx.createBiquadFilter(); + bpLo.type = 'bandpass'; + bpLo.frequency.setValueAtTime(300 + Math.random() * 200, t); + bpLo.Q.setValueAtTime(6, t); + const bpHi = ctx.createBiquadFilter(); + bpHi.type = 'bandpass'; + bpHi.frequency.setValueAtTime(900 + Math.random() * 500, t); + bpHi.Q.setValueAtTime(4, t); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.001, t); + env.gain.linearRampToValueAtTime(peak, t + 0.004); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.02 + Math.random() * 0.025); + noise.connect(bpLo); + bpLo.connect(env); + noise.connect(bpHi); + bpHi.connect(env); + env.connect(this.gainNode); + noise.start(t); + noise.stop(t + 0.06); + }; + + // Spring lever snap: dull square click collapsing in pitch (60-120 Hz) + const springLever = (t, peak) => { + const osc = ctx.createOscillator(); + osc.type = 'square'; + const f0 = 60 + Math.random() * 60; + osc.frequency.setValueAtTime(f0, t); + osc.frequency.exponentialRampToValueAtTime(f0 * 0.5, t + 0.03); + const env = ctx.createGain(); + env.gain.setValueAtTime(peak, t); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.03 + Math.random() * 0.02); + osc.connect(env); + env.connect(this.gainNode); + osc.start(t); + osc.stop(t + 0.055); + }; + + // Rotary selector clunk: short damped mechanical ping, never a clean beep + const rotaryClunk = (t, peak) => { + const osc = ctx.createOscillator(); + osc.type = 'sine'; + const f0 = 180 + Math.random() * 260; + osc.frequency.setValueAtTime(f0, t); + osc.frequency.exponentialRampToValueAtTime(f0 * 0.7, t + 0.06); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.001, t); + env.gain.linearRampToValueAtTime(peak, t + 0.003); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.06 + Math.random() * 0.08); + osc.connect(env); + env.connect(this.gainNode); + osc.start(t); + osc.stop(t + 0.16); + }; + + for (let i = 0; i < strikeCount; i++) { + const r = Math.random(); + if (r < 0.5) toggleSnap(cursor, 0.08 + Math.random() * 0.08); + else if (r < 0.8) springLever(cursor, 0.07 + Math.random() * 0.06); + else rotaryClunk(cursor, 0.09 + Math.random() * 0.07); + cursor += 0.04 + Math.random() * 0.14; + } + } + + /** + * Bioship Organic Telemetry Engine (audio_gaps.md #47, Phase 3 era) + * Living-ship telemetry is wet and pulse-driven, never electronic: vascular + * surges (30-90 Hz asymmetric sine thumps, sometimes lub-dub double beats), + * chitinous joint snaps, bioplasmic fluid pops and membrane flutters. + * One 0.5-1.1 s phrase of 2-4 events on absolute ctx time. + */ + synthesizeBioshipOrganicTelemetry_() { + const ctx = this.am.ctx; + if (!ctx || !this.gainNode) return; + + const now = ctx.currentTime; + const eventCount = 2 + Math.floor(Math.random() * 3); // 2-4 events + let cursor = Math.random() * 0.2; + + const fireEvent = (t) => { + const r = Math.random(); + if (r < 0.35) { + // Vascular surge: asymmetric sine thump with pitch drop + lub-dub echo + const f0 = 30 + Math.random() * 60; + const dur = 0.2 + Math.random() * 0.15; + const surge = (st, peak, freq) => { + const osc = ctx.createOscillator(); + osc.type = 'sine'; + osc.frequency.setValueAtTime(freq, st); + osc.frequency.linearRampToValueAtTime(freq * 0.82, st + dur * 0.7); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.001, st); + env.gain.linearRampToValueAtTime(peak, st + 0.06 + Math.random() * 0.05); + env.gain.exponentialRampToValueAtTime(0.001, st + dur); + osc.connect(env); + env.connect(this.gainNode); + osc.start(st); + osc.stop(st + dur + 0.05); + }; + surge(t, 0.14 + Math.random() * 0.06, f0); + if (Math.random() < 0.5) { + surge(t + 0.22 + Math.random() * 0.06, 0.09, f0 * 0.9); // lub-dub + } + } else if (r < 0.6) { + // Chitin snap: very short high-Q bandpass noise tick + const buf = this.am.createNoiseBuffer('white', 0.03); + const noise = ctx.createBufferSource(); + noise.buffer = buf; + const bp = ctx.createBiquadFilter(); + bp.type = 'bandpass'; + bp.frequency.setValueAtTime(1200 + Math.random() * 1400, t); + bp.Q.setValueAtTime(10 + Math.random() * 4, t); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.12 + Math.random() * 0.06, t); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.008 + Math.random() * 0.01); + noise.connect(bp); + bp.connect(env); + env.connect(this.gainNode); + noise.start(t); + noise.stop(t + 0.03); + } else if (r < 0.85) { + // Bioplasmic fluid pop: bandpass wet impulse with resonant tail + const buf = this.am.createNoiseBuffer('pink', 0.08); + const noise = ctx.createBufferSource(); + noise.buffer = buf; + const bp = ctx.createBiquadFilter(); + bp.type = 'bandpass'; + bp.frequency.setValueAtTime(500 + Math.random() * 900, t); + bp.frequency.exponentialRampToValueAtTime(380, t + 0.05); + bp.Q.setValueAtTime(3 + Math.random() * 2, t); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.001, t); + env.gain.linearRampToValueAtTime(0.14, t + 0.008); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.05); + noise.connect(bp); + bp.connect(env); + env.connect(this.gainNode); + noise.start(t); + noise.stop(t + 0.09); + } else { + // Membrane flutter: pink noise AM-stepped between 300-700 Hz + const buf = this.am.createNoiseBuffer('pink', 0.2); + const noise = ctx.createBufferSource(); + noise.buffer = buf; + const bp = ctx.createBiquadFilter(); + bp.type = 'bandpass'; + bp.frequency.setValueAtTime(300 + Math.random() * 400, t); + bp.Q.setValueAtTime(2, t); + const env = ctx.createGain(); + const flutterRate = 18 + Math.random() * 12; + const flutterDur = 0.08 + Math.random() * 0.07; + env.gain.setValueAtTime(0.001, t); + let step = 0; + while (step < flutterDur) { + env.gain.linearRampToValueAtTime(0.09 + Math.random() * 0.05, t + step + (1 / flutterRate) * 0.6); + env.gain.linearRampToValueAtTime(0.001, t + step + (1 / flutterRate)); + step += 1 / flutterRate; + } + env.gain.setValueAtTime(0.001, t + flutterDur + 0.02); + noise.connect(bp); + bp.connect(env); + env.connect(this.gainNode); + noise.start(t); + noise.stop(t + flutterDur + 0.05); + } + }; + + for (let i = 0; i < eventCount; i++) { + fireEvent(cursor); + cursor += 0.16 + Math.random() * 0.3; + } + } + + /** + * Belter Jury-Rigged Telemetry Engine (audio_gaps.md #29, Phase 3 era) + * Rattling relays, worn copper contactors and erratic voltage on Ceres / + * Tycho / Rocinante hardware: jittered square pulses with randomized + * micro-dropouts, contact-bounce double clicks, harsh metallic ticks and + * unstable clock pings. One 3-6 event volley on absolute ctx time, with an + * occasional late sub-cluster to mimic an unsteady clock. + */ + synthesizeBelterJuryRiggedTelemetry_() { + const ctx = this.am.ctx; + if (!ctx || !this.gainNode) return; + + const now = ctx.currentTime; + let cursor = 0.05 + Math.random() * 0.25; + + const relayPulse = (t) => { + const osc = ctx.createOscillator(); + osc.type = 'square'; + const baseFreq = 60 + Math.random() * 60; + osc.frequency.setValueAtTime(baseFreq, t); + osc.frequency.linearRampToValueAtTime(baseFreq * 0.8, t + 0.06); + const lp = ctx.createBiquadFilter(); + lp.type = 'lowpass'; + lp.frequency.setValueAtTime(420, t); + const env = ctx.createGain(); + // Erratic voltage: randomized micro-dropouts in the pulse body + env.gain.setValueAtTime(0.15, t); + env.gain.setValueAtTime(0.001, t + 0.012 + Math.random() * 0.015); + env.gain.setValueAtTime(0.12, t + 0.026 + Math.random() * 0.02); + env.gain.setValueAtTime(0.001, t + 0.05 + Math.random() * 0.025); + env.gain.setValueAtTime(0.09, t + 0.07); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.1); + osc.connect(lp); + lp.connect(env); + env.connect(this.gainNode); + osc.start(t); + osc.stop(t + 0.11); + }; + + const contactBounce = (t) => { + const clicks = 2 + (Math.random() < 0.35 ? 1 : 0); + for (let i = 0; i < clicks; i++) { + const ct = t + i * (0.001 + Math.random() * 0.002); + const osc = ctx.createOscillator(); + osc.type = 'square'; + osc.frequency.setValueAtTime(500 + Math.random() * 500, ct); + osc.frequency.exponentialRampToValueAtTime(120, ct + 0.008); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.12, ct); + env.gain.exponentialRampToValueAtTime(0.001, ct + 0.012); + osc.connect(env); + env.connect(this.gainNode); + osc.start(ct); + osc.stop(ct + 0.015); + } + }; + + const metallicTick = (t) => { + const buf = this.am.createNoiseBuffer('white', 0.05); + const noise = ctx.createBufferSource(); + noise.buffer = buf; + const bp = ctx.createBiquadFilter(); + bp.type = 'bandpass'; + bp.frequency.setValueAtTime(400 + Math.random() * 1400, t); + bp.Q.setValueAtTime(6, t); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.08, t); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.02); + noise.connect(bp); + bp.connect(env); + env.connect(this.gainNode); + noise.start(t); + noise.stop(t + 0.025); + }; + + const clockPing = (t) => { + // Unstable clock: dulled square collapsing hard, like a dying oscillator + const osc = ctx.createOscillator(); + osc.type = 'square'; + osc.frequency.setValueAtTime(700 + Math.random() * 400, t); + osc.frequency.exponentialRampToValueAtTime(200, t + 0.025); + const env = ctx.createGain(); + env.gain.setValueAtTime(0.05, t); + env.gain.exponentialRampToValueAtTime(0.001, t + 0.04); + osc.connect(env); + env.connect(this.gainNode); + osc.start(t); + osc.stop(t + 0.045); + }; + + const fireEvent = (t) => { + const r = Math.random(); + if (r < 0.35) relayPulse(t); + else if (r < 0.6) contactBounce(t); + else if (r < 0.85) metallicTick(t); + else clockPing(t); + }; + + const eventCount = 3 + Math.floor(Math.random() * 4); // 3-6 events + for (let i = 0; i < eventCount; i++) { + fireEvent(cursor); + cursor += 0.06 + Math.random() * 0.16; // jittered clock drift + } + // Occasional second volley: the station clock skips then catches up + if (Math.random() < 0.35) { + const volleyBase = cursor + 0.1 + Math.random() * 0.4; + const volleyCount = 2 + Math.floor(Math.random() * 2); + for (let i = 0; i < volleyCount; i++) { + fireEvent(volleyBase + i * (0.04 + Math.random() * 0.1)); + } + } + } + applyPreset(config) { if (config.volume !== undefined) this.params.volume = config.volume; if (config.density !== undefined) this.setDensity(config.density); @@ -1557,7 +1872,6 @@ class WhoniverseAudioSynth { this.gainNode = null; this.activeCloister = false; this.cloisterTimer = null; - this.typeTelemetryTimer = null; } init() { @@ -2096,34 +2410,12 @@ class WhoniverseAudioSynth { } /** - * Ambient Loop: TARDIS Console "Type" Input Telemetry (#25) - * Recursive random-window clatter while any TARDIS console room is engaged. - */ - startConsoleTypeTelemetry(minIntervalMs = 2800, maxIntervalMs = 6500) { - this.stopConsoleTypeTelemetry(); - const scheduleNext = () => { - const delay = minIntervalMs + Math.random() * (maxIntervalMs - minIntervalMs); - this.typeTelemetryTimer = setTimeout(() => { - this.synthesizeTypeClatter(); - scheduleNext(); - }, delay); - }; - scheduleNext(); - } - - stopConsoleTypeTelemetry() { - if (this.typeTelemetryTimer) { - clearTimeout(this.typeTelemetryTimer); - this.typeTelemetryTimer = null; - } - } - - /** - * Stops all automated Whoniverse ambient loops. Cloister bell is an - * interactive toggle and keeps its own explicit call sites. + * Stops all automated Whoniverse ambient loops. Phase 3 moved console + * "type" telemetry (audio_gaps.md #25) into TelemetrySynth era 'who' + * routing, which is stopped via telemetry.stop() / FULL STOP. Hook + * retained because js/app.js calls it at three ambience switch points. */ stopAmbientLoops() { - this.stopConsoleTypeTelemetry(); } } @@ -3995,15 +4287,6 @@ class ExpandedSciFiAudioSynth { } } - /** - * Ambient Loop: Intermittent jury-rigged telemetry on Belter stations - */ - startBelterTelemetry(minIntervalMs = 1800, maxIntervalMs = 4500) { - this._scheduleLoop('belterTelemetry', minIntervalMs, maxIntervalMs, () => { - this.synthesizeBelterTelemetryJitter(); - }); - } - /** * True Hull Strain Moan (Deep Space, audio_gaps.md #30) * Massive structural groan of stressed bulkheads flexing under gravitational diff --git a/js/config.js b/js/config.js index 78cfbe7..cee6abb 100644 --- a/js/config.js +++ b/js/config.js @@ -211,7 +211,7 @@ const UniverseRegistry = { hull: { volume: 0.5, baseFreq: 68, filterCutoff: 150, resonance: 2.8, noiseMix: 0.35, harmonicSpread: 1.04 }, warp: { volume: 0.65, bpm: 52, carrierFreq: 96, filterCutoff: 210, pulseShape: 'tos', resonance: 3.5, swirlMix: 0.2 }, lifeSupport: { volume: 0.45, noiseType: 'pink', highpassFreq: 220, lowpassFreq: 1800, airflowModSpeed: 0.08, airflowModDepth: 0.06 }, - telemetry: { volume: 0.55, density: 0.6, era: 'tos' } + telemetry: { volume: 0.55, density: 0.6, era: 'who' } }, 'tardis-victorian': { id: 'tardis-victorian', @@ -223,7 +223,7 @@ const UniverseRegistry = { hull: { volume: 0.65, baseFreq: 44, filterCutoff: 85, resonance: 1.8, noiseMix: 0.45, harmonicSpread: 1.015 }, warp: { volume: 0.3, bpm: 40, carrierFreq: 50, filterCutoff: 120, pulseShape: 'nx', resonance: 2.0, swirlMix: 0.15 }, lifeSupport: { volume: 0.55, noiseType: 'brown', highpassFreq: 110, lowpassFreq: 1200, airflowModSpeed: 0.07, airflowModDepth: 0.12 }, - telemetry: { volume: 0.35, density: 0.4, era: 'nx' } + telemetry: { volume: 0.35, density: 0.4, era: 'who' } }, 'tardis-coral': { id: 'tardis-coral', @@ -235,7 +235,7 @@ const UniverseRegistry = { hull: { volume: 0.75, baseFreq: 52, filterCutoff: 135, resonance: 3.2, noiseMix: 0.5, harmonicSpread: 1.03 }, warp: { volume: 0.9, bpm: 48, carrierFreq: 64, filterCutoff: 230, pulseShape: 'tng', resonance: 4.0, swirlMix: 0.45 }, lifeSupport: { volume: 0.6, noiseType: 'pink', highpassFreq: 160, lowpassFreq: 1700, airflowModSpeed: 0.18, airflowModDepth: 0.15 }, - telemetry: { volume: 0.45, density: 0.5, era: 'tng' } + telemetry: { volume: 0.45, density: 0.5, era: 'who' } }, 'tardis-copper': { id: 'tardis-copper', @@ -247,7 +247,7 @@ const UniverseRegistry = { hull: { volume: 0.6, baseFreq: 56, filterCutoff: 125, resonance: 2.5, noiseMix: 0.4, harmonicSpread: 1.02 }, warp: { volume: 0.7, bpm: 62, carrierFreq: 72, filterCutoff: 210, pulseShape: 'voyager', resonance: 3.6, swirlMix: 0.35 }, lifeSupport: { volume: 0.65, noiseType: 'white', highpassFreq: 220, lowpassFreq: 2200, airflowModSpeed: 0.15, airflowModDepth: 0.1 }, - telemetry: { volume: 0.55, density: 0.7, era: 'voyager' } + telemetry: { volume: 0.55, density: 0.7, era: 'who' } }, 'tardis-cold-machine': { id: 'tardis-cold-machine', @@ -259,7 +259,7 @@ const UniverseRegistry = { hull: { volume: 0.6, baseFreq: 50, filterCutoff: 110, resonance: 2.2, noiseMix: 0.38, harmonicSpread: 1.02 }, warp: { volume: 0.75, bpm: 56, carrierFreq: 68, filterCutoff: 190, pulseShape: 'tng', resonance: 3.2, swirlMix: 0.3 }, lifeSupport: { volume: 0.5, noiseType: 'pink', highpassFreq: 200, lowpassFreq: 1600, airflowModSpeed: 0.12, airflowModDepth: 0.08 }, - telemetry: { volume: 0.4, density: 0.5, era: 'ds9' } + telemetry: { volume: 0.4, density: 0.5, era: 'who' } }, 'tardis-crystal': { id: 'tardis-crystal', @@ -271,7 +271,7 @@ const UniverseRegistry = { hull: { volume: 0.7, baseFreq: 46, filterCutoff: 100, resonance: 3.5, noiseMix: 0.5, harmonicSpread: 1.04 }, warp: { volume: 0.8, bpm: 44, carrierFreq: 88, filterCutoff: 260, pulseShape: 'defiant', resonance: 4.4, swirlMix: 0.4 }, lifeSupport: { volume: 0.55, noiseType: 'pink', highpassFreq: 180, lowpassFreq: 1900, airflowModSpeed: 0.16, airflowModDepth: 0.14 }, - telemetry: { volume: 0.5, density: 0.6, era: 'voyager' } + telemetry: { volume: 0.5, density: 0.6, era: 'who' } }, 'tardis-infinite-white': { id: 'tardis-infinite-white', @@ -283,7 +283,7 @@ const UniverseRegistry = { hull: { volume: 0.65, baseFreq: 48, filterCutoff: 95, resonance: 2.0, noiseMix: 0.42, harmonicSpread: 1.01 }, warp: { volume: 0.6, bpm: 50, carrierFreq: 60, filterCutoff: 160, pulseShape: 'tng', resonance: 2.8, swirlMix: 0.3 }, lifeSupport: { volume: 0.7, noiseType: 'white', highpassFreq: 160, lowpassFreq: 2600, airflowModSpeed: 0.14, airflowModDepth: 0.12 }, - telemetry: { volume: 0.45, density: 0.6, era: 'tng' } + telemetry: { volume: 0.45, density: 0.6, era: 'who' } } } }, @@ -371,7 +371,7 @@ const UniverseRegistry = { hull: { volume: 0.8, baseFreq: 64, filterCutoff: 145, resonance: 3.4, noiseMix: 0.55, harmonicSpread: 1.035 }, warp: { volume: 0.9, bpm: 58, carrierFreq: 84, filterCutoff: 250, pulseShape: 'defiant', resonance: 4.2, swirlMix: 0.4 }, lifeSupport: { volume: 0.6, noiseType: 'white', highpassFreq: 200, lowpassFreq: 2100, airflowModSpeed: 0.18, airflowModDepth: 0.14 }, - telemetry: { volume: 0.5, density: 0.65, era: 'voyager' } + telemetry: { volume: 0.5, density: 0.75, era: 'belter' } }, 'ind-eagle': { id: 'ind-eagle', @@ -429,7 +429,7 @@ const UniverseRegistry = { hull: { volume: 0.75, baseFreq: 45, filterCutoff: 110, resonance: 3.2, noiseMix: 0.5, harmonicSpread: 1.03 }, warp: { volume: 0.85, bpm: 38, carrierFreq: 58, filterCutoff: 180, pulseShape: 'tng', resonance: 4.2, swirlMix: 0.45 }, lifeSupport: { volume: 0.65, noiseType: 'pink', highpassFreq: 150, lowpassFreq: 1600, airflowModSpeed: 0.08, airflowModDepth: 0.2 }, - telemetry: { volume: 0.4, density: 0.4, era: 'tng' } + telemetry: { volume: 0.4, density: 0.4, era: 'bioship' } }, 'bio-talyn': { id: 'bio-talyn', @@ -439,7 +439,7 @@ const UniverseRegistry = { hull: { volume: 0.8, baseFreq: 58, filterCutoff: 140, resonance: 3.6, noiseMix: 0.52, harmonicSpread: 1.04 }, warp: { volume: 0.9, bpm: 64, carrierFreq: 76, filterCutoff: 240, pulseShape: 'defiant', resonance: 4.6, swirlMix: 0.4 }, lifeSupport: { volume: 0.55, noiseType: 'pink', highpassFreq: 180, lowpassFreq: 1800, airflowModSpeed: 0.16, airflowModDepth: 0.16 }, - telemetry: { volume: 0.45, density: 0.5, era: 'ds9' } + telemetry: { volume: 0.45, density: 0.5, era: 'bioship' } }, 'bio-lexx': { id: 'bio-lexx', @@ -449,7 +449,7 @@ const UniverseRegistry = { hull: { volume: 0.85, baseFreq: 38, filterCutoff: 90, resonance: 2.8, noiseMix: 0.6, harmonicSpread: 1.02 }, warp: { volume: 0.7, bpm: 34, carrierFreq: 48, filterCutoff: 140, pulseShape: 'nx', resonance: 3.4, swirlMix: 0.35 }, lifeSupport: { volume: 0.6, noiseType: 'brown', highpassFreq: 120, lowpassFreq: 1300, airflowModSpeed: 0.06, airflowModDepth: 0.22 }, - telemetry: { volume: 0.35, density: 0.3, era: 'nx' } + telemetry: { volume: 0.35, density: 0.4, era: 'bioship' } }, 'bio-vorlon': { id: 'bio-vorlon', @@ -459,7 +459,7 @@ const UniverseRegistry = { hull: { volume: 0.65, baseFreq: 74, filterCutoff: 160, resonance: 3.8, noiseMix: 0.35, harmonicSpread: 1.05 }, warp: { volume: 0.8, bpm: 52, carrierFreq: 104, filterCutoff: 260, pulseShape: 'voyager', resonance: 4.5, swirlMix: 0.5 }, lifeSupport: { volume: 0.5, noiseType: 'pink', highpassFreq: 220, lowpassFreq: 2200, airflowModSpeed: 0.12, airflowModDepth: 0.1 }, - telemetry: { volume: 0.5, density: 0.6, era: 'voyager' } + telemetry: { volume: 0.5, density: 0.6, era: 'bioship' } }, 'bio-wraith': { id: 'bio-wraith', @@ -469,7 +469,7 @@ const UniverseRegistry = { hull: { volume: 0.85, baseFreq: 34, filterCutoff: 85, resonance: 2.5, noiseMix: 0.65, harmonicSpread: 1.015 }, warp: { volume: 0.6, bpm: 32, carrierFreq: 44, filterCutoff: 120, pulseShape: 'nx', resonance: 3.0, swirlMix: 0.25 }, lifeSupport: { volume: 0.65, noiseType: 'brown', highpassFreq: 90, lowpassFreq: 900, airflowModSpeed: 0.06, airflowModDepth: 0.22 }, - telemetry: { volume: 0.3, density: 0.25, era: 'nx' } + telemetry: { volume: 0.3, density: 0.4, era: 'bioship' } }, 'bio-species-8472': { id: 'bio-species-8472', @@ -479,7 +479,7 @@ const UniverseRegistry = { hull: { volume: 0.7, baseFreq: 68, filterCutoff: 150, resonance: 3.5, noiseMix: 0.45, harmonicSpread: 1.035 }, warp: { volume: 0.85, bpm: 56, carrierFreq: 88, filterCutoff: 230, pulseShape: 'defiant', resonance: 4.4, swirlMix: 0.45 }, lifeSupport: { volume: 0.5, noiseType: 'pink', highpassFreq: 200, lowpassFreq: 1900, airflowModSpeed: 0.14, airflowModDepth: 0.12 }, - telemetry: { volume: 0.45, density: 0.55, era: 'voyager' } + telemetry: { volume: 0.45, density: 0.55, era: 'bioship' } } } }, @@ -939,7 +939,7 @@ const UniverseRegistry = { hull: { volume: 0.8, baseFreq: 48, filterCutoff: 110, resonance: 2.8, noiseMix: 0.55, harmonicSpread: 1.025 }, warp: { volume: 0.65, bpm: 42, carrierFreq: 66, filterCutoff: 180, pulseShape: 'defiant', resonance: 3.5, swirlMix: 0.3 }, lifeSupport: { volume: 0.65, noiseType: 'pink', highpassFreq: 160, lowpassFreq: 1700, airflowModSpeed: 0.12, airflowModDepth: 0.14 }, - telemetry: { volume: 0.45, density: 0.5, era: 'voyager' } + telemetry: { volume: 0.45, density: 0.65, era: 'belter' } }, 'sta-ceres': { id: 'sta-ceres', @@ -949,7 +949,7 @@ const UniverseRegistry = { hull: { volume: 0.85, baseFreq: 42, filterCutoff: 95, resonance: 2.6, noiseMix: 0.62, harmonicSpread: 1.02 }, warp: { volume: 0.45, bpm: 38, carrierFreq: 54, filterCutoff: 135, pulseShape: 'nx', resonance: 3.0, swirlMix: 0.2 }, lifeSupport: { volume: 0.7, noiseType: 'brown', highpassFreq: 120, lowpassFreq: 1400, airflowModSpeed: 0.08, airflowModDepth: 0.16 }, - telemetry: { volume: 0.35, density: 0.35, era: 'nx' } + telemetry: { volume: 0.35, density: 0.6, era: 'belter' } }, 'sta-gateway': { id: 'sta-gateway',