KXkonstantinos.xafis
2 min read

A drone engine for Byzantine chant on the Web Audio API

How Isokratis turns Byzantine music theory into a real-time synthesis engine — a 72-moria scale model, oscillator and sample voices, ADSR envelopes, and pitch shifting the way chanters expect it.

#web-audio#isokratistypescript#pwa#byzantine

Isokratis is a practice app for Byzantine chanters. A chanter needs a stable drone — the isokratima — to sing against, and the app synthesizes that drone in real time while the singer chooses the scale and the starting note. The interesting engineering lives in two places: the music model and the audio graph.

Music theory, encoded

Byzantine chant does not use equal temperament. The octave is divided into 72 equal units called moria, and each scale is a sequence of interval steps in that space. So instead of hardcoding frequencies, the model works like the theory does:

Selecting a different scale changes only the interval list. The pitch is always derived from the tonic through the moria arithmetic, which keeps the whole engine consistent — and makes microtonal shifts natural, because a morium is the smallest unit.

The audio graph

The synthesis chain is deliberately small and typed:

AudioSource → ActiveVoice → DroneEngine → destination

Each ActiveVoice is either an oscillator (continuous tone) or a buffer sample (recorded drone), routed through a low-pass filter, an EQ stage and a gain node with an ADSR envelope. The DroneEngine owns the master gain and exposes events (start, stop, voicesChanged) so the UI stays in sync without polling.

The parts that take real care:

Persistence and sync

Settings and presets live in IndexedDB per user (user:123:settings), which makes the app feel instant. On the server, a Cloudflare D1 database stores accounts, sessions and premium state, and cross-device sync uses a stale-while-revalidate helper: IndexedDB answers immediately, the API refreshes in the background.

What the engine taught me

The Web Audio API is surprisingly complete for this use case — the only thing you cannot do is ignore the audio thread’s state machine. Every gain ramp, every voice start, has to be scheduled or it clicks. That discipline carries over: the same care shows up in the API layer, where session validation is cached in Cloudflare KV with a TTL, so a chanter’s first note of the day is not slowed down by a database round-trip.

If you sing Byzantine chant, give it a try. If you write audio code, the moria arithmetic is the part I would steal.