Skip to content

Instantly share code, notes, and snippets.

@kennethreitz
Last active March 26, 2026 15:44
Show Gist options
  • Select an option

  • Save kennethreitz/009d56ad9325b710489874714cf20197 to your computer and use it in GitHub Desktop.

Select an option

Save kennethreitz/009d56ad9325b710489874714cf20197 to your computer and use it in GitHub Desktop.
Mixing major and minor pentatonic over the same blues changes — PyTheory
"""Mixing major and minor pentatonic over the same changes.
"Once you start mixing major and minor pentatonic over the same changes
it's a whole different world. That's where the magic lives."
Uses PyTheory's blues scale system — the notes come from the scales,
not hardcoded. Change the tonic and it works in any key.
Requires: pip install pytheory
brew install portaudio (macOS)
"""
import random
from pytheory import Score, Pattern, Duration, Chord, TonedScale
from pytheory.play import render_score, SAMPLE_RATE
import sounddevice as sd
# ── The two scales ──────────────────────────────────────────────────
TONIC = "A"
ts = TonedScale(tonic=f"{TONIC}4", system="blues")
minor_pent = ts["minor pentatonic"]
major_pent = ts["major pentatonic"]
minor_notes = minor_pent.note_names[:-1] # A C D E G
major_notes = major_pent.note_names[:-1] # A B C# E F#
print(f" {TONIC} minor pentatonic: {' '.join(minor_notes)}")
print(f" {TONIC} major pentatonic: {' '.join(major_notes)}")
print()
# ── Score ───────────────────────────────────────────────────────────
score = Score("4/4", bpm=90, drum_humanize=0.2)
score.drums("shuffle", repeats=8)
rhodes = score.part(
"rhodes", synth="fm", envelope="piano",
volume=0.25, pan=-0.3,
reverb=0.3, reverb_type="plate",
humanize=0.2,
)
lead = score.part(
"lead", synth="triangle", envelope="strings",
volume=0.45, pan=0.2,
legato=True, glide=0.06,
reverb=0.25, reverb_type="plate",
delay=0.15, delay_time=0.33, delay_feedback=0.3,
humanize=0.2,
)
bass = score.part(
"bass", synth="sine", envelope="pluck",
volume=0.4, lowpass=500, humanize=0.15,
)
# ── Chords: simple blues in the tonic ──
for sym in [f"{TONIC}7"] * 2 + [f"D7"] * 2 + [f"{TONIC}7"] * 2 + ["E7", f"{TONIC}7"]:
rhodes.add(Chord.from_symbol(sym), Duration.WHOLE)
# ── Bass: root-fifth from chord tones ──
for sym in [f"{TONIC}7"] * 2 + ["D7"] * 2 + [f"{TONIC}7"] * 2 + ["E7", f"{TONIC}7"]:
c = Chord.from_symbol(sym)
r = c.root
fifth = r.add(7)
bass.add(f"{r.name}2", Duration.QUARTER, velocity=95)
bass.add(f"{r.name}2", Duration.QUARTER, velocity=55)
bass.add(f"{fifth.name}2", Duration.QUARTER, velocity=65)
bass.add(f"{r.name}2", Duration.QUARTER, velocity=75)
# ── Lead: phrase generator mixing both pentatonics ──
def phrase(notes, octave, beats, style="melodic"):
"""Generate a phrase from a set of scale notes."""
result = []
remaining = beats
while remaining > 0.5:
if random.random() < 0.2:
r = random.choice([0.5, 1.0])
r = min(r, remaining)
result.append((None, 0, r))
remaining -= r
else:
n = random.choice(notes)
d = random.choice([0.67, 1.0, 1.5, 2.0])
d = min(d, remaining)
v = random.randint(60, 100)
result.append((f"{n}{octave}", v, d))
remaining -= d
return result
# 8 bars: alternate between minor and major pent, then mix
# Bars 1-2: pure minor (dark)
for note_data in phrase(minor_notes, 5, 4) + phrase(minor_notes, 5, 4):
n, v, d = note_data
lead.rest(d) if n is None else lead.add(n, d, velocity=v)
# Bars 3-4: pure major (bright) — hear the difference
for note_data in phrase(major_notes, 5, 4) + phrase(major_notes, 5, 4):
n, v, d = note_data
lead.rest(d) if n is None else lead.add(n, d, velocity=v)
# Bars 5-6: MIXED — this is where the magic lives
mixed = minor_notes + major_notes # both scales in one pool
for note_data in phrase(mixed, 5, 4) + phrase(mixed, 5, 4):
n, v, d = note_data
lead.rest(d) if n is None else lead.add(n, d, velocity=v)
# Bars 7-8: mixed with longer notes to let it breathe
for note_data in phrase(mixed, 5, 4, "melodic") + phrase(mixed, 4, 4):
n, v, d = note_data
lead.rest(d) if n is None else lead.add(n, d, velocity=v)
# ── Play ────────────────────────────────────────────────────────────
buf = render_score(score)
print(f" 🎵 {TONIC} Blues — Major + Minor Pentatonic")
print(" " + "─" * 50)
print(" bars 1-2: minor only (dark)")
print(" bars 3-4: major only (bright)")
print(" bars 5-8: mixed (the magic)")
print()
print(" change TONIC to any note and it works.")
print()
sd.play(buf, SAMPLE_RATE)
sd.wait()
print(" 🎵")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment