Back to Soybert 3D
SOURCE / PINNED RELEASE

Made of little things.

Soybert 3D

Release
4bf80e758af1…
Author-recorded commit
298fa2d0e8b1…
License
LICENSE
Author’s source reference
nostr://npub1n8ga89w8h6tvwamxusfyzexw8gjy84yxu9rxgnmk955cxtml4ujswzxydd/wss%3A%2F%2Fgit.napplet.soy%2F/n-9021e1b6a96

Archive hash verified: 7083223a53bbd320…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/audio.ts
/** Short synthesized effects: no media files, downloads, or autoplay. */
export class Sound {
  enabled = true;
  private context?: AudioContext;
  unlock() {
    if (!this.enabled) return;
    try { this.context ??= new AudioContext(); void this.context.resume().catch(() => undefined); } catch { /* silent fallback */ }
  }
  play(kind: 'jump' | 'shoot' | 'land' | 'celebrate') {
    if (!this.enabled || !this.context || this.context.state !== 'running') return;
    const c = this.context, now = c.currentTime;
    if (kind === 'shoot') {
      const buffer = c.createBuffer(1, c.sampleRate * .19, c.sampleRate);
      const channel = buffer.getChannelData(0);
      for (let i = 0; i < channel.length; i++) channel[i] = (Math.random() * 2 - 1) * (1 - i / channel.length) ** 3;
      const source = c.createBufferSource(); source.buffer = buffer;
      const filter = c.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 1700;
      const gain = c.createGain(); gain.gain.value = .27;
      source.connect(filter).connect(gain).connect(c.destination); source.start();
      source.onended = () => { source.disconnect(); filter.disconnect(); gain.disconnect(); };
    }
    const notes = kind === 'celebrate' ? [523, 659, 784, 1047] : [kind === 'jump' ? 250 : kind === 'land' ? 90 : 70];
    notes.forEach((frequency, i) => {
      const oscillator = c.createOscillator(), gain = c.createGain(), start = now + i * .105;
      oscillator.type = kind === 'shoot' ? 'triangle' : 'sine';
      oscillator.frequency.setValueAtTime(frequency, start);
      oscillator.frequency.exponentialRampToValueAtTime(kind === 'jump' ? 580 : frequency * .7, start + .17);
      gain.gain.setValueAtTime(.001, start); gain.gain.linearRampToValueAtTime(.12, start + .012); gain.gain.exponentialRampToValueAtTime(.001, start + .2);
      oscillator.connect(gain).connect(c.destination); oscillator.start(start); oscillator.stop(start + .22);
      oscillator.onended = () => { oscillator.disconnect(); gain.disconnect(); };
    });
  }
  dispose() { void this.context?.close().catch(() => undefined); }
}