A browser-based generative instrument that blends real-time synthesis, procedural rhythm, and tonal systems. Runs entirely client-side with a Python engine in Pyodide and a Web Audio worklet for smooth, local playback.
PyChimes renders evolving wind-chime textures by simulating strikes whose timing and force are driven by an algorithmic wind model. The musical layer constrains pitch selection to user-selectable scales and root/octave. A lightweight stereo reverb adds space. The entire instrument runs in the browser:
chimes_core.py) and generates audio blocks.┌───────────────────────────┐ SharedArrayBuffer ┌─────────────────────┐
│ Web Worker (Pyodide) │ ─────────────────────────▶ │ AudioWorkletNode │
│ - Python engine │ │ - pulls stereo L/R │
│ - NumPy DSP │ │ - outputs to DAC │
└─────────────▲─────────────┘ └──────────┬──────────┘
│ postMessage (params, gust events) │
│ ▼
┌──────┴─────────────────────────────────────────────────────────────┐
│ Web UI (HTML/CSS/JS) │
│ sliders, selects, checkboxes → worker param messages │
└────────────────────────────────────────────────────────────────────┘
| Path | Role |
|---|---|
/app/chimes_core.py | Python synthesis & generative logic (wind, gusts, musical system, reverb) |
/worker/pychimes-worker.js | Boots Pyodide, runs the engine, fills the ring buffer, emits gust events |
/worklet/pychimes-processor.js | AudioWorklet processor that reads from the shared buffer |
main.js | Wires UI → worker, sets up worklet and SharedArrayBuffer |
index.html | Web UI layout and styles |
Because the project uses SharedArrayBuffer, browsers require cross-origin isolation. Serve over HTTPS (or localhost) with these headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
https://localhost:<port>/ and click Start Wind Chime.Plain python -m http.server does not set COOP/COEP headers. If you must use it, front it with a proxy that adds the headers.
project/
index.html
main.js
/worker/pychimes-worker.js
/worklet/pychimes-processor.js
/app/chimes_core.py
1/λ.g (0–1) maps to a Poisson-like process with average gap:
T(g) = 30 - 27 * (g^1.25) seconds (approx. 30 s → 3 s)
engine.is_gust_active() and toggles the UI badge.tanh) prevents harsh peaks.(N,2) float32 stereo frames.wind ──▶ rate model ─┐
│ ┌─ envelopes/partials ─┐
gust ──▶ modifiers ───┼─▶ strikes ───────────────▶ mix ─▶ reverb ─▶ DC block ─▶ out(L/R)
│ └─ soft saturation ──┘
scale/root/octave ───▶ pitch selection
| Layer | Tool | Purpose |
|---|---|---|
| Browser | Web Audio API (AudioWorklet) | Deterministic, low-latency audio output |
| Browser | SharedArrayBuffer | Zero-copy streaming between worker and audio thread |
| Python | Pyodide 0.26 + NumPy | Runs DSP and generative logic client-side |
| UI | HTML/CSS/JS | Web UI Interface and controls |
Return to the app: index.html