Ambient: Zugfahrt „klack-klack … klack-klack“ (Drift v8, Diff eingespielt); sw v81
Some checks failed
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Has been cancelled

Zwei Achsen kurz nacheinander (3,1 m), 33 m Schiene bis zum nächsten Paar, dumpferer
Anschlag, Karosserie klingt mit zwei Moden nach. Dioras eigene Drift-Änderungen bleiben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrwnslz 2026-10-04 00:24:43 +02:00
parent 6e28312947
commit 356a5299da
2 changed files with 19 additions and 15 deletions

View file

@ -926,22 +926,26 @@ function buildWomb(K) {
K.set = (a, b) => { sm(tl.frequency, 100 + 300 * b); sm(lp.frequency, 300 + 500 * b); }; K.set = (a, b) => { sm(tl.frequency, 100 + 300 * b); sm(lp.frequency, 300 + 500 * b); };
} }
// Zugfahrt: Rollgeräusch + Schienenstöße. Jeder Stoß wird von vier Achsen nacheinander angeschlagen // Zugfahrt: Rollgeräusch + Schienenstöße. Jeder Stoß wird von zwei Achsen kurz nacheinander angeschlagen
// (zwei pro Drehgestell, Abstand = halbe Schienenlänge) -> gleichmäßig "tschuck-tschuck ... tschuck-tschuck", Takt folgt der Geschwindigkeit. // (Achsabstand 3,1 m, Schienenlänge 33 m) -> "klack-klack ......... klack-klack", Pause ≈ 11× Paarabstand, Takt folgt der Geschwindigkeit.
function railClack(K, t, amp) { function railClack(K, t, amp) {
const ctx = S.ctx, nb = noiseBuf("white"); const ctx = S.ctx, nb = noiseBuf("white");
const s1 = ctx.createBufferSource(), bp = bq("bandpass", R(500, 1100) * (.7 + .8 * K.b), .9), g1 = ctx.createGain(); const s1 = ctx.createBufferSource(), bp = bq("bandpass", R(300, 650) * (.7 + .8 * K.b), .8), g1 = ctx.createGain(); // dumpfer Anschlag
s1.buffer = nb; s1.connect(bp).connect(g1).connect(K.out); s1.buffer = nb; s1.connect(bp).connect(g1).connect(K.out);
g1.gain.setValueAtTime(0, t); g1.gain.linearRampToValueAtTime(amp * .9, t + .003); g1.gain.exponentialRampToValueAtTime(.001, t + .07); g1.gain.setValueAtTime(0, t); g1.gain.linearRampToValueAtTime(amp * .55, t + .004); g1.gain.exponentialRampToValueAtTime(.001, t + .08);
s1.start(t, R(0, 8), .1); s1.start(t, R(0, 8), .1);
const o = ctx.createOscillator(), og = ctx.createGain(); // dumpfer Schlag im Wagenboden const o = ctx.createOscillator(), og = ctx.createGain(); // Schlag im Wagenboden
o.frequency.setValueAtTime(110, t); o.frequency.exponentialRampToValueAtTime(55, t + .1); o.frequency.setValueAtTime(95, t); o.frequency.exponentialRampToValueAtTime(48, t + .12);
og.gain.setValueAtTime(0, t); og.gain.linearRampToValueAtTime(amp * .7, t + .004); og.gain.exponentialRampToValueAtTime(.001, t + .16); og.gain.setValueAtTime(0, t); og.gain.linearRampToValueAtTime(amp * .8, t + .005); og.gain.exponentialRampToValueAtTime(.001, t + .24);
o.connect(og).connect(K.out); o.start(t); o.stop(t + .2); o.connect(og).connect(K.out); o.start(t); o.stop(t + .3);
const s2 = ctx.createBufferSource(), rb = bq("bandpass", R(190, 260), 8), g2 = ctx.createGain(); // Nachklingen der Karosserie // Karosserie klingt nach: zwei Resonanzmoden, lang abklingend
s2.buffer = nb; s2.connect(rb).connect(g2).connect(K.out); const f1 = R(170, 220);
g2.gain.setValueAtTime(0, t); g2.gain.linearRampToValueAtTime(amp * .9, t + .004); g2.gain.exponentialRampToValueAtTime(.001, t + .35); [[f1, 1.5, .65], [f1 * 1.9, .7, .45]].forEach(([f, a, dec]) => {
s2.start(t, R(0, 8), .4); const s2 = ctx.createBufferSource(), rb = bq("bandpass", f, 10), g2 = ctx.createGain();
s2.buffer = nb; s2.connect(rb).connect(g2).connect(K.out);
g2.gain.setValueAtTime(0, t); g2.gain.linearRampToValueAtTime(amp * a, t + .006); g2.gain.exponentialRampToValueAtTime(.001, t + dec);
s2.start(t, R(0, 8), dec + .1);
});
} }
function buildTrain(K) { function buildTrain(K) {
const ctx = S.ctx, lp = bq("lowpass", 700, .6), g = ctx.createGain(), bp = bq("bandpass", 900, .5), bg = ctx.createGain(); const ctx = S.ctx, lp = bq("lowpass", 700, .6), g = ctx.createGain(), bp = bq("bandpass", 900, .5), bg = ctx.createGain();
@ -960,8 +964,8 @@ function buildTrain(K) {
const now = T(); const now = T();
while (K.nextJoint < now + 2.5) { // alle Achsen der Schienenstöße der nächsten Sekunden vorplanen (ein stockender Hauptthread reißt so keine Lücke) while (K.nextJoint < now + 2.5) { // alle Achsen der Schienenstöße der nächsten Sekunden vorplanen (ein stockender Hauptthread reißt so keine Lücke)
const v = K.v, t0 = K.nextJoint, amp = .8 * (.85 + Math.random() * .3); const v = K.v, t0 = K.nextJoint, amp = .8 * (.85 + Math.random() * .3);
[0, 2.5 / v, 12.5 / v, 15 / v].forEach((o, i) => railClack(K, t0 + o, amp * (i % 2 ? .8 : 1))); [0, 3.1 / v].forEach((o, i) => railClack(K, t0 + o, amp * (i ? .9 : 1))); // zwei Achsen des Drehgestells, dann Pause bis zum nächsten Stoß
K.nextJoint += 25 / v; K.nextJoint += 33 / v;
} }
if (now > K.nextPoints) { // Weiche: kurzes schnelles Klapperbündel if (now > K.nextPoints) { // Weiche: kurzes schnelles Klapperbündel
const n = 7 + Math.floor(Math.random() * 5); const n = 7 + Math.floor(Math.random() * 5);

View file

@ -2,7 +2,7 @@
* diora service worker — caches the app shell for offline use. * diora service worker — caches the app shell for offline use.
*/ */
const CACHE = 'diora-v80'; const CACHE = 'diora-v81';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',
'/static/js/app.js', '/static/js/app.js',