Files
interpretations/tracks/gravity.py
T
kennethreitz a9b356f5cc New synths across 10 more tracks — mellotron, drift, wavefold, granular
Silk Road: mellotron_flute in finale. The Dialogue: drift pad, mellotron ending.
Acid Reign: wavefold in breakdown. Chakra: drift at crown. Raga Midnight:
mellotron_strings over 808 drop. The Temple: granular_pad texture.
Sleight of Hand: mellotron_choir. Gravity: drift tambura.
Ghost Protocol: NES pulled back. An Exception: psycho bass 0.3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:55:52 -04:00

483 lines
17 KiB
Python

"""
GRAVITY — heavy. Piano, 808, snap. The weight of it all.
C minor, 88 BPM. No gimmicks. Just pressure.
"""
from pytheory import Key, Duration, Score, Tone, play_score
from pytheory.rhythm import DrumSound
key = Key("C", "minor")
s = key.scale # C D Eb F G Ab Bb
C = s[0]; D = s[1]; Eb = s[2]; F = s[3]
G = s[4]; Ab = s[5]; Bb = s[6]
score = Score("4/4", bpm=88)
prog = key.progression("i", "VI", "iv", "VII")
prog2 = key.progression("i", "VII", "VI", "v")
K = DrumSound.KICK
S = DrumSound.SNARE
CH = DrumSound.CLOSED_HAT
OH = DrumSound.OPEN_HAT
CL = DrumSound.CLAP
# ═══════════════════════════════════════════════════════════════════
# STRUCTURE (64 bars, ~2:55):
# Bars 1-4: Piano alone — the weight
# Bars 5-8: 808 enters — the floor drops
# Bars 9-16: Drums — boom bap + trap hats
# Bars 17-24: Full groove — everything locked
# Bars 25-32: Melody — Rhodes sings over the beat
# Bars 33-40: Breakdown — stripped back, just piano + 808
# Bars 41-48: Build — drums return harder, strings swell
# Bars 49-56: Peak — everything, hats going wild
# Bars 57-64: Outro — peeling away, piano last
# ═══════════════════════════════════════════════════════════════════
# ── PIANO — the foundation, dark chords ────────────────────────
piano = score.part("piano", instrument="piano", volume=0.5,
reverb=0.4, reverb_type="taj_mahal",
delay=0.12, delay_time=0.341, delay_feedback=0.2,
pan=-0.15, humanize=0.1)
# Piano patterns — sparse hits, let the space breathe
# Pattern A: stab, silence, low octave hit
def piano_bar_a(chord, vel):
piano.add(chord, Duration.EIGHTH, velocity=vel)
piano.rest(Duration.QUARTER)
piano.rest(Duration.EIGHTH)
piano.rest(Duration.QUARTER)
piano.add(chord, Duration.EIGHTH, velocity=max(30, vel - 20))
piano.rest(Duration.EIGHTH)
# Pattern B: low single note, space, high stab
def piano_bar_b(root, vel):
piano.add(root.add(-12), Duration.EIGHTH, velocity=vel)
piano.rest(Duration.DOTTED_QUARTER)
piano.rest(Duration.EIGHTH)
piano.add(root.add(12), Duration.SIXTEENTH, velocity=max(30, vel - 10))
piano.rest(Duration.SIXTEENTH)
piano.rest(Duration.QUARTER)
# Pattern C: two quick stabs then nothing
def piano_bar_c(chord, vel):
piano.rest(Duration.EIGHTH)
piano.add(chord, Duration.SIXTEENTH, velocity=vel)
piano.rest(Duration.SIXTEENTH)
piano.add(chord, Duration.EIGHTH, velocity=max(30, vel - 15))
piano.rest(Duration.HALF)
piano.rest(Duration.QUARTER)
single_roots = [C, Ab.add(-12), F, Bb]
# Bars 1-4: alone — just drops into silence
for i, chord in enumerate(prog):
piano_bar_a(chord, 78)
# Bars 5-8: 808 enters, piano gets sparser
for i, (chord, root) in enumerate(zip(prog, single_roots)):
piano_bar_b(root, 75)
# Bars 9-32: alternating patterns, never predictable
for section in range(6):
p = prog if section % 2 == 0 else prog2
r = single_roots if section % 2 == 0 else [C, Bb, Ab.add(-12), G]
vel = 78
piano_bar_a(p[0], vel)
piano_bar_b(r[1], vel)
piano_bar_c(p[2], vel)
piano_bar_a(p[3], vel)
# Bars 33-40: breakdown — barely there, single low notes
for root in single_roots * 2:
piano.add(root.add(-12), Duration.QUARTER, velocity=55)
piano.rest(Duration.DOTTED_HALF)
# Bars 41-56: back, patterns return harder
for section in range(4):
p = prog if section % 2 == 0 else prog2
r = single_roots if section % 2 == 0 else [C, Bb, Ab.add(-12), G]
vel = 82
piano_bar_a(p[0], vel)
piano_bar_c(p[1], vel)
piano_bar_b(r[2], vel)
piano_bar_a(p[3], vel)
# Bars 57-64: fading — one note per bar, dissolving
for root, vel in zip([C, Eb, G, Ab, Eb, C, G.add(-12), C.add(-12)],
[65, 58, 52, 45, 38, 32, 25, 18]):
piano.add(root, Duration.QUARTER, velocity=vel)
piano.rest(Duration.DOTTED_HALF)
# ── 808 — the floor, slides between roots ─────────────────────
sub = score.part("808", synth="sine", envelope="pad", volume=0.7,
lowpass=200, distortion=0.2, distortion_drive=3.0,
sub_osc=0.5, saturation=0.4, sidechain=0.35)
# Bars 1-4: silent
for _ in range(4):
sub.rest(Duration.WHOLE)
# Bars 5-64: 808 — continuous wave, one root per bar
roots = [C.add(-24), Ab.add(-24), F.add(-24), Bb.add(-24)]
roots2 = [C.add(-24), Bb.add(-24), Ab.add(-24), G.add(-24)]
for section in range(15):
r = roots if section % 2 == 0 else roots2
vel = 42 if section < 12 else max(18, 42 - (section - 12) * 8)
for root in r:
sub.add(root, Duration.WHOLE, velocity=vel)
# ── KICK — boom bap pocket ────────────────────────────────────
kick = score.part("kick", volume=0.8, humanize=0.04,
distortion=0.08, distortion_drive=1.5)
# Bars 1-8: silent
for _ in range(8):
kick.rest(Duration.WHOLE)
# Bars 9-32: boom bap — kick on 1, and-of-2, 3
for _ in range(24):
kick.hit(K, Duration.QUARTER, velocity=112)
kick.rest(Duration.EIGHTH)
kick.hit(K, Duration.EIGHTH, velocity=95)
kick.hit(K, Duration.QUARTER, velocity=108)
kick.rest(Duration.QUARTER)
# Bars 33-40: breakdown — just beat 1
for _ in range(8):
kick.hit(K, Duration.QUARTER, velocity=100)
kick.rest(Duration.DOTTED_HALF)
# Bars 41-56: back full, harder
for _ in range(16):
kick.hit(K, Duration.QUARTER, velocity=118)
kick.rest(Duration.EIGHTH)
kick.hit(K, Duration.EIGHTH, velocity=100)
kick.hit(K, Duration.QUARTER, velocity=112)
kick.rest(Duration.QUARTER)
# Bars 57-64: fading
for vel in [105, 95, 85, 75, 62, 48, 35, 22]:
kick.hit(K, Duration.QUARTER, velocity=vel)
kick.rest(Duration.DOTTED_HALF)
# ── SNARE — crack on 2 and 4 ──────────────────────────────────
snare = score.part("snare", volume=0.55, humanize=0.04,
reverb=0.2, reverb_decay=0.8,
delay=0.08, delay_time=0.341, delay_feedback=0.12,
pan=0.05)
for _ in range(8):
snare.rest(Duration.WHOLE)
# Bars 9-32: 2 and 4
for _ in range(24):
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=105)
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=108)
# Bars 33-40: breakdown — ghost snares
for _ in range(8):
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=55)
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=50)
# Bars 41-56: back hard
for _ in range(16):
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=110)
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=112)
# Bars 57-64: fading
for vel in [100, 88, 75, 62, 50, 38, 25, 15]:
snare.rest(Duration.QUARTER)
snare.hit(S, Duration.QUARTER, velocity=vel)
snare.rest(Duration.HALF)
# ── HATS — trap-style, evolving patterns ───────────────────────
hats = score.part("hats", volume=0.28, pan=0.2, humanize=0.05)
for _ in range(8):
hats.rest(Duration.WHOLE)
# Bars 9-16: simple 8ths
for _ in range(8):
for beat in range(4):
hats.hit(CH, Duration.EIGHTH, velocity=72)
hats.hit(CH, Duration.EIGHTH, velocity=48)
# Bars 17-24: 16ths creep in
for _ in range(8):
hats.hit(CH, Duration.EIGHTH, velocity=72)
hats.hit(CH, Duration.EIGHTH, velocity=48)
hats.hit(CH, Duration.SIXTEENTH, velocity=68)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.EIGHTH, velocity=55)
hats.hit(CH, Duration.EIGHTH, velocity=72)
hats.hit(CH, Duration.EIGHTH, velocity=48)
hats.hit(OH, Duration.EIGHTH, velocity=62)
# Bars 25-32: more active, triplet feel creeps in
for _ in range(8):
hats.hit(CH, Duration.EIGHTH, velocity=72)
hats.hit(CH, Duration.SIXTEENTH, velocity=52)
hats.hit(CH, Duration.SIXTEENTH, velocity=48)
hats.hit(CH, Duration.SIXTEENTH, velocity=68)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.SIXTEENTH, velocity=55)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.EIGHTH, velocity=72)
hats.hit(CH, Duration.SIXTEENTH, velocity=48)
hats.hit(CH, Duration.SIXTEENTH, velocity=52)
hats.hit(OH, Duration.EIGHTH, velocity=60)
# Bars 33-40: breakdown — barely there
for _ in range(8):
hats.hit(CH, Duration.QUARTER, velocity=35)
hats.rest(Duration.DOTTED_HALF)
# Bars 41-48: build back — 16ths
for _ in range(8):
for beat in range(4):
hats.hit(CH, Duration.SIXTEENTH, velocity=72)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.SIXTEENTH, velocity=55)
hats.hit(CH, Duration.SIXTEENTH, velocity=38)
# Bars 49-56: PEAK — 32nd note hat rolls scattered
for bar in range(8):
if bar % 2 == 1:
# 32nd note roll bar
for i in range(32):
vel = min(90, 40 + (i % 8) * 6)
hats.hit(CH, 0.125, velocity=vel)
else:
# Normal 16th bar with open hat
hats.hit(CH, Duration.SIXTEENTH, velocity=75)
hats.hit(CH, Duration.SIXTEENTH, velocity=45)
hats.hit(CH, Duration.SIXTEENTH, velocity=58)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.SIXTEENTH, velocity=70)
hats.hit(CH, Duration.SIXTEENTH, velocity=45)
hats.hit(OH, Duration.SIXTEENTH, velocity=62)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.SIXTEENTH, velocity=72)
hats.hit(CH, Duration.SIXTEENTH, velocity=48)
hats.hit(CH, Duration.SIXTEENTH, velocity=55)
hats.hit(CH, Duration.SIXTEENTH, velocity=42)
hats.hit(CH, Duration.SIXTEENTH, velocity=68)
hats.hit(CH, Duration.SIXTEENTH, velocity=45)
hats.hit(OH, Duration.SIXTEENTH, velocity=60)
hats.hit(CH, Duration.SIXTEENTH, velocity=38)
# Bars 57-64: fading
for vel in [60, 52, 44, 36, 28, 22, 15, 0]:
if vel > 0:
for beat in range(4):
hats.hit(CH, Duration.EIGHTH, velocity=vel)
hats.hit(CH, Duration.EIGHTH, velocity=max(12, vel - 25))
else:
hats.rest(Duration.WHOLE)
# ── TAMBURA — buried drone, barely there, just warmth ──────────
tambura = score.part("tambura", synth="drift", envelope="pad", volume=0.08,
reverb=0.5, reverb_type="taj_mahal",
chorus=0.3, chorus_rate=0.06, chorus_depth=0.01,
lowpass=800, pan=-0.3)
for _ in range(4):
tambura.rest(Duration.WHOLE)
for _ in range(52):
tambura.add(C.add(-24), Duration.WHOLE, velocity=35)
for vel in [28, 22, 16, 10, 6, 3, 0, 0]:
if vel > 0:
tambura.add(C.add(-24), Duration.WHOLE, velocity=vel)
else:
tambura.rest(Duration.WHOLE)
# ── SINGING BOWL — bookends, marks the gravity ────────────────
bowl = score.part("bowl", instrument="singing_bowl", volume=0.3,
reverb=0.8, reverb_type="taj_mahal",
delay=0.15, delay_time=0.682, delay_feedback=0.2,
pan=0.2)
bowl.add(C.add(-24), Duration.WHOLE, velocity=65)
for _ in range(62):
bowl.rest(Duration.WHOLE)
bowl.add(C.add(-24), Duration.WHOLE, velocity=50)
# ── SITAR — one bend in the breakdown, like a ghost ───────────
sitar = score.part("sitar", instrument="sitar", volume=0.25,
reverb=0.35, reverb_type="taj_mahal",
delay=0.2, delay_time=0.341, delay_feedback=0.3,
pan=0.3, humanize=0.08)
for _ in range(34):
sitar.rest(Duration.WHOLE)
# Bar 35-36: one long bent note — the sample flip moment
sitar.add(Eb, Duration.WHOLE, velocity=65, bend=-0.25)
sitar.add(C, Duration.WHOLE, velocity=55, bend=0.15)
# Bar 37: quick ornamental phrase then gone
sitar.add(Eb, Duration.QUARTER, velocity=60)
sitar.add(D, Duration.EIGHTH, velocity=55)
sitar.add(C, Duration.EIGHTH, velocity=52)
sitar.add(Bb.add(-12), Duration.HALF, velocity=58, bend=-0.15)
for _ in range(27):
sitar.rest(Duration.WHOLE)
# ── CLAP — layered with snare for weight ───────────────────────
clap = score.part("clap", volume=0.2, reverb=0.15,
delay=0.06, delay_time=0.341, delay_feedback=0.1,
pan=-0.08, humanize=0.04)
for _ in range(16):
clap.rest(Duration.WHOLE)
# Bars 17-32: layered on snare hits
for _ in range(16):
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=82)
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=85)
# Bars 33-40: silent
for _ in range(8):
clap.rest(Duration.WHOLE)
# Bars 41-56: back
for _ in range(16):
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=85)
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=88)
# Bars 57-64: silent
for _ in range(8):
clap.rest(Duration.WHOLE)
# ── RHODES — melody enters bar 25, the soul ───────────────────
rhodes = score.part("rhodes", instrument="electric_piano", volume=0.3,
reverb=0.5, reverb_type="taj_mahal",
delay=0.2, delay_time=0.341, delay_feedback=0.3,
tremolo_depth=0.1, tremolo_rate=2.5,
pan=0.25, humanize=0.08)
for _ in range(24):
rhodes.rest(Duration.WHOLE)
# Bars 25-32: melody — simple, soulful, singing
melody = [
(Eb.add(12), Duration.HALF, 80), (D.add(12), Duration.QUARTER, 72),
(C.add(12), Duration.QUARTER, 75),
(Bb, Duration.HALF, 78), (Ab, Duration.QUARTER, 70),
(G, Duration.QUARTER, 72),
(Ab, Duration.DOTTED_HALF, 80), (G, Duration.QUARTER, 72),
(F, Duration.HALF, 75), (Eb, Duration.HALF, 78),
(G, Duration.HALF, 80), (Ab, Duration.QUARTER, 75),
(Bb, Duration.QUARTER, 72),
(C.add(12), Duration.HALF, 82), (Bb, Duration.QUARTER, 75),
(Ab, Duration.QUARTER, 70),
(G, Duration.WHOLE, 78),
(None, Duration.WHOLE, 0),
]
for note, dur, vel in melody:
if note is None:
rhodes.rest(dur)
else:
rhodes.add(note, dur, velocity=vel)
# Bars 33-40: silent through breakdown
for _ in range(8):
rhodes.rest(Duration.WHOLE)
# Bars 41-48: melody returns, stronger
for note, dur, vel in melody:
if note is None:
rhodes.rest(dur)
else:
rhodes.add(note, dur, velocity=min(127, vel + 8))
# Bars 49-56: variation — higher, more intense
melody2 = [
(G.add(12), Duration.QUARTER, 85), (Ab.add(12), Duration.QUARTER, 82),
(Bb.add(12), Duration.HALF, 88),
(Ab.add(12), Duration.QUARTER, 80), (G.add(12), Duration.QUARTER, 78),
(Eb.add(12), Duration.HALF, 82),
(D.add(12), Duration.QUARTER, 78), (Eb.add(12), Duration.QUARTER, 80),
(G.add(12), Duration.HALF, 85),
(C.add(12), Duration.WHOLE, 82),
(Bb, Duration.HALF, 78), (Ab, Duration.QUARTER, 72),
(G, Duration.QUARTER, 70),
(Eb, Duration.WHOLE, 75),
(None, Duration.WHOLE, 0),
(None, Duration.WHOLE, 0),
]
for note, dur, vel in melody2:
if note is None:
rhodes.rest(dur)
else:
rhodes.add(note, dur, velocity=vel)
# Bars 57-64: one last phrase, fading
rhodes.add(Eb.add(12), Duration.HALF, velocity=65)
rhodes.add(C.add(12), Duration.HALF, velocity=58)
rhodes.add(G, Duration.WHOLE, velocity=50)
for _ in range(6):
rhodes.rest(Duration.WHOLE)
# ── STRINGS — swell at bar 41, the drama ──────────────────────
strings = score.part("strings", instrument="string_ensemble", volume=0.15,
reverb=0.6, reverb_type="cathedral",
chorus=0.2, chorus_rate=0.15, chorus_depth=0.006,
pan=-0.2)
for _ in range(40):
strings.rest(Duration.WHOLE)
# Bars 41-56: slow swells under the beat
for _ in range(4):
for chord in prog:
strings.add(chord, Duration.WHOLE, velocity=48)
# Bars 57-64: fading
for vel in [42, 35, 28, 22, 18, 12, 8, 0]:
if vel > 0:
strings.add(prog[0], Duration.WHOLE, velocity=vel)
else:
strings.rest(Duration.WHOLE)
# ── VINYL — lo-fi texture throughout ──────────────────────────
vinyl = score.part("vinyl", synth="noise", envelope="pad", volume=0.025,
lowpass=1500, highpass=500,
distortion=0.3, distortion_drive=2.5,
saturation=0.5, pan=0.1)
for _ in range(64):
vinyl.add(C, Duration.WHOLE, velocity=20)
# ═════════════════════════════════════════════════════════════════
import sys
print(f"Key: {key}")
print(f"BPM: 88")
print(f"Parts: {list(score.parts.keys())}")
print(f"Duration: {score.duration_ms / 1000:.1f}s | {score.measures} measures")
if "--live" in sys.argv:
print("Playing GRAVITY (live engine)...")
from pytheory_live.live import LiveEngine
engine = LiveEngine(buffer_size=1024)
engine.play_score(score)
else:
print("Playing GRAVITY...")
play_score(score)