🌀 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:
parent
c6dd2adcee
commit
7a444c3965
@ -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 & 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user