change to fat synth, finetune parameters

This commit is contained in:
2025-12-29 13:05:39 +02:00
parent df43d3b476
commit 0e1bff8259
4 changed files with 26 additions and 24 deletions

View File

@@ -9,7 +9,7 @@ export function createNoiseSynth(volume: number): Tone.NoiseSynth {
attack: 0.005, attack: 0.005,
decay: 0.1, decay: 0.1,
sustain: 0, sustain: 0,
release: 0.1 release: 0.3
}, },
volume: volume volume: volume
}); });

View File

@@ -3,14 +3,16 @@ import * as Tone from 'tone';
export function createPadSynth(isDay: boolean): Tone.PolySynth { export function createPadSynth(isDay: boolean): Tone.PolySynth {
return new Tone.PolySynth(Tone.Synth, { return new Tone.PolySynth(Tone.Synth, {
oscillator: { oscillator: {
type: isDay ? 'triangle' : 'sine' type: 'fatsine', // Fat oscillator creates multiple detuned voices for lush sound
count: 3, // Number of detuned oscillators
spread: 30 // Amount of detune in cents for width and movement
}, },
envelope: { envelope: {
attack: 1.5, attack: isDay ? 2.0 : 3.0, // Longer, smoother attack
decay: 1, decay: 1.5,
sustain: 0.7, sustain: 0.85, // Higher sustain for consistent pad presence
release: 1.0 release: isDay ? 2.5 : 4.0 // Longer release for smooth tail-off
}, },
volume: -20 volume: -24 // Quieter to sit back in the mix and reduce mid heaviness
}); });
} }

View File

@@ -217,7 +217,7 @@
onclick={togglePlayback} onclick={togglePlayback}
disabled={!isInitialized} disabled={!isInitialized}
> >
{isPlaying ? 'Stop' : 'Start'} Air Quality Monitor {isPlaying ? 'Stop' : 'Start'} Air Quality Noise
</button> </button>
{#if isPlaying} {#if isPlaying}

View File

@@ -42,7 +42,6 @@
let reverb: Tone.Reverb | null = null; let reverb: Tone.Reverb | null = null;
let delay: Tone.FeedbackDelay | null = null; let delay: Tone.FeedbackDelay | null = null;
let filter: Tone.Filter | null = null; let filter: Tone.Filter | null = null;
let phaser: Tone.Phaser | null = null;
let sequence: Tone.Sequence | null = null; let sequence: Tone.Sequence | null = null;
let gain: Tone.Gain | null = null; let gain: Tone.Gain | null = null;
let analyser: Tone.Analyser | null = null; let analyser: Tone.Analyser | null = null;
@@ -62,11 +61,9 @@
// Derived reactive values using runes with safe fallbacks // Derived reactive values using runes with safe fallbacks
const bpm = $derived.by(() => { const bpm = $derived.by(() => {
const temp = temperature2m ?? 20; const temp = temperature2m ?? 20;
// BPM starts at 10 for 0°C and increases with temperature // BPM: 5 at 0°C or below, 30 at 30°C or above
// Day: more energetic (2x scaling), Night: calmer (1x scaling) const normalizedTemp = Math.max(0, Math.min(30, temp));
const tempAboveZero = Math.max(0, temp); return 5 + (normalizedTemp / 30) * 25;
const scaledBpm = isDay ? 10 + tempAboveZero * 2 : 10 + tempAboveZero;
return Math.max(10, Math.min(200, scaledBpm));
}); });
const reverbWet = $derived.by(() => { const reverbWet = $derived.by(() => {
@@ -88,11 +85,12 @@
return Math.max(0.2, Math.min(0.7, (speed / 20) * 0.5 + 0.2)); return Math.max(0.2, Math.min(0.7, (speed / 20) * 0.5 + 0.2));
}); });
// Filter cutoff: more clouds = darker/lower frequency // Filter cutoff: colder = darker/lower frequency, warmer = brighter/higher frequency
const filterCutoff = $derived.by(() => { const filterCutoff = $derived.by(() => {
const cover = cloudCover ?? 30; const temp = temperature2m ?? 20;
// Map cloud cover: 0% clouds = 8000Hz (bright), 100% clouds = 400Hz (dark) // Map temperature: 0°C = 400Hz (dark), 30°C = 8000Hz (bright)
return Math.max(400, Math.min(8000, 8000 - (cover / 100) * 7600)); const normalizedTemp = Math.max(0, Math.min(30, temp));
return 400 + (normalizedTemp / 30) * 7600;
}); });
// Filter resonance: more wind = more resonant // Filter resonance: more wind = more resonant
@@ -167,9 +165,11 @@
analyser = createAnalyser(); analyser = createAnalyser();
// Connect audio chain using .chain() for clarity // Connect audio chain using .chain() for clarity
// Synth gets filtered based on temperature
synth.chain(filter, delay, reverb, gain, analyser, Tone.Destination); synth.chain(filter, delay, reverb, gain, analyser, Tone.Destination);
arpSynth.chain(filter, delay, reverb, gain); // Arpeggios bypass filter - only delay and reverb
pingSynth.chain(filter, delay, reverb, gain); arpSynth.chain(delay, reverb, gain);
pingSynth.chain(delay, reverb, gain);
bassSynth.chain(delay, reverb, gain); bassSynth.chain(delay, reverb, gain);
// Generate reverb impulse // Generate reverb impulse
@@ -432,10 +432,10 @@
}); });
</script> </script>
<div class="grid grid-cols-1 md:grid-cols-[300px_1fr] gap-8 p-4 max-w-6xl mx-auto"> <div class="mx-auto grid max-w-6xl grid-cols-1 gap-8 p-4 md:grid-cols-[300px_1fr]">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<button <button
class="px-6 py-3 text-base cursor-crosshair transition-all duration-300 rounded-md border border-white/20 hover:border-white/40 disabled:opacity-50 disabled:cursor-not-allowed {isPlaying class="cursor-crosshair rounded-md border border-white/20 px-6 py-3 text-base transition-all duration-300 hover:border-white/40 disabled:cursor-not-allowed disabled:opacity-50 {isPlaying
? 'bg-white text-black' ? 'bg-white text-black'
: 'bg-transparent text-white'}" : 'bg-transparent text-white'}"
onclick={togglePlayback} onclick={togglePlayback}
@@ -450,8 +450,8 @@
<p class="m-0">Humidity: {relativeHumidity2m}%</p> <p class="m-0">Humidity: {relativeHumidity2m}%</p>
<p class="m-0">Cloud Cover: {cloudCover}%</p> <p class="m-0">Cloud Cover: {cloudCover}%</p>
<p class="m-0">Wind Speed: {windSpeed10m.toFixed(1)} m/s</p> <p class="m-0">Wind Speed: {windSpeed10m.toFixed(1)} m/s</p>
<p class="opacity-40 my-1">---</p> <p class="my-1 opacity-40">---</p>
<p class="m-0">BPM: {bpm}</p> <p class="m-0">BPM: {bpm.toFixed(2)}</p>
<p class="m-0">Weather Extremity: {(weatherExtremity * 100).toFixed(0)}%</p> <p class="m-0">Weather Extremity: {(weatherExtremity * 100).toFixed(0)}%</p>
<p class="m-0">Reverb: {reverbWet.toFixed(2)}</p> <p class="m-0">Reverb: {reverbWet.toFixed(2)}</p>
<p class="m-0">Delay: {delayTime} @ {delayFeedback.toFixed(2)} feedback</p> <p class="m-0">Delay: {delayTime} @ {delayFeedback.toFixed(2)} feedback</p>