JSFiddle - React, Tailwind, and code Playground

by Vasil Svetoslavov

HTML

<button onclick="sirenWail()">Wail</button>
<button onclick="sirenYelp()">Yelp</button>
<button onclick="sirenHiLo()">Hi-Lo</button>
<button onclick="sirenPiercer()">Piercer</button>
<button onclick="sirenPhaser()">Phaser</button>
<button onclick="stopSiren()">Stop</button>

<script>
/**
 * Simple siren synth using Web Audio.
 * Each siren uses frequency automation to make patterns.
 */
</script>

JavaScript

let _ctx = null;
let _osc = null;
let _gain = null;

function _ensureAudio() {
  if (!_ctx || _ctx.state === "closed") {
    _ctx = new (window.AudioContext || window.webkitAudioContext)();
  }
  return _ctx;
}

function stopSiren() {
  if (!_ctx) return;
  const now = _ctx.currentTime;

  if (_gain) {
    // quick fade out
    _gain.gain.cancelScheduledValues(now);
    _gain.gain.setValueAtTime(_gain.gain.value, now);
    _gain.gain.linearRampToValueAtTime(0.0001, now + 0.06);
  }

  if (_osc) {
    try { _osc.stop(now + 0.08); } catch {}
  }

  _osc = null;
  _gain = null;

  // Optional: keep ctx alive for faster restart. Close if you want:
  // _ctx.close(); _ctx = null;
}

function _startVoice({ type = "sawtooth", volume = 0.12 } = {}) {
  stopSiren(); // only one siren at a time

  const ctx = _ensureAudio();
  const now = ctx.currentTime;

  const osc = ctx.createOscillator();
  const gain = ctx.createGain();

  osc.type = type;
  gain.gain.setValueAtTime(0.0001, now);
  gain.gain.linearRampToValueAtTime(volume, now + 0.03);

  osc.connect(gain);
  gain.connect(ctx.destination);

  osc.start(now);

  _osc = osc;
  _gain = gain;

  return { ctx, osc, gain, now };
}

/**
 * 1) WAIL (slow rise/fall)
 * Very common “long” siren. Typically ~500–1500 Hz sweep.
 */
function sirenWail(durationMs = 6000) {
  const { ctx, osc, now } = _startVoice({ type: "sawtooth", volume: 0.12 });

  const t0 = now;
  const tEnd = now + durationMs / 1000;

  osc.frequency.setValueAtTime(650, t0);

  // Repeating slow up/down ramps
  let t = t0;
  const up = 1.6;
  const down = 1.6;

  while (t + up + down < tEnd) {
    osc.frequency.linearRampToValueAtTime(1500, t + up);
    osc.frequency.linearRampToValueAtTime(650, t + up + down);
    t += up + down;
  }

  // auto stop
  setTimeout(stopSiren, durationMs);
}

/**
 * 2) YELP (fast wail)
 * The urgent “whoop-whoop” vibe, quicker sweeps.
...