- world_bank.py: poverty, food security, fertility, life expectancy (World Bank) folded into the "shadow" / "bright" concept groups - spells: save/load now captures the full live mix (gains/mutes/freezes), not just the wiring; TEMPLATES panel renamed SPELLS - performance mode (press P): clean projection view + rotating "dispatches from the world" text readout for live shows Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
258 lines
13 KiB
Markdown
258 lines
13 KiB
Markdown
# 🌍 GODSTRUMENT
|
||
|
||
> An instrument where your hand, the room, and the planet are the same kind of thing.
|
||
|
||
Godstrument is a **multi-modal sensor-fusion instrument**. Every input — a hand
|
||
sensor, the room's microphone, a satellite, the price of bitcoin, an earthquake
|
||
on the other side of the Earth — is turned into a clean control signal and fired
|
||
at a central hub. The hub normalizes and smooths everything, runs it through a
|
||
**modulation matrix**, and pours the result out as MIDI/OSC for Ableton or
|
||
TouchDesigner, plus a live **web visualizer** so you can watch the planet play.
|
||
|
||
Nothing here needs the physical sensors to run. The world-data feeds and a
|
||
sensor **simulator** give you a fully playable instrument *today*; the ESP32 +
|
||
ToF + light hardware slot into the exact same OSC contract when they arrive.
|
||
|
||
---
|
||
|
||
## The architecture (everything is an OSC source)
|
||
|
||
```
|
||
hand / room / sky / markets / humanity outputs
|
||
───────────────────────────────────── ───────────
|
||
sim_esp32 ─┐ ┌─► OSC /out/* → Ableton / TouchDesigner
|
||
world_* ─┤ │
|
||
audio ─┼─► OSC 9000 ─► HUB ─► mod-matrix ─────┼─► MIDI CC / notes (optional, via IAC)
|
||
vision ─┤ normalize + One-Euro │
|
||
midi ─┘ + impulse envelopes └─► WebSocket 8765 → 🖥 visualizer (http 8088)
|
||
```
|
||
|
||
- **`normalize.py`** — One Euro filter (jitter-free, low-lag), adaptive
|
||
normalization (auto-scales unknown/drifting ranges to 0..1), impulse envelopes
|
||
(events → decaying signals), slew.
|
||
- **`matrix.py`** — the patchbay: N sources × M destinations, each cable with an
|
||
amount, response curve, polarity, optional **threshold gate** and
|
||
**quantize-to-scale** (so a data stream picks notes in a key instead of
|
||
wobbling formlessly).
|
||
- **`hub.py`** — receives all OSC, runs the control-rate clock, evaluates the
|
||
matrix, drives the outputs + websocket.
|
||
- **`config.json`** — the current patch: which source routes to which
|
||
destination. **This is where you compose.**
|
||
- **`workers/`** — one process per source, each just emits OSC.
|
||
- **`viz/index.html`** — the live console.
|
||
|
||
## The starter patch
|
||
|
||
The sun opens the master filter · bitcoin volatility drives the hats and plays
|
||
the lead melody in a minor pentatonic · Delhi's air becomes distortion grit ·
|
||
every Wikipedia edit on Earth is a glitch burst · earthquakes open the reverb
|
||
void and choose the bass note · Tokyo's weather brightens the pads · aircraft
|
||
aloft thicken the drone · your (simulated) hand morphs the wavetable and feeds
|
||
the delay. Edit `config.json` to rewire any of it.
|
||
|
||
## Run it
|
||
|
||
```bash
|
||
python3 -m venv .venv
|
||
./.venv/bin/pip install python-osc websockets
|
||
./.venv/bin/python run.py
|
||
```
|
||
|
||
Then open **http://localhost:8088**. That's it — real planetary data starts
|
||
flowing in seconds.
|
||
|
||
Profiles: `--profile core` (default: simulator + all world feeds),
|
||
`--profile world` (data only), `--profile all` (adds mic/webcam/MIDI workers if
|
||
their optional libs are installed), `--profile minimal` (just the simulator).
|
||
|
||
## Live data sources (all free, all keyless)
|
||
|
||
| Source | Feed | Character |
|
||
|---|---|---|
|
||
| Earthquakes | USGS all-hour GeoJSON | event triggers |
|
||
| Weather | Open-Meteo | slow / mood |
|
||
| Air quality | Open-Meteo air-quality | slow / grit |
|
||
| Solar wind | NOAA SWPC | slow LFO |
|
||
| ISS position | open-notify | drift |
|
||
| Crypto ticks | Binance websocket | sub-second chaos / rhythm |
|
||
| Aircraft | OpenSky | swarm / drone |
|
||
| Wikipedia edits | Wikimedia SSE stream | human pulse / glitch |
|
||
| Local sky | computed (NOAA solar position) | day/night LFO, per-venue |
|
||
| Humanity | modelled vital rates | births/deaths/growth velocity |
|
||
| Ephemeris / almanac | computed (Swiss Ephemeris) | planets, moon, planting calendar |
|
||
| National debt | US Treasury (no key) | slow dark ballast + daily churn |
|
||
| Inflation | US BLS CPI (no key) | grinding pressure |
|
||
| Human condition | World Bank (no key) | poverty, hunger, fertility, life expectancy |
|
||
|
||
The **dark half** (debt, inflation, pollution, quakes, the death rate) balances
|
||
the **bright** (births, moon, harmony, fertility, daylight) — group your sources
|
||
by *feeling*, not category: a "shadow" group, a "sad" group, whatever you blend.
|
||
Congress-trade and conflict feeds are possible too but need keyed/curated sources.
|
||
|
||
## Spells
|
||
|
||
Save a whole configuration — routes, groups, orbits, **and the live mix** (gains,
|
||
mutes, freezes) — as a named **spell** from the ⚙ panel's SPELLS section (`save`,
|
||
then `load` from the dropdown). Casting a saved spell restores the exact state.
|
||
They live in `patches/*.json`.
|
||
|
||
## Performance mode
|
||
|
||
Press **P** for a clean projection view: the editing chrome vanishes, the source
|
||
readouts declutter, and a rotating line of "dispatches from the world" (*bitcoin
|
||
at $62,902 · the moon is 81% lit · mercury is retrograde · M4.2 earthquake*)
|
||
appears for the audience. Open the console on a second monitor / projector and
|
||
hit P. (Curated selections + MIDI-mappable display controls are the next layer.)
|
||
|
||
## Strudel & MIDI voices
|
||
|
||
The ⚙ Web MIDI out already turns every destination into CC/notes, so any synth
|
||
becomes an output that "receives sources." `strudel/godstrument.md` is a starter
|
||
kit that drives a live **Strudel** track (free/open, no Ableton) from the hub's
|
||
CC map — plus a no-MIDI websocket path for a custom web-audio synth.
|
||
|
||
## Playing it live — the performance layer
|
||
|
||
Raw data is honest but mechanical. The **tweak layer** (`transform.py`) lets you
|
||
*play* it: `gain`, `offset` (bend/lean), `invert`, `freeze` (a CDJ-style hold —
|
||
"stop the planet here"), and `smooth` (make a signal liquid or snap it tight).
|
||
|
||
**Groups** let one control lean on many sources at once. The starter patch
|
||
defines three — `planet` (local world), `cosmos` (shared sky), `human` — and
|
||
binds live controls to them in `config.json`:
|
||
|
||
```json
|
||
"controls": [
|
||
{"src": "hand.open", "target": "@planet", "param": "gain", "min": 0.2, "max": 1.4},
|
||
{"src": "midi.cc.1", "target": "@cosmos", "param": "offset", "min": -0.3, "max": 0.3},
|
||
{"src": "midi.cc.20","target": "@planet", "param": "freeze", "min": 0.0, "max": 1.0}
|
||
]
|
||
```
|
||
|
||
`src` is any signal — a hardware knob (`midi.cc.1`, via the MIDI worker), a hand
|
||
sensor (`hand.open`, `tof.near`), or another data stream. So a physical knob, or
|
||
your open palm, becomes a macro over four data sources at once.
|
||
|
||
## Time-warp — scratch a month of the planet
|
||
|
||
`run.py --profile warp` swaps the live quake + weather feeds for **replay**
|
||
workers (`timewarp.py`): they fetch history and play it back *compressed* on a
|
||
loop — 26 days of global earthquakes into 5 minutes, a week of weather into 4 —
|
||
so glacial data finally has agency in a track.
|
||
|
||
Because a replay has a **playhead**, you can *scrub* it. The `warp` section in
|
||
`config.json` binds a control to each loop's jog:
|
||
|
||
```json
|
||
"warp": {
|
||
"quakes": {"port": 9101, "rate_src": "tof.cx", "nudge_src": "tof.near"}
|
||
}
|
||
```
|
||
|
||
So your hand (or a knob) sets the warp speed and shoves the playhead forward/back
|
||
like a CDJ platter. A month of the Earth's seismicity becomes a loopable,
|
||
scratchable rhythm.
|
||
|
||
## Memory — record everything, replay anything
|
||
|
||
`run.py --record` runs `recorder.py` alongside the live rig, taps the hub, and
|
||
writes every signal + event to `godstrument.db` (SQLite — live and historical in
|
||
one file). Then `replay_db.py` can time-warp **any** recorded signal — not just
|
||
feeds with a public history API, but your own hand, the room mic, the ToF sensor:
|
||
|
||
```bash
|
||
python workers/replay_db.py --signals tof.cx,tof.cy,audio.rms --window last:3d --into 240
|
||
```
|
||
|
||
Footprint is small (change-based logging + a decimation cap). Measured:
|
||
**~68 bytes/sample**, so **world data ≈ 80 MB/day** (most of it the Wikipedia
|
||
firehose — throttle it and it's ~15 MB/day); fast local sensors at 20 Hz are the
|
||
main cost. A **3–5 day rolling buffer is a few hundred MB** — trivial, fine on a
|
||
Pi's SD card. Prune with `DELETE FROM samples WHERE t < <cutoff>` + `VACUUM`.
|
||
`recorder.py --stats` prints size / signals / span.
|
||
|
||
## Your own market — the dealgod feed
|
||
|
||
`run.py --dealgod` runs `workers/world_dealgod.py`, which reads price history from
|
||
the **basegod warehouse** (`dealgod.price_events`, ~4M rows) over the tailnet —
|
||
read-only, a single server-side aggregate query, **no writes** — bins it into an
|
||
*activity* curve and an *average-price* curve, and replays them compressed +
|
||
jog-scrubbable. Your deal-flow becomes a control source: market activity drives
|
||
the hats and nudges tempo; average price adds grit and **plays the lead melody**
|
||
(G dorian). Flags: `--ssh`, `--days`, `--into`. Point `--ssh` at a `dashboard_ro`
|
||
host for strict least-privilege networked access.
|
||
|
||
## The heavens — real ephemeris, no downloads
|
||
|
||
`world_ephemeris.py` computes actual planetary positions with the Swiss Ephemeris
|
||
(Moshier mode — **no data files**, 3000 BC–3000 AD). Nothing is stored or fetched;
|
||
the sky is generated on demand, like the sun worker. It emits **moon phase**,
|
||
**zodiac longitudes** (natural LFOs from monthly to Saturn's 29-year wheel),
|
||
**aspect tension/harmony**, and **retrograde flags**. In the starter patch the
|
||
moon sets the master space, Mercury retrograde smears the delay, hard aspects add
|
||
grit, Saturn weights the drone. Ephemeris is *computed, not ingested* — the
|
||
opposite of big data (`pip install pyswisseph`, zero downloads).
|
||
|
||
## The console is playable
|
||
|
||
The visualizer isn't just a display — it's a control surface. **Click any source
|
||
sphere to mute / unmute it** (it gets a red slash). The **⚙ button, top-right**,
|
||
opens a panel with per-group **mute / freeze / gain** macros (planet, cosmos,
|
||
humanity, market, sky). Commands travel back to the hub over the same websocket,
|
||
so it stays in sync. Deep patch editing — routes, curves, ranges — still lives in
|
||
`config.json`.
|
||
|
||
**Web MIDI** (Chrome/Edge): the ⚙ panel has a WEB MIDI section — `enable MIDI`,
|
||
pick an **out** port and the destinations stream as CC/notes straight to your DAW
|
||
(no Python/IAC needed), pick an **in** port and hit **MIDI learn** to bind a knob
|
||
to any control (click a slider / mute / sphere, then move the knob; saved to
|
||
`localStorage`). Web MIDI needs a secure context: `localhost` works as-is; a
|
||
VPS-hosted console needs HTTPS (e.g. `tailscale serve`). The browser runs the
|
||
MIDI, so a remote-hosted page still drives the MIDI on *your* laptop.
|
||
|
||
## The planting almanac
|
||
|
||
`world_almanac.py` computes the old sow-by-the-moon calendar from the same sky —
|
||
no data, no downloads. The zodiac's four elements cycle fire→earth→air→water, and
|
||
biodynamic tradition maps them to crop types, so the Moon walks a slow four-step
|
||
sequencer (changing every ~2.25 days): **earth=root, water=leaf, air=flower,
|
||
fire=fruit/seed**. It also emits waxing/waning (sow above vs below ground), the
|
||
Moon's ascending/descending declination cycle (~27.3d), and a fertility score.
|
||
The instrument's character can follow the moon through the garden.
|
||
|
||
## Planetary orbits
|
||
|
||
Any group can be assigned a planet (`"orbit": "neptune"` in `config.json`); its
|
||
whole contribution then **waxes and wanes at that planet's real relative orbital
|
||
rate**. With `orbit_base_seconds` mapping one Earth year, Mercury (your market)
|
||
pulses every ~14s, Mars (humanity) every ~113s, Saturn (cosmos) every ~29min,
|
||
Neptune (the sky) every ~2.7h. Group your sources by planet and the solar system
|
||
becomes a bank of LFOs spanning seconds to a lifetime.
|
||
|
||
## Wiring it on stage — the editing console
|
||
|
||
The console is fully live-editable, no config file needed:
|
||
- **Drag a source sphere onto a destination** to patch a new cable.
|
||
- **Shift-click** any node for its **inspector** — what it is, every cable in/out,
|
||
an amount slider and an unpatch × per connection.
|
||
- **⌘/Ctrl-click** spheres to multi-select, then **group** them into a new macro.
|
||
- Mutes, group macros, and Web MIDI live in the ⚙ panel.
|
||
|
||
It all travels to the hub over the websocket (`add_route` / `remove_route` /
|
||
`set_route` / `make_group`), which rewires the modulation matrix in real time.
|
||
|
||
## Sending it to Ableton / TouchDesigner
|
||
|
||
- **OSC:** the hub streams `/out/<destination>` floats to `127.0.0.1:9001`.
|
||
Point TouchDesigner's OSC In, or a Max-for-Live OSC device, at that port.
|
||
- **MIDI:** set `"midi": {"enabled": true}` in `config.json`, create an **IAC
|
||
bus** in *Audio MIDI Setup → MIDI Studio*, `pip install python-rtmidi`, and the
|
||
hub sends CC + notes to it. Map the CCs in Ableton with MIDI-learn.
|
||
|
||
## Adding real hardware later
|
||
|
||
The ESP32 firmware just has to send the same OSC addresses the simulator does
|
||
(`/gs/tof/cx`, `/gs/tof/cy`, `/gs/tof/near`, `/gs/light/lux`,
|
||
`/gs/light/flash/event`) to your Mac's IP on port 9000. Kill `sim_esp32.py`,
|
||
power on the board, nothing else changes.
|