🌀 Earth Echo — a Space Echo whose tape is driven by the planet

A tape-delay effect in the built-in synth, RE-201 flavoured, with its knobs
exposed as matrix voices so any feed can drive them (and they groove/CV like
any voice). Audio chain: a delay with a feedback loop (lowpass tone roll +
highpass + tanh tape saturation), an LFO on delayTime for wow&flutter, and a
white-noise hiss bed for wear; the mix taps post -> echoIn -> wet -> out.
params() maps: echo.time -> delay 60-500ms with a LONG glide time-constant
(tape-motor pitch-bend), echo.feedback -> feedback HARD-CAPPED at 0.97
(self-oscillates, never runs away), echo.tone -> loop lowpass 6800->500Hz,
echo.flutter -> LFO depth, echo.wear -> hiss, echo.wet gated by earthEchoOn.

Sky menu "🌀 earth echo" toggles it (gs_echo persists); enabling installs the
earthy default cables: wind->time, volatility->feedback, air.pm25->tone,
moon->flutter, fire.count->wear, quake->wet. Verified: 6 echo voices appear in
the matrix with cables, zero console errors; param math unit-tested (feedback
capped, all ranges bounded, safe). Grimoire section added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-06 15:22:59 +10:00
parent c6dd2adcee
commit 7a444c3965

View File

