kb: bootstrap foundation + jazz guitar gold standard
SCHEMA.md authoring contract, validate-kb.mjs quality gate (pitch-class verification of shapes against chord qualities), registry, and the jazz style: 5 progressions x 2 guitar plays (shells + drop-2), comping rhythms, improv guidance, sourced licks. Validator and vite build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -6,13 +6,13 @@ The queue for the `/kb-expand` loop. One cell per session, top-to-bottom. Protoc
|
||||
|
||||
| # | Cell | Status |
|
||||
|---|---|---|
|
||||
| 0 | Bootstrap: `src/data/kb/` + `SCHEMA.md` + `scripts/validate-kb.mjs` + `kb/index.js` + **jazz/guitar gold standard** | todo |
|
||||
| 0 | Bootstrap: `src/data/kb/` + `SCHEMA.md` + `scripts/validate-kb.mjs` + `kb/index.js` + **jazz/guitar gold standard** | done (2026-06-12, iteration 1) |
|
||||
|
||||
## Guitar
|
||||
|
||||
| # | Style | Status |
|
||||
|---|---|---|
|
||||
| 1 | Jazz (part of bootstrap) | todo |
|
||||
| 1 | Jazz (part of bootstrap) | done (2026-06-12, 5 progressions × 2 plays, validator ✓) |
|
||||
| 2 | Blues | todo |
|
||||
| 3 | Rock | todo |
|
||||
| 4 | Bossa Nova | todo |
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
// KB quality gate — validates src/data/kb/ against the contract in src/data/kb/SCHEMA.md.
|
||||
// Run: node scripts/validate-kb.mjs (exit 1 on any error)
|
||||
import { readdirSync, existsSync } from 'node:fs'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { CHORD_TYPES } from '../src/lib/theory.js'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const KB = join(ROOT, 'src', 'data', 'kb')
|
||||
|
||||
const MODES = ['major', 'minor', 'dorian', 'phrygian', 'lydian', 'mixolydian']
|
||||
const OPEN_PC = [4, 9, 2, 7, 11, 4] // EADGBe low-E first
|
||||
const PERFECT_FIFTH = 7
|
||||
const BASS_TOKENS = ['R', 'b3', '3', '5', '6', 'b7', '7', '9', 'O', 'chrom>', 'chrom<', '5>', 'x', '-']
|
||||
const MIN_PROGRESSIONS = 4
|
||||
const MIN_PLAYS = 2
|
||||
const MAX_SPAN = 4
|
||||
|
||||
const errors = []
|
||||
const err = (where, msg) => errors.push(`${where}: ${msg}`)
|
||||
|
||||
// Resolve a degree string ('3', 'b9', '13'…) to a pitch class relative to the
|
||||
// chord root, through the quality's intervals where the degree is quality-dependent.
|
||||
function resolveDegree(deg, quality) {
|
||||
const iv = CHORD_TYPES[quality].intervals
|
||||
const fixed = { 1: 0, b9: 1, 9: 2, '#9': 3, 11: 5, '#11': 6, b5: 6, b13: 8, 13: 9, 6: 9, b3: 3, b7: 10 }
|
||||
if (deg === '3') return iv.find(i => i === 3 || i === 4) ?? iv.find(i => i === 2 || i === 5) ?? null
|
||||
if (deg === '5') return iv.find(i => i === 6 || i === 7 || i === 8) ?? null
|
||||
if (deg === '7') return iv.find(i => i === 9 || i === 10 || i === 11) ?? null
|
||||
return fixed[deg] ?? null
|
||||
}
|
||||
|
||||
function checkGuitarShape(where, chordStep, quality) {
|
||||
const { shape, extensions = [] } = chordStep
|
||||
if (!shape) return err(where, 'missing shape')
|
||||
const strings = shape.offsets ?? shape.frets
|
||||
if (!Array.isArray(strings) || strings.length !== 6)
|
||||
return err(where, 'offsets/frets must be an array of 6 (low E first)')
|
||||
const isMovable = !!shape.offsets
|
||||
|
||||
if (isMovable) {
|
||||
if (!(shape.rootStr >= 1 && shape.rootStr <= 6)) return err(where, `bad rootStr ${shape.rootStr}`)
|
||||
if (strings[6 - shape.rootStr] !== 0) return err(where, 'offset on the root string must be 0')
|
||||
} else {
|
||||
if (!(shape.onlyRoot >= 0 && shape.onlyRoot <= 11)) return err(where, 'open shape needs onlyRoot (pc 0-11)')
|
||||
}
|
||||
|
||||
const fretted = strings.filter(f => f !== 'x')
|
||||
if (fretted.some(f => !Number.isInteger(f) || f < -2 || f > 15))
|
||||
return err(where, `bad fret values: ${JSON.stringify(strings)}`)
|
||||
const nonOpen = fretted.filter(f => f !== 0)
|
||||
if (nonOpen.length && Math.max(...nonOpen) - Math.min(...nonOpen) > MAX_SPAN)
|
||||
return err(where, `fret span > ${MAX_SPAN} — not intermediate-friendly`)
|
||||
|
||||
// Pitch-class verification: every sounded note must belong to the chord
|
||||
// (quality intervals + declared extensions); defining tones must be present.
|
||||
const iv = CHORD_TYPES[quality].intervals
|
||||
const allowed = new Set(iv)
|
||||
for (const ext of extensions) {
|
||||
const pc = resolveDegree(ext, quality)
|
||||
if (pc === null) return err(where, `unresolvable extension '${ext}' for ${quality}`)
|
||||
allowed.add(pc)
|
||||
}
|
||||
const rootRel = isMovable ? (12 - OPEN_PC[6 - shape.rootStr]) % 12 : null
|
||||
const sounded = new Set()
|
||||
strings.forEach((f, i) => {
|
||||
if (f === 'x') return
|
||||
const pc = isMovable
|
||||
? (OPEN_PC[i] + rootRel + f + 24) % 12
|
||||
: (OPEN_PC[i] + f - shape.onlyRoot + 24) % 12
|
||||
sounded.add(pc)
|
||||
})
|
||||
for (const pc of sounded)
|
||||
if (!allowed.has(pc)) return err(where, `sounded pc ${pc} is not in ${quality} (+ext) — shape misspells the chord`)
|
||||
const required = iv.filter(i => i !== PERFECT_FIFTH && !(chordStep.rootless && i === 0))
|
||||
for (const pc of required)
|
||||
if (!sounded.has(pc)) return err(where, `defining tone pc ${pc} of ${quality} missing from shape`)
|
||||
}
|
||||
|
||||
function checkPianoRecipe(where, chordStep, quality) {
|
||||
const { recipe } = chordStep
|
||||
if (!recipe) return err(where, 'missing recipe')
|
||||
for (const hand of ['LH', 'RH']) {
|
||||
const degs = recipe[hand]
|
||||
if (degs === undefined) continue
|
||||
if (!Array.isArray(degs) || !degs.length) return err(where, `${hand} must be a non-empty array`)
|
||||
if (degs.length > 5) return err(where, `${hand} has ${degs.length} notes — one hand, max 5`)
|
||||
for (const d of degs)
|
||||
if (resolveDegree(d, quality) === null) err(where, `unresolvable degree '${d}' for ${quality}`)
|
||||
}
|
||||
if (recipe.LH === undefined && recipe.RH === undefined) err(where, 'recipe needs LH and/or RH')
|
||||
}
|
||||
|
||||
function checkBassPlay(where, play, prog) {
|
||||
const totalBars = prog.bars.reduce((a, b) => a + b, 0)
|
||||
if (!Array.isArray(play.bars) || play.bars.length !== totalBars)
|
||||
return err(where, `bars length ${play.bars?.length} ≠ progression total ${totalBars}`)
|
||||
play.bars.forEach((bar, i) => {
|
||||
if (!Array.isArray(bar.beats) || !bar.beats.length) return err(`${where} bar ${i}`, 'missing beats')
|
||||
for (const b of bar.beats)
|
||||
if (!BASS_TOKENS.includes(b)) err(`${where} bar ${i}`, `unknown beat token '${b}'`)
|
||||
})
|
||||
}
|
||||
|
||||
async function loadModule(path) {
|
||||
return (await import(pathToFileURL(path).href)).default
|
||||
}
|
||||
|
||||
const styleDirs = readdirSync(KB, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name)
|
||||
if (!styleDirs.length) { console.error('No style folders in src/data/kb/'); process.exit(1) }
|
||||
|
||||
const registry = existsSync(join(KB, 'index.js')) ? await loadModule(join(KB, 'index.js')) : null
|
||||
if (!registry) err('kb/index.js', 'registry missing')
|
||||
|
||||
const allIds = new Set()
|
||||
let totals = { styles: 0, progressions: 0, plays: 0 }
|
||||
|
||||
for (const style of styleDirs) {
|
||||
const dir = join(KB, style)
|
||||
const w = `kb/${style}`
|
||||
if (registry && !registry[style]) err('kb/index.js', `style '${style}' not registered`)
|
||||
|
||||
const meta = existsSync(join(dir, 'meta.js')) ? await loadModule(join(dir, 'meta.js')) : null
|
||||
if (!meta) { err(w, 'meta.js missing'); continue }
|
||||
if (meta.id !== style) err(`${w}/meta.js`, `id '${meta.id}' ≠ folder '${style}'`)
|
||||
for (const f of ['label', 'feel', 'character']) if (!meta[f]) err(`${w}/meta.js`, `missing ${f}`)
|
||||
|
||||
const progs = existsSync(join(dir, 'progressions.js')) ? await loadModule(join(dir, 'progressions.js')) : null
|
||||
if (!Array.isArray(progs) || !progs.length) { err(w, 'progressions.js missing/empty'); continue }
|
||||
if (progs.length < MIN_PROGRESSIONS) err(w, `${progs.length} progressions < ${MIN_PROGRESSIONS}`)
|
||||
|
||||
const progById = {}
|
||||
for (const p of progs) {
|
||||
const pw = `${w}/progressions.js [${p.id}]`
|
||||
if (!p.id?.startsWith(`${style}-`)) err(pw, `id must start with '${style}-'`)
|
||||
if (allIds.has(p.id)) err(pw, 'duplicate id'); allIds.add(p.id)
|
||||
progById[p.id] = p
|
||||
const n = p.degrees?.length
|
||||
if (!n) { err(pw, 'degrees missing'); continue }
|
||||
for (const [field, arr] of [['rn', p.rn], ['qualities', p.qualities], ['bars', p.bars]])
|
||||
if (!Array.isArray(arr) || arr.length !== n) err(pw, `${field} length ≠ degrees length`)
|
||||
if (p.degrees.some(d => !Number.isInteger(d) || d < 0 || d > 11)) err(pw, 'degrees must be ints 0-11')
|
||||
for (const q of p.qualities ?? []) if (!CHORD_TYPES[q]) err(pw, `unknown quality '${q}'`)
|
||||
if (!MODES.includes(p.mode)) err(pw, `unknown mode '${p.mode}'`)
|
||||
if (!Array.isArray(p.songs) || !p.songs.length) err(pw, 'songs missing')
|
||||
if (!p.tip) err(pw, 'tip missing')
|
||||
}
|
||||
totals.styles++; totals.progressions += progs.length
|
||||
|
||||
for (const inst of ['guitar', 'piano', 'bass']) {
|
||||
const file = join(dir, `${inst}.js`)
|
||||
if (!existsSync(file)) continue
|
||||
const pack = await loadModule(file)
|
||||
const iw = `${w}/${inst}.js`
|
||||
if (!pack.styleIntro) err(iw, 'styleIntro missing')
|
||||
if (!Array.isArray(pack.comping) || !pack.comping.length) err(iw, 'comping missing')
|
||||
if (inst !== 'bass' && (!pack.improv?.scales?.length || !pack.improv?.targetNotes))
|
||||
err(iw, 'improv.scales / improv.targetNotes required')
|
||||
|
||||
for (const p of progs)
|
||||
if ((pack.plays?.[p.id]?.length ?? 0) < MIN_PLAYS)
|
||||
err(iw, `progression '${p.id}' has < ${MIN_PLAYS} plays`)
|
||||
|
||||
for (const [pid, plays] of Object.entries(pack.plays ?? {})) {
|
||||
const prog = progById[pid]
|
||||
if (!prog) { err(iw, `plays key '${pid}' is not a progression of this style`); continue }
|
||||
plays.forEach((play, pi) => {
|
||||
const lw = `${iw} ${pid} play[${pi}] "${play.label ?? '?'}"`
|
||||
if (!play.label || !play.level || !play.tips) err(lw, 'label/level/tips required')
|
||||
totals.plays++
|
||||
if (inst === 'bass') return checkBassPlay(lw, play, prog)
|
||||
if (!Array.isArray(play.chords) || play.chords.length !== prog.degrees.length)
|
||||
return err(lw, `chords length ≠ progression length ${prog.degrees.length}`)
|
||||
play.chords.forEach((step, ci) => {
|
||||
const cw = `${lw} chord[${ci}] (${prog.rn[ci]})`
|
||||
if (inst === 'guitar') checkGuitarShape(cw, step, prog.qualities[ci])
|
||||
else checkPianoRecipe(cw, step, prog.qualities[ci])
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
console.error(`✗ KB validation failed — ${errors.length} error(s):\n`)
|
||||
for (const e of errors) console.error(' ' + e)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`✓ KB valid — ${totals.styles} style(s), ${totals.progressions} progressions, ${totals.plays} plays`)
|
||||
@@ -0,0 +1,133 @@
|
||||
# KB Authoring Contract
|
||||
|
||||
Every knowledgebase cell must conform to this schema and pass `node scripts/validate-kb.mjs`. The gold-standard exemplar is `jazz/` — imitate it. Background and rationale: `docs/kb-plan.md`.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
src/data/kb/<style>/
|
||||
meta.js — style identity
|
||||
progressions.js — the style's standard progressions (instrument-independent)
|
||||
guitar.js — instrument packs (piano.js, bass.js as cells are completed)
|
||||
```
|
||||
|
||||
Register each style in `src/data/kb/index.js`. The UI reads only the registry.
|
||||
|
||||
## Hard rules
|
||||
|
||||
1. **Key-agnostic.** Degrees and movable shapes only — never absolute chord names in data. Open guitar shapes are the one exception (they declare `onlyRoot`, a pitch class, and render only in matching keys).
|
||||
2. **Qualities** must be keys of `CHORD_TYPES` in `src/lib/theory.js` (`maj`, `min`, `dom7`, `maj7`, `min7`, `dim`, `dim7`, `half_dim`, `aug`, `sus4`, `sus2`, `maj6`, `min6`, `add9`).
|
||||
3. **Intermediate level.** Guitar: fret span ≤ 4 within a shape. Piano: one hand per recipe stays within a 10th. If a play is harder, provide an easier alternative in the same play set.
|
||||
4. Plays for the same progression must be **idiomatically different** (register, density, technique) — not transpositions of each other.
|
||||
|
||||
## meta.js
|
||||
|
||||
```js
|
||||
export default {
|
||||
id: 'jazz', // folder name
|
||||
label: 'Jazz',
|
||||
feel: 'swing', // swing | straight | shuffle | 16th | bossa…
|
||||
tempoRange: [110, 230],
|
||||
character: 'One sentence on what makes the style sound like itself.',
|
||||
}
|
||||
```
|
||||
|
||||
## progressions.js
|
||||
|
||||
```js
|
||||
export default [
|
||||
{
|
||||
id: 'jazz-251-major', // '<style>-<slug>', globally unique
|
||||
name: 'ii–V–I',
|
||||
rn: ['ii7', 'V7', 'Imaj7'], // display numerals
|
||||
degrees: [2, 7, 0], // semitone offsets from key root, 0–11
|
||||
qualities: ['min7', 'dom7', 'maj7'],
|
||||
bars: [1, 1, 2], // same length as degrees
|
||||
mode: 'major', // major | minor | dorian | mixolydian | …
|
||||
songs: ['Autumn Leaves'],
|
||||
tip: 'One transferable idea.',
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
4–8 progressions per style. Cross-check `docs/progression-repertoire.md` §1.
|
||||
|
||||
## Instrument packs
|
||||
|
||||
Common envelope:
|
||||
|
||||
```js
|
||||
export default {
|
||||
styleIntro: '2-3 sentences on this instrument's role in the style.',
|
||||
comping: [{ label, rhythm, description }], // ≥1 named rhythm
|
||||
plays: { '<progression-id>': [ <play>, <play> ] }, // ≥2 plays per progression
|
||||
improv: { // guitar/piano; optional for bass
|
||||
scales: [{ over: 'ii7', scale: 'dorian', why }],
|
||||
targetNotes: '…',
|
||||
licks: [{ tab/notation, description, over: '<progression-id>', source }],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Guitar play
|
||||
|
||||
```js
|
||||
{
|
||||
label: 'Shell voicings',
|
||||
level: 'intermediate',
|
||||
chords: [ // one per progression step
|
||||
{
|
||||
shape: {
|
||||
// movable: fret offsets relative to the root fret; 'x' = muted
|
||||
rootStr: 6, // string carrying the root, 6 = low E
|
||||
offsets: [0, 'x', 0, 0, 'x', 'x'], // ALWAYS 6 entries, low E first
|
||||
fingers: [1, 0, 2, 3, 0, 0],
|
||||
// open shapes instead use: frets: [...absolute], onlyRoot: <pc 0-11>
|
||||
},
|
||||
extensions: ['9'], // declared color tones beyond the quality (validator allows only these)
|
||||
note: 'root–♭7–♭3',
|
||||
},
|
||||
// …
|
||||
],
|
||||
tips: 'Voice-leading or ensemble advice.',
|
||||
}
|
||||
```
|
||||
|
||||
### Piano play
|
||||
|
||||
Voicings are degree recipes resolved through the chord quality. Degrees: `'1' '3' '5' '7'` resolve per quality (e.g. `'3'` → ♭3 for min7); altered/extended degrees are explicit: `'b9' '9' '#9' '11' '#11' 'b13' '13' '6'`.
|
||||
|
||||
```js
|
||||
{
|
||||
label: 'Rootless A/B alternation',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ recipe: { LH: ['3', '5', '7', '9'] }, note: 'Type A' },
|
||||
{ recipe: { LH: ['7', '9', '3', '13'] }, note: 'Type B' },
|
||||
],
|
||||
register: 'top note between C4 and C5',
|
||||
tips: '…',
|
||||
}
|
||||
```
|
||||
|
||||
### Bass play
|
||||
|
||||
```js
|
||||
{
|
||||
label: 'Walking, chromatic approach',
|
||||
level: 'intermediate',
|
||||
bars: [{ beats: ['R', '3', '5', 'chrom>'] }], // per bar of the progression
|
||||
// beat tokens: R 3 5 7 (chord degrees) · 'chrom>' / 'chrom<' (chromatic into next root
|
||||
// from below/above) · '5>' (dominant approach) · 'x' (ghost) · '-' (hold)
|
||||
tips: '…',
|
||||
}
|
||||
```
|
||||
|
||||
## Musician checklist (self-review before committing)
|
||||
|
||||
- [ ] Plays per progression genuinely differ in register/density/technique
|
||||
- [ ] Every shape/recipe is playable by intermediate hands (rule 3)
|
||||
- [ ] The style is recognizable from the rhythm descriptions alone (bossa ≠ jazz with new labels)
|
||||
- [ ] Every tip teaches a transferable idea (voice leading, register, space), not just "play this"
|
||||
- [ ] Songs/licks have sources; nothing invented
|
||||
- [ ] `node scripts/validate-kb.mjs` green; `npm run build` green
|
||||
@@ -0,0 +1,13 @@
|
||||
// KB registry — the UI reads only this. Each style folder registers here;
|
||||
// instruments appear as their cells are completed (see docs/kb-backlog.md).
|
||||
import jazzMeta from './jazz/meta.js'
|
||||
import jazzProgressions from './jazz/progressions.js'
|
||||
import jazzGuitar from './jazz/guitar.js'
|
||||
|
||||
export default {
|
||||
jazz: {
|
||||
meta: jazzMeta,
|
||||
progressions: jazzProgressions,
|
||||
instruments: { guitar: jazzGuitar },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// Jazz guitar pack — gold-standard KB cell. Shapes verified by note-spelling
|
||||
// against: jazzguitar.be (shell chords, drop-2, comping rhythms), freddiegreen.org,
|
||||
// jenslarsen.nl (comping rhythms, voice leading), premierguitar.com (drop-2).
|
||||
|
||||
// Shell voicings (Freddie Green style) — root + 3rd + 7th, fifths omitted.
|
||||
const SHELL_6 = { // root on low E string
|
||||
maj7: { rootStr: 6, offsets: [0, 'x', 1, 1, 'x', 'x'], fingers: [1, 0, 3, 4, 0, 0] }, // R–7–3
|
||||
dom7: { rootStr: 6, offsets: [0, 'x', 0, 1, 'x', 'x'], fingers: [1, 0, 2, 3, 0, 0] }, // R–♭7–3
|
||||
min7: { rootStr: 6, offsets: [0, 'x', 0, 0, 'x', 'x'], fingers: [1, 0, 2, 3, 0, 0] }, // R–♭7–♭3
|
||||
half_dim: { rootStr: 6, offsets: [0, 'x', 0, 0, -1, 'x'], fingers: [2, 0, 3, 4, 1, 0] }, // R–♭7–♭3–♭5
|
||||
}
|
||||
const SHELL_5 = { // root on A string
|
||||
maj7: { rootStr: 5, offsets: ['x', 0, -1, 1, 'x', 'x'], fingers: [0, 2, 1, 4, 0, 0] }, // R–3–7
|
||||
dom7: { rootStr: 5, offsets: ['x', 0, -1, 0, 'x', 'x'], fingers: [0, 2, 1, 3, 0, 0] }, // R–3–♭7
|
||||
min7: { rootStr: 5, offsets: ['x', 0, -2, 0, 'x', 'x'], fingers: [0, 3, 1, 4, 0, 0] }, // R–♭3–♭7
|
||||
half_dim: { rootStr: 5, offsets: ['x', 0, 1, 0, 1, 'x'], fingers: [0, 1, 3, 2, 4, 0] }, // R–♭5–♭7–♭3
|
||||
}
|
||||
|
||||
// Drop-2 voicings on the top four strings (D–G–B–e) — stays out of the bass register.
|
||||
const DROP2 = {
|
||||
maj7Root: { rootStr: 4, offsets: ['x', 'x', 0, 2, 2, 2], fingers: [0, 0, 1, 3, 3, 3] }, // R–5–7–3
|
||||
dom7Root: { rootStr: 4, offsets: ['x', 'x', 0, 2, 1, 2], fingers: [0, 0, 1, 3, 2, 4] }, // R–5–♭7–3
|
||||
min7Root: { rootStr: 4, offsets: ['x', 'x', 0, 2, 1, 1], fingers: [0, 0, 1, 4, 2, 3] }, // R–5–♭7–♭3
|
||||
halfDimRoot: { rootStr: 4, offsets: ['x', 'x', 0, 1, 1, 1], fingers: [0, 0, 1, 2, 3, 4] }, // R–♭5–♭7–♭3
|
||||
min7Inv3: { rootStr: 1, offsets: ['x', 'x', 0, 0, 0, 0], fingers: [0, 0, 1, 1, 1, 1] }, // ♭7–♭3–5–R (one-finger barre)
|
||||
dom7Inv2: { rootStr: 2, offsets: ['x', 'x', 1, 2, 0, 2], fingers: [0, 0, 2, 3, 1, 4] }, // 3–♭7–R–5
|
||||
maj7Inv2: { rootStr: 1, offsets: ['x', 'x', 1, 1, 0, 0], fingers: [0, 0, 2, 3, 1, 1] }, // 7–3–5–R
|
||||
}
|
||||
|
||||
export default {
|
||||
styleIntro:
|
||||
'In a jazz jam the guitar is part of the rhythm section: small voicings built on 3rds and 7ths, placed around the soloist, never on top of the piano. The fifths and often the roots are someone else\'s job — your two guide tones carry the whole harmony.',
|
||||
|
||||
comping: [
|
||||
{
|
||||
label: 'Four-to-the-bar (Freddie Green)',
|
||||
rhythm: '♩ ♩ ♩ ♩',
|
||||
description: 'Short, percussive quarter-note strums on all four beats, slight accent on 2 and 4 — the Count Basie pulse. Damp the unused strings; the chunk matters more than the chord.',
|
||||
},
|
||||
{
|
||||
label: 'Charleston',
|
||||
rhythm: '𝅗𝅥. + "and of 2"',
|
||||
description: 'Hit on beat 1 (held) plus a stab on the and-of-2 — the foundational syncopated comping cell. Displace it ("and of 1" + beat 3) for forward motion.',
|
||||
},
|
||||
{
|
||||
label: 'The push (anticipated and-of-4)',
|
||||
rhythm: 'tied from "and of 4"',
|
||||
description: 'Strike the next bar\'s chord an eighth note early and tie it over the barline — the standard jazz anticipation. Telegraphs the change to the whole band.',
|
||||
},
|
||||
],
|
||||
|
||||
plays: {
|
||||
'jazz-251-major': [
|
||||
{
|
||||
label: 'Shell voicings, guide-tone glue',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: SHELL_5.min7, note: 'R–♭3–♭7' },
|
||||
{ shape: SHELL_6.dom7, note: '♭7 of ii holds; ♭3 falls a half-step to become the 3rd' },
|
||||
{ shape: SHELL_5.maj7, note: '♭7 of V falls a half-step to the 3rd; the other voice holds' },
|
||||
],
|
||||
tips: 'Only the roots jump — both upper voices move 0 or 1 fret across the whole progression. Watch the D and G strings: that two-note thread is the ii–V–I.',
|
||||
},
|
||||
{
|
||||
label: 'Drop-2 in one position (top four strings)',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: DROP2.min7Inv3, note: 'one-finger barre: ♭7–♭3–5–R' },
|
||||
{ shape: DROP2.dom7Inv2, note: 'every voice moves 0–2 frets' },
|
||||
{ shape: DROP2.maj7Inv2, note: 'lands with the root on top' },
|
||||
],
|
||||
tips: 'The whole progression sits in one 3-fret window with no position jump — ideal when a piano is holding the low end. Great behind a singer: high, thin, out of the way.',
|
||||
},
|
||||
],
|
||||
|
||||
'jazz-251-minor': [
|
||||
{
|
||||
label: 'Shell voicings with the ♭5 voiced',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: SHELL_6.half_dim, note: 'the ♭5 on the B string is the colour — don\'t skip it' },
|
||||
{ shape: SHELL_5.dom7, extensions: ['b9'], note: 'add the ♭9 a fret above the root for the full minor-key sound' },
|
||||
{ shape: SHELL_6.min7, note: 'home — resolve and get light' },
|
||||
],
|
||||
tips: 'The ♭5 of the iiø7 *is* the ♭9 of the V7 — same pitch, reinterpreted. Find it once, hold it through both chords.',
|
||||
},
|
||||
{
|
||||
label: 'Drop-2, top-four strings',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: DROP2.halfDimRoot, note: 'root + one-finger barre' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
{ shape: DROP2.min7Inv3, note: 'one-finger barre to rest on' },
|
||||
],
|
||||
tips: 'Both barre grips bookending this make the iiø7 the only real stretch — practise the V7 grip as the pivot between them.',
|
||||
},
|
||||
],
|
||||
|
||||
'jazz-rhythm-a': [
|
||||
{
|
||||
label: 'Shells, four-to-the-bar',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: SHELL_6.maj7, note: '' },
|
||||
{ shape: SHELL_5.min7, note: '' },
|
||||
{ shape: SHELL_5.min7, note: 'same grip, two frets down from vi' },
|
||||
{ shape: SHELL_6.dom7, note: '' },
|
||||
],
|
||||
tips: 'One chord per bar, four chunks per bar, Freddie Green style. At rhythm-changes tempo the small shapes are the only ones that keep up.',
|
||||
},
|
||||
{
|
||||
label: 'Drop-2 turnaround, upper register',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: DROP2.maj7Root, note: '' },
|
||||
{ shape: DROP2.min7Inv3, note: '' },
|
||||
{ shape: DROP2.min7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
],
|
||||
tips: 'A loop, not a line — bar 4 feeds bar 1. Practise it as one circular hand motion until the join disappears.',
|
||||
},
|
||||
],
|
||||
|
||||
'jazz-625': [
|
||||
{
|
||||
label: 'Shells, alternating root strings',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: SHELL_6.min7, note: '' },
|
||||
{ shape: SHELL_5.min7, note: '' },
|
||||
{ shape: SHELL_6.dom7, note: '' },
|
||||
{ shape: SHELL_5.maj7, note: '' },
|
||||
],
|
||||
tips: 'Roots falling in fifths alternate 6th string → 5th string at the same fret — the progression stays in one position by construction. This is why shells were built for circle-of-fifths tunes.',
|
||||
},
|
||||
{
|
||||
label: 'Drop-2 circle, top-four strings',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: DROP2.min7Inv3, note: '' },
|
||||
{ shape: DROP2.min7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
{ shape: DROP2.maj7Inv2, note: '' },
|
||||
],
|
||||
tips: 'Sing the top note of each grip as you move — drop-2 makes the melody line on the e string audible, and that line is what the soloist hears from you.',
|
||||
},
|
||||
],
|
||||
|
||||
'jazz-blues': [
|
||||
{
|
||||
label: 'Shells through the form',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: SHELL_6.dom7, note: 'I7 — root on the 6th string' },
|
||||
{ shape: SHELL_5.dom7, note: 'IV7 — same fret, root string up' },
|
||||
{ shape: SHELL_6.dom7, note: '' },
|
||||
{ shape: SHELL_6.dom7, note: '' },
|
||||
{ shape: SHELL_5.dom7, note: '' },
|
||||
{ shape: SHELL_5.dom7, note: '' },
|
||||
{ shape: SHELL_6.dom7, note: '' },
|
||||
{ shape: SHELL_5.dom7, note: 'VI7 — the jazz move; hear bar 8 coming' },
|
||||
{ shape: SHELL_5.min7, note: 'ii7 of the turnaround' },
|
||||
{ shape: SHELL_6.dom7, note: 'V7' },
|
||||
{ shape: SHELL_6.dom7, note: 'home' },
|
||||
{ shape: SHELL_6.dom7, note: 'V7 pickup into the next chorus' },
|
||||
],
|
||||
tips: 'I7 and IV7 sit at the same fret on adjacent root strings — the first four bars are a two-finger-move exercise. Keep everything within two frets of the I.',
|
||||
},
|
||||
{
|
||||
label: 'Drop-2 blues, top-four strings',
|
||||
level: 'intermediate',
|
||||
chords: [
|
||||
{ shape: DROP2.dom7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: 'IV7 without leaving the position' },
|
||||
{ shape: DROP2.dom7Root, note: '' },
|
||||
{ shape: DROP2.dom7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
{ shape: DROP2.dom7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: 'VI7' },
|
||||
{ shape: DROP2.min7Inv3, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
{ shape: DROP2.dom7Root, note: '' },
|
||||
{ shape: DROP2.dom7Inv2, note: '' },
|
||||
],
|
||||
tips: 'Comping above the 7th fret leaves the whole low end to bass and piano — the classic organ-trio guitar register. Charleston rhythm, not four-to-the-bar, up here.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
improv: {
|
||||
scales: [
|
||||
{ over: 'ii7', scale: 'dorian', why: 'Minor 7 chords in a major key take Dorian — the natural 6 keeps it from sounding sad.' },
|
||||
{ over: 'V7', scale: 'mixolydian', why: 'The ♭7 is built in; in minor keys use Phrygian dominant (harmonic minor from the V) for the ♭9 sound.' },
|
||||
{ over: 'Imaj7', scale: 'major', why: 'Plain major works; avoid sitting on the 4th over the maj7.' },
|
||||
{ over: 'I7 (blues)', scale: 'mixolydian', why: 'Mix with the blues scale — Mixolydian for the changes, blues scale for the attitude.' },
|
||||
{ over: 'iiø7', scale: 'locrian', why: 'Target the ♭3 or ♭5; the ♭5 becomes the ♭9 of the next V7.' },
|
||||
],
|
||||
targetNotes:
|
||||
'Land the 3rd of each chord on the downbeat of the change. In any ii–V–I the 7th of one chord falls a half-step to the 3rd of the next — that two-note rail is the whole map.',
|
||||
licks: [
|
||||
{
|
||||
over: 'jazz-251-major',
|
||||
description: '"The Lick" — the most famous ii–V cliché in jazz (Parker, Coltrane, everyone). Degrees 1–2–♭3–4–2–♭7–1 over the ii chord.',
|
||||
tab: 'e|--------------------------\nB|--------------------------\nG|--------------------------\nD|----2--3--5--2------------\nA|-5--------------3--5------\nE|--------------------------\n D E F G E C D (over Dm7 in C)',
|
||||
source: 'Wikipedia: "The Lick"; Alex Heitlinger compilation (2011)',
|
||||
},
|
||||
{
|
||||
over: 'jazz-251-major',
|
||||
description: 'Stock bebop ii–V–I: ii arpeggio up, then the 3–5–♭7–♭9 diminished arpeggio over the V7 (B–D–F–A♭ over G7), resolving half-step into the I.',
|
||||
tab: 'e|--------------------------|------------4--3---------|--------\nB|----------------5--3------|----3--6----------6--3----|--1-----\nG|-------------5---------5--|-4---------------------4--|--------\nD|----3--7---------------7--|--------------------------|--------\nA|-5------------------------|--------------------------|--------\nE|--------------------------|--------------------------|--------\n Dm7 arpeggio + 9th G7: 3-5-♭7-♭9 dim arp Cmaj7',
|
||||
source: 'David Baker, How to Play Bebop Vol. 1; jazzguitar.be "50 Bebop Licks"',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
id: 'jazz',
|
||||
label: 'Jazz',
|
||||
feel: 'swing',
|
||||
tempoRange: [110, 230],
|
||||
character: 'Harmony in constant motion — 7th chords everywhere, 3rds and 7ths doing the voice-leading work, rhythm section breathing around the soloist.',
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export default [
|
||||
{
|
||||
id: 'jazz-251-major',
|
||||
name: 'ii–V–I',
|
||||
rn: ['ii7', 'V7', 'Imaj7'],
|
||||
degrees: [2, 7, 0],
|
||||
qualities: ['min7', 'dom7', 'maj7'],
|
||||
bars: [1, 1, 2],
|
||||
mode: 'major',
|
||||
songs: ['All The Things You Are', 'Tune Up — Miles Davis', 'Honeysuckle Rose'],
|
||||
tip: 'The 7th of each chord resolves down a half-step to the 3rd of the next — that two-note thread is the whole progression.',
|
||||
},
|
||||
{
|
||||
id: 'jazz-251-minor',
|
||||
name: 'minor ii–V–i',
|
||||
rn: ['iiø7', 'V7', 'i7'],
|
||||
degrees: [2, 7, 0],
|
||||
qualities: ['half_dim', 'dom7', 'min7'],
|
||||
bars: [1, 1, 2],
|
||||
mode: 'minor',
|
||||
songs: ['Autumn Leaves (bridge)', 'Blue Bossa', 'Beautiful Love'],
|
||||
tip: 'Same engine as the major ii–V–I, darker fuel: the ♭5 of the iiø7 is the ♭9 colour waiting to happen on the V7.',
|
||||
},
|
||||
{
|
||||
id: 'jazz-rhythm-a',
|
||||
name: 'Rhythm changes turnaround',
|
||||
rn: ['Imaj7', 'vi7', 'ii7', 'V7'],
|
||||
degrees: [0, 9, 2, 7],
|
||||
qualities: ['maj7', 'min7', 'min7', 'dom7'],
|
||||
bars: [1, 1, 1, 1],
|
||||
mode: 'major',
|
||||
songs: ['I Got Rhythm — Gershwin', 'Oleo — Sonny Rollins', 'Blue Moon'],
|
||||
tip: 'A loop, not a line — bar 4 hands you straight back to bar 1. Learn it as one circular shape your hands repeat.',
|
||||
},
|
||||
{
|
||||
id: 'jazz-625',
|
||||
name: 'vi–ii–V–I circle',
|
||||
rn: ['vi7', 'ii7', 'V7', 'Imaj7'],
|
||||
degrees: [9, 2, 7, 0],
|
||||
qualities: ['min7', 'min7', 'dom7', 'maj7'],
|
||||
bars: [1, 1, 1, 1],
|
||||
mode: 'major',
|
||||
songs: ['Fly Me to the Moon', 'Autumn Leaves (A section, relative view)'],
|
||||
tip: 'Pure circle-of-fifths motion: every root falls a fifth. If you can hear this one, you can predict half the jazz repertoire.',
|
||||
},
|
||||
{
|
||||
id: 'jazz-blues',
|
||||
name: 'Jazz blues (12-bar)',
|
||||
rn: ['I7', 'IV7', 'I7', 'I7', 'IV7', 'IV7', 'I7', 'VI7', 'ii7', 'V7', 'I7', 'V7'],
|
||||
degrees: [0, 5, 0, 0, 5, 5, 0, 9, 2, 7, 0, 7],
|
||||
qualities: ['dom7', 'dom7', 'dom7', 'dom7', 'dom7', 'dom7', 'dom7', 'dom7', 'min7', 'dom7', 'dom7', 'dom7'],
|
||||
bars: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
mode: 'major',
|
||||
songs: ["Billie's Bounce — Charlie Parker", "Now's the Time — Charlie Parker", 'Tenor Madness — Sonny Rollins'],
|
||||
tip: 'A 12-bar blues wearing a suit: bars 8-10 swap the plain V-IV for a VI7 → ii–V turnaround. Hear bar 8 coming and you sound like a jazz player.',
|
||||
},
|
||||
]
|
||||
Reference in New Issue
Block a user