Skip to content

Option Relationships ​

MIDI Sketch Bach's configuration options interact with each other in specific ways. Understanding these relationships helps you craft configurations that produce the results you want.

Two layers of options

form, isMinor, and character affect the internal composition. character also affects note articulation and the MIDI CC profile in the finished output. key transposes that result for output, while instrument, bpm, and seed control rendering or reproducibility. The Music Primer for Engineers explains the musical terms used here.

Dependency Overview ​

Which option decides whatThe configuration splits into two bands. In the upper band, form, character, isMinor and seed change the notes that get composed; a note records that a form may forbid a character outright. In the lower band, key controls output transposition and metadata. Instrument and bpm also feed late-stage realization: instrument affects ornament density and the output range and program, while bpm affects trill pacing and tempo events.these change the notesread before and during compositionformfixes the voice count, the meter, the natural length and the whole formal layoutcharactershapes subject material, articulation and MIDI levels — intervals, energy, contourisMinorselects the internal C major or C minor harmonic planseedseeds the RNG; 0 draws a random non-zero seed, reported as getInfo().seedUseda form may forbid a character outright: chorale_prelude rejects playful and restless, toccata_and_fugue rejects noblelate realization & outputresolved in late-stage processingkeytransposes the MIDI and getEvents() pitches, and labels the output-key metadatainstrumentselects ornament density and pitch ceiling, then sets the GM program and output rangebpmcontrols trill pacing and tempo events; 0 means the default of 100Length is the one thing decided by two options together — scale and targetBars run a resolution chain of their own.

Form Decides the Voice Count ​

The form is the most influential option. It fixes the number of voices, the meter, and the natural length, and it selects the default instrument.

Picking the form picks the textureThree forms shown side by side as voice lanes. The fugue has three lanes with staggered theme entries and episodes between them. The cello prelude has a single continuous lane. The chaconne declares three lanes: V0 variation, V1 middle material that starts as a rest and may rest for bars or cycles where no clean register placement exists, and V2 ground. Each card also lists the voice count, meter, length and the instrument the form accepts.form: 'fugue'three voices, entries staggered3 voices · 4/442 reference bars → 44 outputorgan — the only accepted instrumentform: 'cello_prelude'one continuous line1 voice · 4/48 bars, with no snapping neededcello — the only accepted instrumentform: 'chaconne'V0 variation + V1 middle over V2 ground3 voices · 3/4; V1 may rest by bar16 bars, in three-timeviolin — the only accepted instrumentThere is no voice-count option. numVoices and num_voices are still accepted for compatibility, and ignored.

numVoices was removed

There is no voice-count option anymore — pick the form to pick the texture (see the Forms table). Passing num_voices/numVoices is accepted and ignored for backward compatibility; it never errors and has no effect.

Default Cascade ​

When you specify a form, the engine fills in unspecified fields:

js
// You specify:
generator.generate({ form: 'fugue', key: 2, isMinor: true })

// Engine resolves to:
// {
//   form: 'fugue',
//   key: 2,
//   isMinor: true,
//   instrument: 'organ',  ← form default
//   bpm: 100,             ← default
//   seed: 0,              ← random (resolved seed in getInfo().seedUsed)
//   character: 'severe',  ← default
//   scale: 'short',       ← default (≈ natural length)
// }
// Voice count (3), meter (4/4), and reference length (42 bars) come from the form.
// The 4-bar grid resolves the default output to 44 bars.

Any field you explicitly set overrides the default:

js
// Override BPM and length
generator.generate({
  form: 'fugue',
  bpm: 72,          // overrides default 100
  scale: 'medium'   // overrides default 'short'
})

See the Forms table for per-form voice counts and the Presets Reference for the full default table.

Instrument Is Chosen by the Form ​

Each form is written for a specific instrument, and the engine accepts only what that form was written for:

FormDefault InstrumentAlso accepted
fugue, prelude_and_fugue, trio_sonata, chorale_prelude, toccata_and_fugue, passacaglia, fantasia_and_fugueOrgan--
cello_preludeCello--
chaconneViolin--
goldberg_variationsHarpsichordPiano