@ -940,6 +940,8 @@
<p><b>One — the built-in browser synth.</b> Click <b></b> and a full voice rack wakes inside the page: <b>lead</b> (bitcoin's price and your market's average price both quantize to it — minor pentatonic and G dorian), <b>bass</b> (quake depth chooses its notes in a low minor), <b>pad</b> (brightened by Tokyo's warmth, daylight, harmony, the birth rate, longer lives), <b>drone</b> (thickened by aircraft aloft, barometric pressure, Saturn's slow wheel, the weight of the national debt), and <b>perc</b> (BTC volatility and market activity drive the hats). Over them sit the effects the world modulates: <b>reverb</b> (earthquakes open the void, room light sizes it), <b>delay</b> (your hand's Y feeds it, Mercury retrograde smears it), <b>saturation</b> (Delhi's air becomes grit, hard aspects add dissonance, wildfires burn it, hunger grinds beneath), and <b>glitch</b> (every Wikipedia edit on Earth, every light flash, every new wildfire fires a burst). Nothing to install; the planet plays a synth in your tab.</p>
<p><b>🌀 The Earth Echo — a Space Echo whose tape is the planet.</b> Right-click the sky → <b>🌀 earth echo</b>, press <b></b>, and the whole mix runs onto a virtual Roland tape delay — but its knobs are wired to the living world. Its <b>repeat rate</b> is the wind (Tokyo's gusts whip the tape speed, and because a tape motor has inertia the pitch <i>glides</i> as it changes — that dubby seasick bend); its <b>intensity</b> is volatility (bitcoin's fever climbs the feedback toward a screaming, self-oscillating howl); its <b>tone</b> is the air (Delhi's PM2.5 rolls the treble off until the echoes melt into a dark, suffocating soup); its <b>wow &amp; flutter</b> is the Moon (the lunar cycle warbles the pitch with an eerie, drifting vibrato); its <b>tape wear</b> is the world's fire (every wildfire adds hiss and grit, the machine growing more fragile as the planet burns); and a <b>quake</b> throws the whole echo forward. Best of all, these six knobs are ordinary <b>voices in the matrix</b><kbd>echo.time</kbd>, <kbd>echo.feedback</kbd>, <kbd>echo.tone</kbd>, <kbd>echo.flutter</kbd>, <kbd>echo.wear</kbd>, <kbd>echo.wet</kbd> — so you can drag <i>any</i> feed onto any of them, groove them on the 16-step grid, or send them out as CV. Turn it on and the world doesn't just play the instrument; it plays the <i>space</i> the instrument lives in.</p>
<p><b>Two — Web MIDI, out into a DAW or hardware.</b> In Chrome or Edge, open the ⚙ panel's WEB MIDI section, <i>enable MIDI</i>, and pick an <b>out</b> port. Every destination now streams as CC or notes straight to your rig — no Python, no IAC bus. The map is fixed and MIDI-learnable at the far end:</p>
<ul>
@ -4618,6 +4620,8 @@
() => openCommune());
ctxItem("⚡ control voltage…" + (cv.on ? " (live)" : ""),
() => openCV());
ctxItem((earthEchoOn ? "🌀 earth echo: on" : "🌀 earth echo — the planet on tape"),
() => earthEchoSet(!earthEchoOn));
if (commune.room) {
const gOwner = commune.alloc["@godtime"];
ctxItem(gOwner === communeMe() ? "🕸 release the godtime" :
@ -5112,6 +5116,31 @@
}
if (cv.rows.length) { /* wait for user to press start — audio needs a gesture */ }
// ---- the Earth Echo — a tape delay whose knobs ARE matrix voices, wired to
// the planet by default (any feed can be re-patched onto them like any voice).
let earthEchoOn = false;
try { earthEchoOn = localStorage.getItem("gs_echo") === "1"; } catch (e) {}
const ECHO_CABLES = [
["weather.wind", "echo.time", 0.75], // wind whips the tape speed → pitch-glide
["crypto.vel", "echo.feedback", 0.8], // volatility drives it toward self-oscillation
["air.pm25", "echo.tone", 0.9], // pollution rolls the treble off, dark dub soup
["astro.moon", "echo.flutter", 0.65], // the moon warbles the pitch (wow & flutter)
["fire.count", "echo.wear", 0.7], // the world's fires wear the tape (hiss)
["quake.event", "echo.wet", 0.6], // a quake throws the whole echo forward
];
function earthEchoSet(on) {
earthEchoOn = on;
try { localStorage.setItem("gs_echo", on ? "1" : "0"); } catch (e) {}
if (on) {
for (const [s, d2, a] of ECHO_CABLES) addRouteLocal(s, d2, a); // installs cables + echo.* voices
eventTicker.unshift({ text: "🌀 earth echo on — the planet is on the tape: wind→time · volatility→feedback · air→tone · moon→wow · fire→wear. patch any of it like any voice.", tAdded: now(), src: "sys" });
} else {
for (const [s, d2] of ECHO_CABLES) removeRouteLocal(s, d2);
eventTicker.unshift({ text: "🌀 earth echo off", tAdded: now(), src: "sys" });
}
}
if (earthEchoOn) setTimeout(() => { for (const [s, d2, a] of ECHO_CABLES) addRouteLocal(s, d2, a); }, 1200);
// ---- your stars (the natal source — a real ephemeris, in the browser) -----
// Truncated Meeus (Sun ~0.01°, Moon ≤0.1°) + JPL/Standish Keplerian elements
// (planets ≤~0.2°, valid 1800-2050), precessed J2000→equinox of date so the
@ -5690,6 +5719,30 @@
const delWet = ctx.createGain(); delWet.gain.value = 0.35;
delSend.connect(delay); delay.connect(delWet); delWet.connect(out);
// ---- Earth Echo — a Roland Space Echo whose tape is driven by the planet.
// Repeat rate glides (tape-motor inertia → pitch bend); feedback climbs to
// near self-oscillation; a lowpass in the loop is the treble roll (pollution
// darkens); a waveshaper is tape grit; an LFO on the delay time is wow &
// flutter (the moon warbles it); a noise bed is tape hiss (wear).
const echoIn = ctx.createGain(); echoIn.gain.value = 1;
const echoDelay = ctx.createDelay(1.2); echoDelay.delayTime.value = 0.28;
const echoFilt = ctx.createBiquadFilter(); echoFilt.type = "lowpass"; echoFilt.frequency.value = 6500;
const echoHP = ctx.createBiquadFilter(); echoHP.type = "highpass"; echoHP.frequency.value = 120;
const echoSat = ctx.createWaveShaper(); echoSat.curve = tanhCurve();
const echoFb = ctx.createGain(); echoFb.gain.value = 0.3;
const echoWet = ctx.createGain(); echoWet.gain.value = 0; // silent until enabled
echoDelay.connect(echoFilt); echoFilt.connect(echoHP); echoHP.connect(echoSat);
echoSat.connect(echoFb); echoFb.connect(echoDelay); // the tape loop
echoIn.connect(echoDelay); echoDelay.connect(echoWet); echoWet.connect(out);
const echoFlutLfo = ctx.createOscillator(); echoFlutLfo.type = "sine"; echoFlutLfo.frequency.value = 1.3;
const echoFlutAmt = ctx.createGain(); echoFlutAmt.gain.value = 0;
echoFlutLfo.connect(echoFlutAmt); echoFlutAmt.connect(echoDelay.delayTime); echoFlutLfo.start();
const hissBuf = ctx.createBuffer(1, ctx.sampleRate, ctx.sampleRate);
{ const ch = hissBuf.getChannelData(0); for (let i = 0; i < ch.length; i++) ch[i] = (Math.random() * 2 - 1) * 0.5; }
const hiss = ctx.createBufferSource(); hiss.buffer = hissBuf; hiss.loop = true;
const hissGain = ctx.createGain(); hissGain.gain.value = 0;
hiss.connect(hissGain); hissGain.connect(echoDelay); hiss.start();
const preSat = ctx.createGain(); // all voices sum here
const shaper = ctx.createWaveShaper(); shaper.curve = tanhCurve();
const satDry = ctx.createGain(); satDry.gain.value = 1;
@ -5699,6 +5752,7 @@
preSat.connect(shaper); shaper.connect(satWet); satWet.connect(post);
const glitch = ctx.createGain(); glitch.gain.value = 1; // stutter target
post.connect(glitch); glitch.connect(out); post.connect(delSend); post.connect(revSend);
post.connect(echoIn); // the mix onto the Earth Echo tape
lfo = ctx.createOscillator(); lfo.frequency.value = 0.2;
const lfoAmt = ctx.createGain(); lfoAmt.gain.value = 260; lfo.connect(lfoAmt);
@ -5733,7 +5787,8 @@
lfo.start();
return { out, preSat, glitch, revSend, fb, satDry, satWet, leadOsc, leadFilt, leadAmp,
bassOsc, bassAmp, padOscs, padFilt, padAmp, droneOscs, droneAmp };
bassOsc, bassAmp, padOscs, padFilt, padAmp, droneOscs, droneAmp,
echoDelay, echoFilt, echoFb, echoWet, echoFlutAmt, hissGain };
}
function hit(node, peak, dec) { // pluck envelope
@ -5764,6 +5819,16 @@
const sat = d("saturation", 0);
S(N.satWet.gain, sat * 0.9, 0.2); S(N.satDry.gain, 1 - sat * 0.4, 0.2);
lfo.frequency.setTargetAtTime(0.1 + d("lfo.rate", 0) * 6, t, 0.3);
// ---- Earth Echo — the planet on the tape ----
// Repeat rate GLIDES (long time-constant) so a change pitch-bends the tail,
// the tape-motor artifact. Feedback climbs toward — never past — self-osc.
N.echoDelay.delayTime.setTargetAtTime(0.06 + c01(d("echo.time", 0)) * 0.44, t, 0.28);
N.echoFb.gain.setTargetAtTime(Math.min(0.97, c01(d("echo.feedback", 0)) * 0.99), t, 0.15);
N.echoFilt.frequency.setTargetAtTime(6800 - c01(d("echo.tone", 0)) * 6300, t, 0.3); // pollution darkens
N.echoFlutAmt.gain.setTargetAtTime(c01(d("echo.flutter", 0)) * 0.006, t, 0.3); // the moon warbles it
N.hissGain.gain.setTargetAtTime(c01(d("echo.wear", 0)) * 0.03, t, 0.4); // fire wears the tape
N.echoWet.gain.setTargetAtTime(earthEchoOn ? (0.16 + c01(d("echo.wet", 0)) * 0.5) : 0, t, 0.3);
// retriggers are rate-limited to the godtime grid — data jitter glides
// the pitch but only strikes on musical time, not machine-gun time
const minGap = 30 / Math.max(40, godtime.bpm); // one 8th note