pike/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files 01f4d8d8c48d3f5e632a40decfe9db183ebed99e
1
/**
2
 * The spike, out loud.
3
 *
4
 * One oscillator per beep, scheduled by how much fuse is left: a slow tick
5
 * when it is planted, a stutter when it is about to go. No audio files, and
6
 * nothing is created until the person has clicked the page — a browser will
7
 * not start an audio context before that, and should not.
8
 */
9
10
/** Seconds between beeps for this much fuse left. */
11
export function beepInterval(fuse) {
12
  if (fuse <= 0) return 0;
13
  return Math.max(0.12, Math.min(1, fuse / 30));
14
}
15
16
/** The pitch rises as the fuse burns, so it is audible without watching. */
17
export const beepPitch = (fuse) => 760 + 620 * (1 - Math.min(1, fuse / 45));
18
19
export function makeBeeper() {
20
  let audio = null;
21
  let next = 0;
22
23
  const context = () => {
24
    if (audio) return audio;
25
    const Ctor = globalThis.AudioContext || globalThis.webkitAudioContext;
26
    if (!Ctor) return null;
27
    audio = new Ctor();
28
    return audio;
29
  };
30
31
  return {
32
    /** Called on a click: browsers only allow audio to start from a gesture. */
33
    wake() {
34
      const ctx = context();
35
      if (ctx && ctx.state === "suspended") ctx.resume();
36
    },
37
    /** Called every frame with the fuse the server last sent. */
38
    update(planted, fuse, now) {
39
      if (!planted || fuse <= 0) {
40
        next = 0;
41
        return;
42
      }
43
      if (!audio) return; // no gesture yet; nothing to play into
44
      if (now < next) return;
45
      next = now + beepInterval(fuse);
46
      beep(audio, beepPitch(fuse));
47
    },
48
  };
49
}
50
51
function beep(audio, hz) {
52
  const osc = audio.createOscillator();
53
  const gain = audio.createGain();
54
  osc.type = "square";
55
  osc.frequency.value = hz;
56
  gain.gain.setValueAtTime(0.0001, audio.currentTime);
57
  gain.gain.exponentialRampToValueAtTime(0.09, audio.currentTime + 0.01);
58
  gain.gain.exponentialRampToValueAtTime(0.0001, audio.currentTime + 0.09);
59
  osc.connect(gain).connect(audio.destination);
60
  osc.start();
61
  osc.stop(audio.currentTime + 0.1);
62
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts