Files
interpretations/tracks/ghost_protocol.py
T
kennethreitz fe8efa72be Ghost Protocol: Portishead intro → Strobe build, NES emotional peak
Rewritten from scratch with Strobe philosophy:
- Portishead dark Rhodes + trip-hop beat (bars 1-32)
- Hypnotic saw arp emerges, grows imperceptibly (bars 17+)
- No kick until bar 49 — the patient build IS the point
- NES square wave melody at the emotional peak (bars 65-96)
- Pluck stabs for the peak energy
- 128 bars (~6 min), filter sweeps across entire track
- Everything dissolves back to silence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 05:08:19 -04:00

372 lines
12 KiB
Python

"""
GHOST PROTOCOL — Portishead intro → deadmau5 Strobe-style build.
Dark, patient, hypnotic. One pluck arp that IS the track.
The kick doesn't arrive until you've forgotten you're waiting for it.
"""
from pytheory import Key, Duration, Score, Tone, play_score
from pytheory.rhythm import DrumSound
key = Key("F", "minor")
s = key.scale
F = s[0]; G = s[1]; Ab = s[2]; Bb = s[3]
C = s[4]; Db = s[5]; Eb = s[6]
score = Score("4/4", bpm=128)
K = DrumSound.KICK
CL = DrumSound.CLAP
CH = DrumSound.CLOSED_HAT
OH = DrumSound.OPEN_HAT
prog = key.progression("i", "VI", "VII", "i")
# ═══════════════════════════════════════════════════════════════════
# STRUCTURE (128 bars, ~6 min at 128 BPM):
# Bars 1-16: Portishead — dark, downtempo feel, scratchy
# Bars 17-32: The arp emerges from the darkness
# Bars 33-48: Pad swells, arp grows, still no kick
# Bars 49-64: Kick arrives — release, the Strobe moment
# Bars 65-80: Full energy, filter wide open
# Bars 81-96: Peak — everything singing
# Bars 97-112: Filtering down, layers drop
# Bars 113-128: Just the arp and pad, dissolving
# ═══════════════════════════════════════════════════════════════════
# ── RHODES — Portishead dark chords, sparse, tremolo ────────────
rhodes = score.part("rhodes", instrument="electric_piano", volume=0.3,
reverb=0.5, reverb_type="taj_mahal",
tremolo_depth=0.2, tremolo_rate=2.5,
humanize=0.1)
# Bars 1-16: dark sparse chords — Portishead vibe
for _ in range(4):
for chord in prog:
rhodes.add(chord, Duration.HALF, velocity=70)
rhodes.rest(Duration.HALF)
# Bars 17-32: thinner, making room for the arp
for _ in range(4):
for chord in prog:
rhodes.add(chord, Duration.QUARTER, velocity=55)
rhodes.rest(Duration.DOTTED_HALF)
# Bars 33-48: fading out
for _ in range(2):
for chord in prog:
rhodes.add(chord, Duration.QUARTER, velocity=40)
rhodes.rest(Duration.DOTTED_HALF)
for _ in range(8):
rhodes.rest(Duration.WHOLE)
# Bars 49-128: gone
for _ in range(80):
rhodes.rest(Duration.WHOLE)
# ── TRIP-HOP BEAT — Portishead style, bars 5-32 ────────────────
trip = score.part("trip_hop", volume=0.3, humanize=0.08,
reverb=0.15, reverb_decay=0.6)
# Bars 1-4: silence — let rhodes breathe
for _ in range(4):
trip.rest(Duration.WHOLE)
# Bars 5-16: slow, lazy breakbeat — Portishead pocket
S = DrumSound.SNARE
for _ in range(12):
trip.hit(K, Duration.QUARTER, velocity=90)
trip.rest(Duration.EIGHTH)
trip.hit(CH, Duration.EIGHTH, velocity=50)
trip.hit(S, Duration.QUARTER, velocity=85)
trip.hit(CH, Duration.EIGHTH, velocity=48)
trip.hit(K, Duration.EIGHTH, velocity=75)
# Bars 17-24: beat thins out
for _ in range(8):
trip.hit(K, Duration.QUARTER, velocity=70)
trip.rest(Duration.QUARTER)
trip.hit(S, Duration.QUARTER, velocity=60)
trip.rest(Duration.QUARTER)
# Bars 25-32: just ghost hits, disappearing
for bar in range(8):
vel = max(20, 60 - bar * 5)
trip.hit(K, Duration.QUARTER, velocity=vel)
trip.rest(Duration.DOTTED_HALF)
# Bars 33-128: gone
for _ in range(96):
trip.rest(Duration.WHOLE)
# ── THE ARP — the soul of the track, enters quietly bar 17 ─────
arp = score.part("arp", synth="saw", envelope="pluck", volume=0.2,
reverb=0.3, delay=0.4, delay_time=0.234,
delay_feedback=0.45, lowpass=1200, detune=6,
humanize=0.04)
# Slow filter open over the entire track
arp.lfo("lowpass", rate=0.008, min=800, max=6000, bars=128, shape="saw")
# Bars 1-16: silence
for _ in range(16):
arp.rest(Duration.WHOLE)
# The pattern — hypnotic, never changes, just grows
arp_pattern = [
F, None, C.add(12), None,
Eb, None, C.add(12), Ab,
F, None, C.add(12), None,
Ab, None, Eb, C.add(12),
]
# Bars 17-32: arp emerges, barely audible
for _ in range(4):
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=50)
# Bars 33-48: arp grows
for _ in range(4):
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=65)
# Bars 49-64: kick arrives, arp confident
for _ in range(4):
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=80)
# Bars 65-96: full energy, arp singing
for _ in range(8):
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=90)
# Bars 97-112: filtering down
for _ in range(4):
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=70)
# Bars 113-128: dissolving
for rep in range(4):
vel = max(25, 60 - rep * 10)
for note in arp_pattern:
if note is None:
arp.rest(Duration.SIXTEENTH)
else:
arp.add(note, Duration.SIXTEENTH, velocity=vel)
# ── PAD — supersaw atmosphere, builds imperceptibly ─────────────
pad = score.part("pad", synth="supersaw", envelope="pad", volume=0.15,
reverb=0.6, chorus=0.4, chorus_rate=0.2,
chorus_depth=0.01, lowpass=600)
pad.lfo("lowpass", rate=0.008, min=400, max=5000, bars=128, shape="triangle")
# Bars 1-16: dark, barely there
for _ in range(4):
for chord in prog:
pad.add(chord, Duration.WHOLE, velocity=40)
# Bars 17-48: slowly swelling
for _ in range(8):
for chord in prog:
pad.add(chord, Duration.WHOLE, velocity=55)
# Bars 49-96: full, warm
for _ in range(12):
for chord in prog:
pad.add(chord, Duration.WHOLE, velocity=70)
# Bars 97-128: fading
for rep in range(8):
vel = max(20, 65 - rep * 6)
for chord in prog:
pad.add(chord, Duration.WHOLE, velocity=vel)
# ── BASS — enters with the kick, bar 49 ────────────────────────
bass = score.part("bass", synth="saw", envelope="pluck", volume=0.35,
lowpass=250, distortion=0.15, distortion_drive=2.5,
sub_osc=0.5)
# Bars 1-48: silence
for _ in range(48):
bass.rest(Duration.WHOLE)
# Bars 49-96: the groove
bass_line = [
(F.add(-24), Duration.EIGHTH), (None, Duration.EIGHTH),
(F.add(-24), Duration.EIGHTH), (None, Duration.EIGHTH),
(F.add(-24), Duration.EIGHTH), (None, Duration.QUARTER),
(Ab.add(-24), Duration.EIGHTH),
]
for _ in range(12):
for note, dur in bass_line:
if note is None:
bass.rest(dur)
else:
bass.add(note, dur, velocity=100)
# Bars 97-128: fading
for rep in range(8):
vel = max(20, 90 - rep * 10)
for note, dur in bass_line:
if note is None:
bass.rest(dur)
else:
bass.add(note, dur, velocity=vel)
# ── KICK — the Strobe moment, enters bar 49 ────────────────────
kick = score.part("kick", volume=0.5, humanize=0.03)
# Bars 1-48: no kick — this IS the point
for _ in range(48):
kick.rest(Duration.WHOLE)
# Bars 49-96: four on the floor — the release
for _ in range(48):
for beat in range(4):
kick.hit(K, Duration.QUARTER, velocity=115)
# Bars 97-112: kick continues, stable
for _ in range(16):
for beat in range(4):
kick.hit(K, Duration.QUARTER, velocity=108)
# Bars 113-128: kick fades last — the final heartbeat
for bar in range(16):
vel = max(25, 105 - bar * 5)
for beat in range(4):
kick.hit(K, Duration.QUARTER, velocity=vel)
# ── CLAP — with the kick ───────────────────────────────────────
clap = score.part("clap", volume=0.3, reverb=0.15, humanize=0.04)
# Bars 1-48: silence
for _ in range(48):
clap.rest(Duration.WHOLE)
# Bars 49-96: 2 and 4
for _ in range(48):
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=95)
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=98)
# Bars 97-128: fading
for bar in range(32):
vel = max(20, 95 - bar * 2)
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=vel)
clap.rest(Duration.QUARTER)
clap.hit(CL, Duration.QUARTER, velocity=vel)
# ── HATS — offbeat, with the kick ──────────────────────────────
hats = score.part("hats", volume=0.25, humanize=0.04)
# Bars 1-48: silence
for _ in range(48):
hats.rest(Duration.WHOLE)
# Bars 49-96: offbeat 8ths
for _ in range(48):
for beat in range(4):
hats.rest(Duration.EIGHTH)
hats.hit(CH, Duration.EIGHTH, velocity=70)
# Bars 97-128: fading
for bar in range(32):
vel = max(20, 70 - bar * 2)
for beat in range(4):
hats.rest(Duration.EIGHTH)
hats.hit(CH, Duration.EIGHTH, velocity=vel)
# ── NES MELODY — emotional square wave, the heart of the peak ───
nes = score.part("nes", synth="square", envelope="organ", volume=0.2,
reverb=0.4, reverb_type="taj_mahal",
delay=0.3, delay_time=0.234, delay_feedback=0.4,
lowpass=4000, humanize=0.05)
# Bars 1-64: silence
for _ in range(64):
nes.rest(Duration.WHOLE)
# Bars 65-80: the emotional peak — simple, singing melody
nes_melody = [
(F.add(12), Duration.HALF, 85),
(Eb.add(12), Duration.QUARTER, 80),
(C.add(12), Duration.QUARTER, 78),
(Db.add(12), Duration.HALF, 82),
(C.add(12), Duration.HALF, 80),
(Ab, Duration.HALF, 78),
(Bb, Duration.QUARTER, 75),
(C.add(12), Duration.QUARTER, 80),
(F.add(12), Duration.WHOLE, 88),
]
for _ in range(4):
for note, dur, vel in nes_melody:
nes.add(note, dur, velocity=vel)
# Bars 81-96: melody fades
for rep in range(2):
vel_off = rep * 15
for note, dur, vel in nes_melody:
nes.add(note, dur, velocity=max(25, vel - vel_off))
for _ in range(8):
nes.rest(Duration.WHOLE)
# Bars 97-128: silence
for _ in range(32):
nes.rest(Duration.WHOLE)
# ── PLUCK STABS — offbeat chords, bars 65-96 ───────────────────
pluck = score.part("pluck", synth="saw", envelope="pluck", volume=0.18,
reverb=0.2, delay=0.2, delay_time=0.234,
delay_feedback=0.3, lowpass=2500, detune=8)
# Bars 1-64: silence
for _ in range(64):
pluck.rest(Duration.WHOLE)
# Bars 65-96: the peak — offbeat stabs
for _ in range(8):
for chord in prog:
pluck.rest(Duration.EIGHTH)
pluck.add(chord, Duration.EIGHTH, velocity=85)
pluck.rest(Duration.QUARTER)
pluck.rest(Duration.EIGHTH)
pluck.add(chord, Duration.EIGHTH, velocity=80)
pluck.rest(Duration.QUARTER)
# Bars 97-128: silence
for _ in range(32):
pluck.rest(Duration.WHOLE)
# ═════════════════════════════════════════════════════════════════
import sys
print(f"Key: {key}")
print(f"BPM: 128")
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 GHOST PROTOCOL (live engine)...")
from pytheory_live.live import LiveEngine
engine = LiveEngine(buffer_size=1024)
engine.play_score(score)
else:
print("Playing GHOST PROTOCOL...")
play_score(score)