The instrument choice affects the General MIDI program, the playable range used to fit the completed output, and the ornament density of the post-pass. Each form has an explicit set of compatible instruments; requesting one outside that set throws, as does an unknown instrument string.

TIP

The Goldberg Variations is the one form with a real choice. harpsichord gives the crisp attack that keeps the variation texture transparent; piano gives a warmer, more sustained reading of the same material.

Whole-score octave displacement keeps the output inside the instrument's compass: rather than clamping individual notes that fall outside the range, the engine shifts the entire piece by octaves so the internal voice leading survives intact.

Scale and targetBars ​

scale and targetBars both set the output length. scale is a multiplier of the form's natural length. targetBars: 0 uses scale, while a positive targetBars is an explicit override. Use targetBars values from 0 through 128. The API accepts integers up to 65535, but values near the upper limit can overflow while the bar count is aligned and return the form's minimum length.

How the output length is resolvedA chain of four values. The form's natural length is multiplied by the scale to give a raw target, that target is snapped to the form's own bar grid, and the snapped value is clamped to the range between the form's minimum and 128. A targetBars value of zero uses scale; a positive value overrides scale and enters at the raw-target stage, then passes through snapping and clamping. Goldberg full when no positive targetBars is supplied is a special complete 128-bar layout rather than a literal four times its 20-bar natural length. Below, three worked examples for the fugue.natural lengththe form's own referencebar count — 42 for a fugue× scaleraw targetshort ≈1x, medium ≈2x,long ≈3x, full ≈4x*snapsnappedrounded to the form's ownbar grid — its ground periodclampoutput barsheld inside [form minimum,128]; no form goes past 128targetBarspositive wins; zero uses scaleworked through, for the fuguenatural length 42 bars, grid step 4 · * Goldberg full with no positive targetBars resolves to 128 barsrequestrawsnappedoutputscale: 'short'42 × 1 = 424444scale: 'long'42 × 3 = 126128128targetBars: 48484848Except for Goldberg full, both routes finish through the same snap and clamp, which is why targetBars is a request for a length rather than a guarantee of one.
ConfigurationBehavior
scale onlyLength = form's natural length × the scale multiplier
targetBars: 0Uses scale (zero is the sentinel for no explicit override)
Positive targetBarsEngine targets that bar count and ignores scale
Both specifiedA positive targetBars wins; scale is ignored
Neither specifiedDefault: scale: "short" (≈ natural length)

The scale multipliers are approximately short ≈ 1x, medium ≈ 2x, long ≈ 3x, full ≈ 4x of the form's natural length. The Goldberg Variations is the exception: scale: "full" when no positive targetBars is supplied selects the complete compressed 128-bar layout rather than 20 × 4 = 80 bars.

TIP

Positive values are snapped to the form's granularity (e.g. the ground-bass period) and clamped to [form minimum, 128]. Every form caps at 128 bars. Use scale for a general size category; use targetBars for a specific length.

js
// Length as a multiple of the form's natural length
generator.generate({ form: 'fugue', scale: 'long' })   // 42 × 3 = 126, snapped to 128 bars

// Specific length (snapped and clamped)
generator.generate({ form: 'fugue', targetBars: 48 })

// targetBars wins when both specified
generator.generate({
  form: 'fugue',
  scale: 'short',      // ignored
  targetBars: 48        // this is used
})

Seed Behavior ​

The seed parameter controls deterministic output:

Seed ValueBehavior
0 (default)A random non-zero seed is chosen; the resolved value is reported via getInfo().seedUsed
Any positive integerDeterministic — same config + same seed = byte-identical output

Reproducing a random run

After a seed: 0 run, read getInfo().seedUsed and pass it back as seed to regenerate the exact same piece.

Reproducibility

Deterministic reproduction requires the same version of MIDI Sketch Bach. The internal algorithms may change between versions, so the same seed may produce different output after an upgrade. If you need to preserve specific outputs, save the generated MIDI files rather than relying on seed reproducibility across versions.

js
// Random each time
generator.generate({ form: 'fugue', seed: 0 })

