Architecture
Reading the musical terms
This page uses music terms as data-model terms: voice means a melodic stream, harmony means the chord plan over time, and form means the composition template. See the Music Primer for Engineers for the short definitions.
Overview
What is MIDI Sketch Bach?
MIDI Sketch Bach is an algorithmic composition engine that generates Baroque-style instrumental MIDI using rule-based composition, not machine learning. Every note is determined by a harmonic plan and counterpoint constraints, producing editable MIDI you can import into any DAW.
A C++ composer engine is compiled to WebAssembly, with a JavaScript API on top:
The Composer Engine
A single composer subsystem handles every form. Rather than separate generators per genre, the engine expresses each form as a layout of voice intents over a harmonic plan, then fills, validates, and renders that layout. The form decides the texture (voice count, meter, natural length); there is no voice-count option.
Voice intent and harmonic plan
A voice intent is the role assigned to one melodic stream over a span of bars: subject, answer, ground bass, figuration, and so on. A harmonic plan is the chord roadmap the candidate search must fit into.
The pipeline is:
- Compose Request — resolve and validate the config; resolve the seed; fix voice count / meter / length from the form.
- Form Director — assign per-form voice intents (subjects, grounds, cantus firmus, figuration, variations) to bar spans.
- Candidate Search — per-beat, chord-tone-anchored note selection against the harmonic plan.
- Rule Validator — counterpoint and structure checks, fail-fast.
- Renderer — voices to tracks.
- Ornament & Expression — deterministic post-passes, kept outside
Composer::run()and invoked by the public generation path. - MIDI Writer — key transposition and Standard MIDI File output.
See the Generation Pipeline for a step-by-step breakdown.
Design-Value Arc
Structure follows a fixed design arc — establish → develop → climax (at ~80% of the span) → resolve — that controls density, register, and velocity tiers. The arc is a property of the form, not something searched per seed, which keeps output musically shaped and reproducible.
Density, register, velocity
Density is how many notes happen in a span. Register is pitch height, such as low bass or high treble. Velocity is MIDI note intensity. The arc raises and lowers these values so the output has phrase shape instead of a flat stream of notes.
Form Families
The form director handles several layout families:
- Fugal (
fugue,prelude_and_fugue,toccata_and_fugue,fantasia_and_fugue) — subject/answer entries with episodes. - Ground-bass variation (
passacaglia,chaconne,goldberg_variations) — an immutable bass with successive variation cycles;goldberg_variationsbuilds an aria plus thirty variations including canons at widening intervals, andpassacaglia/chaconnerun their cycles in 3/4. - Cantus firmus (
chorale_prelude) — a fixed chorale line plus a contrapuntal voice. - Linear / figural (
trio_sonata,cello_prelude) — interacting or continuous figuration.
Determinism
The engine is fully deterministic: the same config and seed produce byte-identical output. Composition runs internally in C; the requested key is applied only by the MIDI writer, so the events JSON always reports pitches in C while the .mid file is transposed.
Why events stay in C
Keeping internal event data in C makes validator behavior easier to compare across keys: the same form and seed can be inspected before the final output transposition. The MIDI file is still written in the requested key.
WASM Integration
- Initialization:
init()loads and instantiates the WASM module. - Memory management:
BachGeneratorallocates WASM memory;destroy()frees it. - Data transfer: MIDI output is copied from WASM memory to a JavaScript
Uint8Array; event data is serialized from WASM and parsed in JavaScript.