Echo / src /audio /Cheer.js
mlmihjaz's picture
Initial commit: ECHO 3D vocab game with Node+SQLite backend, admin dashboard, Docker deploy
173e683
Raw
History Blame Contribute Delete
1.24 kB
/**
* Cheer — Web Audio chimes and arpeggios used on correct answers / mastery / level-up.
*/
let ctx = null;
function ensure() { if (!ctx) { try { ctx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) {} } return ctx; }
function tone(c, freq, when, dur = 0.45, gain = 0.18, type = 'triangle') {
const o = c.createOscillator(), g = c.createGain();
o.type = type; o.frequency.value = freq;
g.gain.setValueAtTime(0, when);
g.gain.linearRampToValueAtTime(gain, when + 0.02);
g.gain.exponentialRampToValueAtTime(0.001, when + dur);
o.connect(g).connect(c.destination);
o.start(when); o.stop(when + dur + 0.05);
}
export const Cheer = {
enabled: true,
prime() { ensure(); },
small() {
if (!this.enabled) return;
const c = ensure(); if (!c) return;
const now = c.currentTime;
[523.25, 659.25, 783.99, 1046.5].forEach((f, i) => tone(c, f, now + i * 0.08));
},
master() {
if (!this.enabled) return;
const c = ensure(); if (!c) return;
const now = c.currentTime;
[523.25, 659.25, 783.99, 1046.5, 1318.51, 1567.98, 2093.0].forEach((f, i) =>
tone(c, f, now + i * 0.07, 0.6, 0.22, i % 2 ? 'sine' : 'triangle')
);
},
levelUp() {
this.master();
},
};