@jmove/generator
v1.2.7
Published
Jazz backing track generator — walking bass, piano comping, drum patterns, and style presets for practice sessions
Maintainers
Readme
@jmove/generator
Jazz backing track generator — walking bass lines, piano comping, drum patterns, and full jam sessions from chord progressions.
Zero dependencies. TypeScript. ESM + CJS.
Demo
Try the live app at jmove.it.com — the Practice Player uses this package to generate all backing tracks in real time.
Stories
Deep technical articles about how JMove and the generator work:
- Inside @jmove/generator: Every Algorithm Explained — Evans voicings, stochastic drum comping, groove templates, 996 tests
- Building JMove: Architecture of a Jazz Practice Tool — three engines, two languages, 45 chord qualities, 26 style presets
- Walking Bass from First Principles — contour-first planning, approach weights, two-bar phrasing arcs
- 1,511 Jazz Standards in Your Browser — iReal Pro URI decoding, 43-rule quality map, 748KB offline database
- Five Ways to See a Chord — Zustand sync, DFS fretboard search, Viterbi voice-leading optimizer
- 14 Ways to Reharmonize a Jazz Standard — tritone subs to Coltrane changes, every technique with history and theory
Features
- Per-Instrument Complexity — 3 general sliders (drumComplexity, pianoComplexity, bassComplexity) drive 13 granular sub-controls via piecewise linear mapping. Drums: tomFrequency, fillIntensity, rideWash, ghostDensity, cymbalColor. Piano: voicingDensity, rhythmicActivity, registerRange, anticipation. Bass: chromaticApproach, registerWidth, syncopation, beatVariety. Manual overrides take precedence over derived values
- Musicality Engine — phrase-level intelligence: dynamic drops, air gaps, harmonic anticipation, passing chords, conversation dynamics, motif memory. Four parameters (creativity, conversation, airGaps, harmonicFreedom) control how musical and surprising the output sounds
- Ensemble Coordination —
generateEnsemble()produces drums, bass, and piano as a coordinated band. Kick patterns shape bass timing, bass register guides piano voicings, drum density modulates comping activity - Seedable & Reproducible — deterministic PRNG (xoshiro128**) with per-instrument streams. Save a seed, replay the exact same take
- Jam Session Generator — random chord progressions across 17 jazz forms (blues, rhythm changes, AABA, modal, Coltrane matrix, and more)
- Walking Bass — rule-based walking lines with dissonance filtering, chromatic approaches, pattern variety, 19 styles (swing, bossa, Latin tumbao, neo-soul, math rock, IDM), proper odd-meter groupings (11/8, 7/8, 5/4)
- Piano Comping — Bill Evans rootless voicings (Type A/B), quartal, shell, cluster — all quality-aware (diatonic intervals per chord type), voice-led with rhythmic templates per style
- Drum Patterns — 19 styles with humanization, ghost notes, groove templates based on GrooVAE research. Holdsworth preset features Chad Wackerman-inspired drumming: cross-stick interjections, ghost cascades, linear fills, 11/8-specific patterns with asymmetric grouping accents
- 23 Style Presets — Classic Swing to IDM, with per-instrument style overrides and tuned musicality parameters
- Auto-Detect — analyze a score to recommend the best preset
- Full Song Form — multi-section arrangements with dynamic shaping
- Phrase-Aware Generation — 2/4/8-bar phrase boundaries with section-driven dynamics (intro sparse, shout dense)
- Streaming Iterator —
generateEnsembleMeasures()yields measure-by-measure for incremental generation - Groove Templates — structured micro-timing offsets per instrument, not random jitter
Install
npm install @jmove/generatorRequires Node.js 20+.
Quick Start
Ensemble (recommended)
import { generateJamSession, generateEnsemble, scoreChordsToEvents } from '@jmove/generator';
// Generate a 12-bar blues in Bb
const session = generateJamSession({
key: 'Bb',
form: 'blues12',
style: 'swing',
tempo: 140,
timeSignature: [4, 4],
});
// Generate coordinated ensemble (drums → bass → piano)
const chords = scoreChordsToEvents(session.score.measures);
const result = generateEnsemble({
chordEvents: chords,
style: 'swing',
tempo: 140,
measures: 12,
seed: 42, // deterministic — omit for random
});
console.log(result.drums.length, 'drum hits');
console.log(result.bass.length, 'bass notes');
console.log(result.piano.length, 'piano chords');
console.log('Replay with seed:', result.seed);Individual Generators
import {
generateWalkingBass,
generatePianoComping,
generateDrumPattern,
scoreChordsToEvents,
} from '@jmove/generator';
// Generate individual instrument parts (standalone, no coordination)
const bass = generateWalkingBass(chords, { style: 'swing', tempo: 140 });
const piano = generatePianoComping(chords, { style: 'swing', tempo: 140 });
const drums = generateDrumPattern({ style: 'swing', tempo: 140, measures: 12 });API Reference
Jam Session
generateJamSession(config: JamConfig): JamResult
Generate a complete chord progression with score.
interface JamConfig {
key: JamKey; // 'C' | 'Db' | 'D' | ... | 'B'
form: JamForm; // 'blues12' | 'rhythm32' | 'aaba32' | ...
style: PracticeStyle; // 'swing' | 'bossa' | 'funk' | ...
tempo: number; // BPM (must be > 0)
timeSignature: [number, number]; // e.g. [4, 4], [3, 4], [7, 8]
measures?: number; // override measure count (for 'free' form)
}
interface JamResult {
score: QuantizedScore; // full score with measures and chords
config: JamConfig;
progressionLabel: string; // e.g. "Bb7 | Eb7 | Bb7 | ..."
sections?: SongSection[]; // for 'fullSong' form
}transposeProgression(chords, semitones): string[][]
Transpose a chord progression by semitones.
getFormsForStyle(style): JamForm[]
Get available forms for a style (e.g. waltz styles get waltz-compatible forms).
enrichQuality(quality): string
Normalize chord quality strings (e.g. '-' → 'm', '^7' → 'maj7').
Walking Bass
generateWalkingBass(chords: ChordEvent[], options?: WalkingBassOptions): BassNote[]
Generate a walking bass line from chord events.
interface BassNote {
pitch: number; // MIDI pitch (28-55, E1-G3)
time: number; // seconds
duration: number; // seconds
velocity: number; // 0-127
}
interface WalkingBassOptions {
style?: string; // affects pattern: swing=quarter walk, bossa=root-5th, latin=tumbao
tempo?: number; // affects swing ratio and dynamics
swingAmount?: number;
density?: number;
humanize?: boolean;
}scoreChordsToEvents(measures): ChordEvent[]
Extract chord events with timing from score measures.
Piano Comping
generatePianoComping(chords: ChordEvent[], options?: PianoCompingOptions): CompNote[]
Generate piano voicings with rhythmic comping patterns.
interface CompNote {
pitches: number[]; // MIDI pitches (chord voicing)
time: number;
duration: number;
velocity: number;
}
interface PianoCompingOptions {
style?: string;
tempo?: number;
humanize?: boolean;
swingAmount?: number;
density?: number;
strum?: boolean; // arpeggiate voicings
strumMs?: number; // strum speed in ms
}Drum Patterns
generateDrumPattern(options: DrumPatternOptions): DrumHit[]
Generate a drum pattern for the specified style and duration.
interface DrumHit {
pitch: number; // GM drum map pitch
time: number; // seconds
duration: number;
velocity: number;
}
interface DrumPatternOptions {
style?: string;
tempo?: number;
measures?: number;
timeSignature?: [number, number];
humanize?: boolean;
startTime?: number;
swingAmount?: number;
density?: number;
}GM_DRUMS
General MIDI drum map constants:
GM_DRUMS.KICK // 36
GM_DRUMS.SNARE // 38
GM_DRUMS.HI_HAT_CLOSED // 42
GM_DRUMS.HI_HAT_OPEN // 46
GM_DRUMS.RIDE // 51
GM_DRUMS.CRASH // 49
GM_DRUMS.SPLASH // 55
GM_DRUMS.CHINA // 52
GM_DRUMS.TOM_HIGH // 50
GM_DRUMS.TOM_MID // 47
GM_DRUMS.TOM_LOW // 45
GM_DRUMS.TOM_FLOOR // 43
// ... and moreEnsemble
generateEnsemble(options: EnsembleOptions): EnsembleResult
Generate a coordinated ensemble with built-in alignment and phrase awareness.
interface EnsembleOptions {
chordEvents: ChordEvent[];
style: PracticeStyle;
tempo: number;
measures: number;
timeSignature?: [number, number];
sections?: SongSection[]; // section-driven dynamics
density?: number; // 0-100
swingAmount?: number; // 0-100
strumMs?: number; // 0-30
seed?: number; // omit = random, provide = deterministic
instrumentStyles?: InstrumentStyles;
creativity?: number; // 0-100: surprise frequency
conversation?: number; // 0-100: inter-instrument responsiveness
airGaps?: number; // 0-100: intentional silence
harmonicFreedom?: number; // 0-100: reharmonization intensity
drumGranular?: DrumGranular; // per-control drum overrides
pianoGranular?: PianoGranular; // per-control piano overrides
bassGranular?: BassGranular; // per-control bass overrides
}
interface EnsembleResult {
drums: DrumHit[];
bass: BassNote[];
piano: CompNote[];
seed: number; // always returned for replay
context: BandContext; // coordination state (kick times, bass register, etc.)
}generateEnsembleMeasures(options: EnsembleOptions): Generator<MeasureSlice>
Streaming version — yields one measure at a time for incremental generation.
for (const slice of generateEnsembleMeasures(options)) {
schedule(slice.drums, slice.bass, slice.piano);
}createPRNG(seed: number): RandomFn
Create a seedable random function (xoshiro128**). Pass to any generator via the random option for deterministic output.
import { createPRNG, generateDrumPattern } from '@jmove/generator';
const rng = createPRNG(42);
const drums = generateDrumPattern({ style: 'swing', measures: 4, random: rng });
// Same seed → same drums every timeComplexity Mapping
resolveDrumGranular(complexity?, overrides?): DrumGranular
resolvePianoGranular(complexity?, overrides?): PianoGranular
resolveBassGranular(complexity?, overrides?): BassGranular
Map a general complexity slider (0-100, default 50) to per-control values. Manual overrides take precedence.
import { resolveDrumGranular } from '@jmove/generator';
// Complexity 70 → busier drums, override tom frequency to max
const drum = resolveDrumGranular(70, { tomFrequency: 85 });
// drum.tomFrequency = 85 (overridden)
// drum.fillIntensity = 66 (derived from complexity 70)
// drum.rideWash = 66, drum.ghostDensity = 58, drum.cymbalColor = 48
// Pass to generateEnsemble
const result = generateEnsemble({
chordEvents, style: 'swing', tempo: 140, measures: 12,
drumGranular: drum,
});interface DrumGranular {
tomFrequency: number; // 0-100: tom probability in comping + micro-variation
fillIntensity: number; // 0-100: fill frequency and size
rideWash: number; // 0-100: ride cymbal density (sparse → dense)
ghostDensity: number; // 0-100: ghost note frequency
cymbalColor: number; // 0-100: splash/china substitution on section boundaries
}
interface PianoGranular {
voicingDensity: number; // 0-100: shell (2-note) → full (4-note) voicings
rhythmicActivity: number; // 0-100: sparse → dense comping patterns
registerRange: number; // 0-100: register drift magnitude (narrow → wide)
anticipation: number; // 0-100: harmonic anticipation probability
}
interface BassGranular {
chromaticApproach: number; // 0-100: diatonic → chromatic approach tones
registerWidth: number; // 0-100: narrow → full 2-octave range
syncopation: number; // 0-100: 8th-note enclosure fill probability
beatVariety: number; // 0-100: predictable → adventurous beat 2 choices
}Style Presets
STYLE_PRESETS: StylePreset[]
23 built-in presets:
| Category | Presets | |----------|---------| | Traditional | Classic Swing, Hard Bop Drive, West Coast Cool, Soft Ballad | | Modern | Fusion Groove, ECM Space, Miles Modal, Contemporary Jazz | | Latin | Bossa Nova, Latin Fire | | Groove | Funk Pocket, Jazz Waltz, Blues Shuffle, Neo-Soul Pocket | | Experimental | Holdsworth Fusion, Alfa Mist, Pat Metheny, Math Rock, IDM | | Hybrid | Fusion/ECM, Modal Funk, Fusion/Neo-Soul |
interface StylePreset {
id: string;
name: string;
description: string;
style: PracticeStyle;
instrumentStyles?: { // per-instrument overrides
bass?: PracticeStyle;
piano?: PracticeStyle;
drums?: PracticeStyle;
};
parameters: {
swingAmount: number; // 0-100
density: number; // 0-100
strumMs?: number; // 0-30: piano chord strum spread in ms
creativity?: number; // 0-100: surprise frequency
conversation?: number; // 0-100: inter-instrument responsiveness
airGaps?: number; // 0-100: intentional silence
harmonicFreedom?: number; // 0-100: reharmonization intensity
};
tempoRange: [number, number];
}STYLE_LABELS: Record<PracticeStyle, string>
Display names for all 19 practice styles.
STYLE_CATEGORIES: Record<string, PracticeStyle[]>
Styles grouped by category (Traditional, Modern, Latin, Groove, Experimental).
autoDetectPreset(score: QuantizedScore): StylePreset
Analyze a score and return the best-matching preset based on tempo, time signature, chord content, and style hints.
Groove & Swing Utilities
getGrooveTemplate(style): GrooveTemplate
Get micro-timing template for a style. Templates define per-instrument bias and jitter values based on GrooVAE research.
applyGroove(time, element, template): number
Apply groove displacement to a note time.
tempoSwingMultiplier(tempo): number
Tempo-dependent swing scaling. Slow tempos swing harder; fast tempos straighten out.
instrumentSwingFactor(role): number
Per-instrument swing scaling. Ride swings hardest, bass walks straighter, piano between.
humanizeTime(time, amount?): number
Add timing jitter to a note.
humanizeVelocity(velocity, amount?): number
Add velocity variation to a note.
Style Mapping
irealStyleToPracticeStyle(irealStyle): PracticeStyle
Convert iReal Pro style strings (e.g. "Medium Swing", "Bossa Nova") to generator styles.
Constants
ALL_KEYS // ['C', 'Db', 'D', ..., 'B']
FORM_LABELS // { blues12: '12-Bar Blues', ... }
FORM_MEASURE_COUNTS // { blues12: 12, rhythm32: 32, ... }
TIME_SIGNATURE_GROUPS // grouped time signatures
ALL_TIME_SIGNATURES // all supported time signaturesStyles
19 styles with distinct algorithms for bass, piano, and drums:
| Style | Swing | Bass | Piano | Drums |
|-------|-------|------|-------|-------|
| swing | Triplet swing | Quarter-note walk | Rootless voicings, syncopated | Ride + hi-hat 2&4 |
| bossa | Straight 8ths | Root-5th pattern | Montuno rhythm | Cross-stick + syncopated kick |
| latin | Straight 8ths | Tumbao pattern | Montuno variations | Cascara + clave |
| ballad | Light swing | Half-note roots | Whole/half-note voicings | Brushes feel |
| funk | Straight 16ths | Syncopated octaves | Staccato stabs | 16th hi-hat + ghost notes |
| fusion | Light swing | Chromaticism | Extended voicings | Linear patterns |
| ecm | Minimal | Sparse, open | Wide intervals | Brushes, space |
| hardBop | Heavy swing | Strong walk | Punchy voicings | Driving ride |
| coolJazz | Light swing | Melodic walk | Light touch | Brushes |
| modal | Medium swing | Pedal points | Quartal voicings | Sparse |
| jazzWaltz | Waltz swing | 3/4 walk | Waltz comp | 3/4 ride pattern |
| shuffleBlues | Triplet shuffle | Shuffle bass | Blues comping | Shuffle groove |
| neoSoul | Broken feel | Erykah-style | Glasper voicings | J Dilla pocket |
| contemporaryJazz | Moderate | Nordic clarity | Avishai Cohen trio | Brushes/sticks mix |
| holdsworth | Straight | Melodic minor | Wide voicings | Wackerman: bell ride, cross-stick, ghost cascades, 11/8-aware |
| alfaMist | Broken beat | Lo-fi chromatic | Rhodes, chromatic | Broken beat |
| metheny | Light swing | Jaco melodic | Lydian shimmer | Bob Moses brushes |
| mathRock | Straight | Angular | Staccato | Odd groupings |
| idm | Generative | Glitch patterns | Algorithmic | Generative |
Forms
17 chord progression forms:
| Form | Measures | Description |
|------|----------|-------------|
| blues12 | 12 | 12-bar blues |
| minorBlues12 | 12 | Minor blues |
| rhythm32 | 32 | Rhythm changes (I Got Rhythm) |
| aaba32 | 32 | AABA standard form |
| abac32 | 32 | ABAC form |
| modal16 | 16 | Modal vamp |
| turnaround8 | 8 | Short turnaround |
| songForm24 | 24 | Song form |
| rondo20 | 20 | Rondo form |
| clave16 | 16 | Clave-based montuno |
| secondLine16 | 16 | New Orleans second line |
| coltraneMatrix16 | 16 | Coltrane changes matrix |
| throughComposed12 | 12 | Through-composed |
| pentatonic8 | 8 | Pentatonic vamp |
| quartal16 | 16 | Quartal harmony |
| fullSong | varies | Multi-section arrangement |
| free | custom | Free form (set measures in config) |
Community Presets
Contribute new style presets! See CONTRIBUTING.md.
# Copy the template
cp preset-template.ts presets/your-preset.ts
# Edit and validate
npx tsx scripts/validate-preset.ts presets/your-preset.ts
# Submit a PRPresets are validated against preset-schema.json and smoke-tested with the generator.
Development
# Install
npm install
# Run tests (1196 tests)
npm test
# Watch mode
npm run test:watch
# Lint + type check
npm run lint
npm run typecheck
# Build (ESM + CJS + .d.ts)
npm run build
# Validate community presets
npm run validate-preset -- --allArchitecture
src/
index.ts Barrel exports (public API)
types.ts All public type definitions
ensemble.ts Ensemble coordination layer (generateEnsemble)
prng.ts Seedable PRNG (xoshiro128**)
jamGenerator.ts Chord progression generation (17 forms)
walkingBass.ts Walking bass line generation
pianoComping.ts Piano voicing + comping patterns
drumPatterns.ts Drum pattern generation (19 styles)
complexityMapping.ts Per-instrument complexity → granular controls
stylePresets.ts Built-in style presets
autoDetectPreset.ts Score analysis + preset recommendation
grooveTemplates.ts Micro-timing templates (GrooVAE-based)
swingUtils.ts Tempo/instrument swing calculations
styleMapping.ts iReal Pro style string mapping