PyChimes — Ambient Generative Windchimes

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.

Overview

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:

Architecture

┌───────────────────────────┐      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            │
       └────────────────────────────────────────────────────────────────────┘

Key Files

PathRole
/app/chimes_core.pyPython synthesis & generative logic (wind, gusts, musical system, reverb)
/worker/pychimes-worker.jsBoots Pyodide, runs the engine, fills the ring buffer, emits gust events
/worklet/pychimes-processor.jsAudioWorklet processor that reads from the shared buffer
main.jsWires UI → worker, sets up worklet and SharedArrayBuffer
index.htmlWeb UI layout and styles

Serving the App

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

Local quick start (example)

  1. Run a dev server that sets the headers above (any static server or proxy that can inject headers). Examples: Caddy, nginx, Vite preview, or an Express script.
  2. Open 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.

Directory layout

project/
  index.html
  main.js
  /worker/pychimes-worker.js
  /worklet/pychimes-processor.js
  /app/chimes_core.py

Engine Design (chimes_core.py)

Wind Model

Gust System

Pitch & Musicality

Synthesis

Signal Flow

wind  ──▶ rate model ─┐
                      │      ┌─ envelopes/partials ─┐
gust ──▶ modifiers ───┼─▶ strikes ───────────────▶ mix ─▶ reverb ─▶ DC block ─▶ out(L/R)
                      │      └─ soft saturation ──┘
scale/root/octave ───▶ pitch selection

Technologies

LayerToolPurpose
BrowserWeb Audio API (AudioWorklet)Deterministic, low-latency audio output
BrowserSharedArrayBufferZero-copy streaming between worker and audio thread
PythonPyodide 0.26 + NumPyRuns DSP and generative logic client-side
UIHTML/CSS/JSWeb UI Interface and controls

Extending

Notes on Deployment

Return to the app: index.html