// Always produces the same result
generator.generate({ form: 'fugue', key: 2, isMinor: true, seed: 42 })

// Different key = transposed MIDI/getEvents output; internal generated.v1 is unchanged
generator.generate({ form: 'fugue', key: 0, isMinor: true, seed: 42 })

Key and Mode ​

The engine composes internally in C. isMinor selects the internal C-major or C-minor harmonic plan; key does not alter that plan. Instead, key transposes the finished MIDI and getEvents() note pitches and labels their output-key metadata.

Tonal center

The tonal center is the pitch that feels like home in the output. key chooses its pitch class, while isMinor chooses whether the internally composed plan and the output-key label are major or minor.

js
// D major
generator.generate({ key: 2, isMinor: false })

// D minor — by pitch class or by canonical name
generator.generate({ key: 2, isMinor: true })
generator.generate({ key: 'D', isMinor: true })
ParameterRangeDefault
key0--11 (pitch class) or a canonical name ("C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B")0 (C)
isMinortrue / falsefalse (major)

Names are matched exactly — "g" and "Db" both throw. Use getKeys() to enumerate the accepted spellings.

Changing only key changes the output transposition and key metadata. It does not change the harmonic vocabulary or modulation plan, so getGenerated() keeps the same internal-C pitches for the same form, mode, character, and seed. getEvents() exposes the transposed/output-key pitches instead.

Character and Form ​

The character parameter (severe, playful, noble, restless) shapes the primary thematic material. Its impact varies by form, and some combinations are forbidden:

Character is not genre

character changes the melodic profile of the subject or primary material: interval size, rhythmic energy, chromatic tendency, and contour. It also changes note articulation and the MIDI CC profile in every form. In cello_prelude, it orders the figure palette used for each bar. See Instruments for the instrument-specific expression output. It does not switch the form. A restless fugue is still a fugue.

Form TypeCharacter Impact
Fugal forms (fugue, prelude_and_fugue, toccata_and_fugue, fantasia_and_fugue)Strong — directly shapes the fugue subject, which defines the entire piece
Variation forms (passacaglia, chaconne, goldberg_variations)Moderate — colours the variations over the fixed bass
Chorale PreludeModerate — affects the contrapuntal voice; cantus firmus is fixed
Trio SonataModerate — shapes the motivic material for the upper voices
Cello PreludeModerate — influences figuration patterns

Forbidden character/form pairs (these throw)

  • chorale_prelude rejects playful and restless.
  • toccata_and_fugue rejects noble.

Requesting a forbidden pair throws instead of silently substituting a character.

TIP

severe is a solid default for most situations. Try restless for Toccata and Fugue, or noble for Chorale Prelude.

Validation Rules ​

Complete validation constraints for all configuration fields. Note that several fields now throw on invalid input rather than clamping:

Config validation vs musical validation

This table covers API/config validation: whether option values are accepted. Counterpoint and form-structure failures are separate musical validator rules; see Validator Rule Reference.

FieldTypeRangeDefaultValidation
formnumber or string0--9 / name"fugue"Unknown name / out-of-range number throws
keynumber or string0--11 / canonical name0Out-of-range number or unknown name throws
isMinorbooleantrue/falsefalseNon-boolean throws
bpmnumber0 or 40--2001000 uses default 100; any other out-of-range value throws
seednumber0+00 = random; resolved value in getInfo().seedUsed
characterstring or numbername / 0--3"severe"Unknown value throws; forbidden form pairs throw
instrumentstring or numbername / 0--5Form defaultUnknown value throws; an instrument the form does not accept throws
scalestring or numbername / 0--3"short"Unknown value throws
targetBarsinteger0--6553500 uses scale; positive values override it, then snap to form granularity and clamp to [min, 128]
numVoicesnumber----Accepted and ignored (form decides voices)

Each rejection carries its own message — Invalid BPM (must be 0 or 40-200), Incompatible instrument for this form, and so on — so a failure identifies the field that caused it.

Dual-licensed: AGPL-3.0 · commercial licensing available. Generated MIDI is yours to use freely.