Deterministic 10Hz sim (power/breaker, spatial heat, uplink bandwidth, train-vs-infer economy), Pixi isometric renderer over MODELBEAST-generated room + sprites, DOM HUD with SlopTube feed, save/load, company naming, WebAudio SFX. Slop thumbnails are genuine diffusion slop; low model tiers publish deranged qwen-7B titles, training buys coherence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
// Tiny WebAudio synth — no asset files. All sounds built from oscillators/noise.
|
|
let ctx: AudioContext | null = null;
|
|
let muted = localStorage.getItem("gigaslop-muted") === "1";
|
|
|
|
function ac(): AudioContext | null {
|
|
if (muted) return null;
|
|
if (!ctx) ctx = new AudioContext();
|
|
if (ctx.state === "suspended") ctx.resume();
|
|
return ctx;
|
|
}
|
|
|
|
export function setMuted(m: boolean) {
|
|
muted = m;
|
|
localStorage.setItem("gigaslop-muted", m ? "1" : "0");
|
|
}
|
|
export function isMuted() {
|
|
return muted;
|
|
}
|
|
|
|
function tone(freq: number, dur: number, type: OscillatorType, vol: number, when = 0, glideTo?: number) {
|
|
const c = ac();
|
|
if (!c) return;
|
|
const t0 = c.currentTime + when;
|
|
const o = c.createOscillator();
|
|
const g = c.createGain();
|
|
o.type = type;
|
|
o.frequency.setValueAtTime(freq, t0);
|
|
if (glideTo) o.frequency.exponentialRampToValueAtTime(glideTo, t0 + dur);
|
|
g.gain.setValueAtTime(vol, t0);
|
|
g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
|
|
o.connect(g).connect(c.destination);
|
|
o.start(t0);
|
|
o.stop(t0 + dur + 0.02);
|
|
}
|
|
|
|
function noise(dur: number, vol: number, lowpass = 800) {
|
|
const c = ac();
|
|
if (!c) return;
|
|
const buf = c.createBuffer(1, c.sampleRate * dur, c.sampleRate);
|
|
const d = buf.getChannelData(0);
|
|
for (let i = 0; i < d.length; i++) d[i] = (Math.random() * 2 - 1) * (1 - i / d.length);
|
|
const src = c.createBufferSource();
|
|
src.buffer = buf;
|
|
const f = c.createBiquadFilter();
|
|
f.type = "lowpass";
|
|
f.frequency.value = lowpass;
|
|
const g = c.createGain();
|
|
g.gain.value = vol;
|
|
src.connect(f).connect(g).connect(c.destination);
|
|
src.start();
|
|
}
|
|
|
|
export const sfx = {
|
|
click: () => tone(880, 0.05, "square", 0.04),
|
|
place: () => { noise(0.12, 0.25, 500); tone(140, 0.1, "sine", 0.15); },
|
|
sell: () => tone(520, 0.12, "triangle", 0.08, 0, 260),
|
|
wire: () => { tone(660, 0.06, "square", 0.05); tone(990, 0.06, "square", 0.05, 0.07); },
|
|
publish: () => { tone(523, 0.08, "sine", 0.07); tone(784, 0.12, "sine", 0.07, 0.08); },
|
|
viral: () => [523, 659, 784, 1047, 1319].forEach((f, i) => tone(f, 0.18, "triangle", 0.09, i * 0.07)),
|
|
tierUp: () => [392, 523, 659, 784].forEach((f, i) => tone(f, 0.3, "sawtooth", 0.05, i * 0.12)),
|
|
breaker: () => { noise(0.4, 0.5, 250); tone(80, 0.5, "sawtooth", 0.2, 0, 35); },
|
|
error: () => tone(180, 0.15, "square", 0.06),
|
|
};
|