Skip to content

About

Procedural trance music generator in the style of Switch Angel's live-coded Strudel sets. No samples, DMCA-safe.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

TranceStream

A procedural trance music generator in the style of Switch Angel's live-coded Strudel sets. Streams stereo audio to your system output in real time, exports WAV and MIDI, and is fully deterministic from a text seed.

All audio is synthesised from mathematics — no samples, no soundfonts, fully DMCA-safe.


Vision

The ultimate goal is Death Angel — a fully original AI live-coding trance entity with its own identity, style, and generative personality.

Switch Angel (SA) is the current focus because she provides a concrete, measurable target. Matching her sound precisely is the discipline that builds the full stack: synthesis, composition, arrangement, and style parameterisation. Once we can convincingly reproduce SA's output from first principles, everything needed to build something original is in place.

SA is the vehicle. Death Angel is the destination.


Current goal (SA phase)

Make a listener familiar with trance believe they are hearing Switch Angel playing live — without knowing the music is procedurally generated.

The reference is Switch Angel (SA): a live-coder who builds trance tracks in real time using Strudel.cc. Her sound fingerprint has been reverse-engineered from OCR analysis of 311 code snapshots across 5 of her YouTube sessions. Every synthesis constant in this project traces back to a measurement from that source.

The target sound requires getting five things right simultaneously:

Element SA's approach This project
Pad 5-voice supersaw, very dark LP filter (~400 Hz), trancegate breathing, FDN reverb, sidechain pump instruments/pad.py — full chain implemented
Kick TR-909 style: 285→50 Hz pitch sweep, tau=31 ms, decay 120 ms instruments/drums.py — confirmed constants
Sidechain Pad/bass duck to ~40% on every kick; .duckdepth(.6) SIDECHAIN_DEPTH=0.6 in song/theory.py
Trancegate Probabilistic binary gate, 16 steps/bar, density=2/3 (SA's rand.mul(1.5).round().seg(16)) synth/envelopes.py
Chord progression C min → D min → Eb maj → F maj (iv–v–bVI–bVII), 4 bars per chord, G natural minor at 140 BPM song/theory.py — SA-confirmed values

What is done and what remains

The v3 architecture is structurally correct — all five voices exist, all SA-confirmed constants are in song/theory.py, and the arrangement arc (Intro → Groove → Breakdown → Build-up → Drop) is implemented. The remaining gap is perceptual verification: the output has not yet been measured against SA's reference for sidechain pump depth, trancegate shape, and filter floor. The constants are right; the measurements are missing.

See CLAUDE.md for the prioritised list of parameters still needing output-level verification.


Quick start

pip install numpy scipy sounddevice mido
python trance_stream_v3.py --stream

Streams indefinitely until Ctrl-C. Add --bars N to stop after N bars.


CLI reference

python trance_stream_v3.py [OPTIONS]

  -s, --seed SEED       Text seed for deterministic generation (default: sunrise)
                        Same seed + mood always produces the same track.

  -m, --mood MOOD       Harmonic character (default: uplifting)
                        uplifting | dark | acid | dreamy | progressive

      --bpm BPM         Tempo in BPM (default: 140)

      --bars N          Number of bars to generate (default: infinite when
                        streaming, 128 when rendering to file)

      --volume V        Master volume 0.0–1.0 (default: 1.0)

      --stream          Real-time bar-by-bar playback, runs forever. Ctrl-C to stop.

      --from-bar BAR    Skip silently to BAR before streaming or rendering.
                        All instrument state (oscillator phases, filter, sidechain,
                        chord progression) is advanced correctly. Instantaneous —
                        useful for jumping to a specific moment for diagnosis.

                        Examples:
                          --stream --from-bar 90          stream from bar 90
                          --stream --from-bar 45 --bars 12  hear the breakdown
                          --stream --from-bar 96 --solo lead  isolate lead at climax

      --viz             Full-screen text visualiser while streaming.
                        Shows chord, filter arc, active tracks, trancegate phase,
                        and a Rule-30 cellular automaton spacetime diagram.
                        Only valid with --stream.

      --ascii-video [PATH ...]
                        Load one or more pre-rendered ASCII video frame files and
                        use them as a colour overlay on the CA diagram. The CA
                        continues running; the video colours each cell. Press v
                        to cycle through the loaded videos and back to normal CA
                        colour mode.
                        If omitted, all ascii_videos/*.txt files are
                        auto-discovered (Bad Apple + Star Wars when both are
                        present). Only active with --stream --viz.

      --wav PATH        Write output to a WAV file (default when not streaming:
                        /tmp/trance_v3.wav)

  -o, --out-midi PATH   Write a MIDI file (default when not streaming:
                        /tmp/trance_v3.mid)

      --solo TRACK ...  Solo one or more tracks; all others are muted.
      --mute TRACK ...  Mute one or more tracks.

                        Track names: kick  pad  lead  bass  hihat  clap  pulse

      --analyse         Print spectral/MIDI analysis after rendering
      --spectrogram     Generate a spectrogram PNG after rendering
      --play            Play back the WAV after batch rendering

Track isolation (solo / mute)

Standard DAW solo and mute for diagnosing what each element sounds like in isolation. Useful when something sounds wrong and you want to identify which track is responsible.

# Hear only the kick
python trance_stream_v3.py --stream --solo kick

# Hear only the lead synth
python trance_stream_v3.py --stream --solo lead

# Hear everything except the lead
python trance_stream_v3.py --stream --mute lead

# Pad + bass together — check harmonic content without rhythm
python trance_stream_v3.py --stream --solo pad bass

# Diagnose lead entering at bar 7 with no other distractions
python trance_stream_v3.py --stream --solo lead --bars 20

Making tracks

The two main levers are --mood and --seed.

--mood sets the harmonic character: scale, chord progression, and BPM range.

--seed is a free-text string. It derives the root key via MD5 hash (deterministic but non-obvious), picks the chord progression variant, offsets the arrangement stage timings, and controls lead character and arc shape. Same seed + mood always gives the same track.

# Uplifting — euphoric arc, natural minor
python trance_stream_v3.py --stream --mood uplifting -s "sunrise"

# Dark — tension loop, slower BPM
python trance_stream_v3.py --stream --mood dark -s "midnight"

# Acid — hypnotic i → III → iv loop
python trance_stream_v3.py --stream --mood acid -s "303"

# Progressive — major scale, bright and groovy
python trance_stream_v3.py --stream --mood progressive -s "journey"

# Dreamy — dorian scale, bittersweet
python trance_stream_v3.py --stream --mood dreamy -s "velvet sphinx"

Moods

Mood Scale Chord roots BPM range
uplifting Natural minor i – VI – III – v 138–142
dark Natural minor i – iv – i – VII 136–140
acid Natural minor i – III – iv – III 138–145
progressive Major I – IV – V – ii 128–138
dreamy Dorian i – V – VI – III 128–136

Each chord entry carries a root and a fifth so the lead notearp has two distinct pitches to arpeggiate between.


Arrangement

v3 uses an additive stage model — voices enter one at a time and then stay. The arrangement has three dynamic phases:

Build (bars 0 → pullback−4): voices enter one by one; master gain rises from 0.55 to 1.0, making each new entry feel bigger than the last. Once pad_chord_on is reached the pad switches from a sustained drone to 16 rhythmic filter stabs per bar (SA's .seg 16 equivalent).

Breakdown (4 bars before pullback): bass, lead, hihat and clap all drop out simultaneously, leaving only kick + pad. The filter then dips to ~311 Hz over 8 bars — the darkest, most stripped-back moment in the song.

Second half (after pullback): all voices re-enter as the filter reopens toward 12 kHz. The chord progression shifts to chord_prog_b and the root may lift by +2 semitones (seed-determined). FM modulation and hi-hat enter in the final third.

Default stage positions (shifted ±4 bars per seed):

Stage Default bar What happens
kick_on 0 Kick drum — four-on-floor
pad_root_on 2 Pad enters on single root note
bass_on 4 Acid bass enters — rhythmic pattern from SA's bstruct
lead_root_on 8 Lead enters on single root note
lead_melody_on 16 Lead gets full notearp melody + delay
pad_chord_on 20 Pad gains moving chord + trancegate
lead_voicing_on 32 Lead gains bar-varying voicing shift
clap_on 56 Backbeat clap added
fm_on 88 FM modulation opens on lead
pulse_on 100 Pulse texture shimmer layer
hihat_on 112 Hi-hat added
kick_syncopated 116 Kick upgrades to beat(0,4,8,11,14)

Synthesis

Voice Signal chain
Kick Sine sweep + noise transient, tail bleeds into next bar
Pad 5-voice supersaw → LP swell (lpenv) → trancegate (1.5×, depth 0.7, breathes 0.3–1.0) → FDN reverb → sidechain; plays in C4 register
Bass Sawtooth → acidenv LP sweep → LPF; 5–7 spaced hits per bar with clear gaps
Lead 3-voice supersaw + FM → acidenv LP sweep → trancegate → ping-pong delay; plays in C5 register, one octave above pad
Hi-hat White noise → HPF → exponential decay; tri-LFO varies decay
Clap Filtered noise burst, backbeat (steps 4, 12)
Pulse Pulse oscillator × 16 triggers per bar, FM-modulated by time

All oscillators maintain phase continuity across bar boundaries. Kick tails overflow into the next bar. Trancegate phase is anchored to absolute session time so instruments entering mid-song breathe in sync with the kick. Sidechain uses a stateful IIR follower so ducking recovers smoothly across bars.

Stream uses a dedicated render thread feeding a queue so the audio thread never stalls between bars.


Visualiser

python trance_stream_v3.py --stream --viz --seed dawn --mood uplifting

ASCII video overlay

The CA diagram has a second display mode where pre-rendered ASCII video files are used as a colour overlay. The CA keeps scrolling and driving the music; the video frame at each moment determines the colour of each cell.

Two built-in assets are available — download them once:

# Bad Apple (6,572 frames, 30 fps, 60×32)
python tools/fetch_bad_apple.py

# Star Wars asciimation (15,973 frames, 15 fps, 67×13)
python tools/fetch_starwars.py

Once the frame files are in tools/, run --viz and they are auto-discovered:

python trance_stream_v3.py --stream --viz

Press v while streaming to cycle through the videos: [normal CA colours] → [Bad Apple] → [Star Wars] → [normal CA colours] → …

Load specific files or a custom set:

# One file only
python trance_stream_v3.py --stream --viz --ascii-video ascii_videos/bad_apple_frames.txt

# Explicit playlist
python trance_stream_v3.py --stream --viz --ascii-video ascii_videos/bad_apple_frames.txt ascii_videos/starwars_15fps_frames.txt

The overlay uses cover scaling: the video always fills the entire CA area with no letterbox bars. The video is center-cropped to fit the terminal shape. Playback is wall-clock synced at the video's native frame rate, advancing smoothly on every sixteenth-note tick regardless of BPM.

Any conforming frame file works — the format is one frame per line with rows separated by literal \n (the backslashxx/bad-apple-ascii format). The frame rate is parsed from the filename pattern *_Nfps_*; defaults to 30 if absent.

Renders a full-screen text display that updates once per bar. Layout adapts to terminal width: wide (≥ 100 cols) shows full labels and timing stats; narrow (< 100 cols) uses compact abbreviations.

UI panels

Panel What it shows
Header Seed, mood, BPM
Info row Bar number, current chord name, filter cutoff in Hz, FM depth %
Track row Seven track indicators that flicker in sync with actual hits (see below)
Filter bar Cyan filled bar tracking the cutoff arc from ~850 Hz to ~12 kHz
Gate bar ● cursor showing the trancegate position within the current bar cycle
CA diagram Rule-30 cellular automaton spacetime history (see below)
Timing row Render time, bar budget, headroom in ms (wide layout only)

Track indicators

Each bar is delivered as 16 sixteenth-note chunks. The indicator line updates on every chunk, flashing in sync with what the listener actually hears.

Percussive voices (kick, bass, hihat, clap) go fully dark between hits — you can read the rhythm pattern directly from the display. Sustained voices (pad, lead, pulse) stay dim ● while active to show they are droning, flashing bright on each retrigger.

State Meaning
Bold bright ● Instrument firing this sixteenth note
Dim ● Active but not firing (sustained voice)
○ Between hits (percussive voice) or not yet in the arrangement

CA diagram — reading the colours

The automaton section fills all available terminal rows and uses the full width (one character per cell). Each bar appends a new row at the bottom; older rows scroll up. Two musical properties are encoded in the colour:

Hue = chord. Each of the four chords in the progression has its own colour. Reading horizontal bands in the history shows the harmonic cycle:

Colour Chord index Role in progression
Cyan 0 Tonic — home, stable
Green 1 First movement away from tonic
Yellow 2 Tension chord
Magenta 3 Release / turnaround

A colour change in the diagram means a chord change in the music. Wide bands = that chord holds for several bars; narrow bands = quick movement.

Brightness = filter arc / session energy. The filter cutoff opens gradually over the session, driving the characteristic trance energy build:

Brightness Filter slider Filter Hz Session phase
Dim < 0.5 < ~2985 Hz Early, closed
Normal 0.5 – 0.7 ~2985 – 5096 Hz Mid session
Bold > 0.7 > ~5096 Hz Late, open

There is a deliberate dim dip around bar 64 (the pullback moment), then rows go bold and stay bold once the filter fully opens around bar 96. Reading the diagram top-to-bottom is reading the harmonic and energy history of the entire stream.

"Rule 30" appears as a dim overlay on the rightmost edge of the newest row — ambient metadata, not part of the pattern.

CA → sound (the loop is closed)

The automaton does not just visualise the music — it drives three sound parameters each bar:

CA property Sound parameter Effect
Two centre bits → index into (0, 2, 5, 7) Lead voicing offset (semitones) Melody shifts register each bar — SA's .add "<5 4 0 <0 2>>" equivalent
Cell density (fraction of live cells) Lead delay wet 0.25–0.85 Dense rows = washed/spacious; sparse rows = dry/tight
Cell density > 0.55 Bass step pattern Busier pattern when CA is active; sparser when quiet

Musical events inject bits back into the CA: chord changes force bit 0 to 1; FM onset forces the centre bit; every 4-bar phrase boundary toggles a near-centre bit. This creates a feedback loop where harmony drives pattern which drives timbre which drives variation.


Exporting

# Render 128 bars to WAV only (no audio hardware needed)
python trance_stream_v3.py --bars 128 --wav out.wav -s "sunrise"

# Stream forever and write WAV simultaneously (Ctrl-C to stop and finalise)
python trance_stream_v3.py --stream --wav out.wav -s "sunrise"

# Write MIDI
python trance_stream_v3.py --bars 64 --wav out.wav -o out.mid -s "sunrise"

WAV and stream output are sample-identical (same seed, same path, same result).


Diagnostic tools

# Spectral report
python tools/analyse_audio.py /tmp/trance_v3.wav

# Spectrogram image
python tools/spectrogram.py /tmp/trance_v3.wav --out /tmp/spec.png

Dependencies

Package Purpose
numpy Audio buffer arithmetic
scipy Filters (lfilter), stateful IIR
sounddevice Real-time stereo PCM output
mido MIDI file construction
pip install numpy scipy sounddevice mido

Python 3.10 or later required.


Research

Built from 311 OCR'd code snapshots across 5 of Switch Angel's YouTube live-coding sessions. See research/analysis/switch_angel_vocabulary.md for the full synthesis parameter reference.

TranceStream does not copy or depend on her code. The research was used to understand the target synthesis chain; all code is original.


Research and application value

What this project is actually doing

At its core, this is analysis-by-synthesis — hypothesize a parametric model of a musical style, then falsify or refine it by comparing synthesized output against the original. That is a scientific method applied to music.

Research value

Computational musicology with teeth. Most music analysis describes what it hears subjectively — "warm", "punchy", "euphoric". This process forces those descriptions into measurable, reproducible parameters: spectral centroid, sidechain depth ratios, filter sweep curves. That is a methodology contribution, not just a personal project.

Style as a mathematical object. If you can fully parameterize a musical style without copying any audio, you have shown that style is separable from content in a formal sense. That has implications for copyright theory, generative music, and how we think about artistic identity.

Interpretable music generation. The dominant paradigm in AI music (Suno, Udio, etc.) is latent-space black boxes. An analysis-by-synthesis approach produces something you can read, edit, and reason about. A trance generator where every parameter traces back to a measurement is the opposite of a black box — and far more useful for music producers who want control.

Verification methodology. The discipline enforced here — measure first, no excuses for untested work — is itself a methodology. Most audio tools are evaluated by ear, which is unreproducible. Building a measurable acceptance bar (CLAP scores, spectral comparisons, RMS envelopes) is a transferable framework for the broader field.

Application value

Procedural audio at style fidelity. Games and interactive media need music that adapts in real time. The ability to generate infinite variation within a precisely parameterized style — without licensing existing recordings — is commercially valuable and technically unsolved at this level of fidelity.

Live performance systems. The Death Angel vision — a machine that performs rather than plays back — touches HCI and music technology research around agency, responsiveness, and what "live" means for a generative system.

Synthesis education. Deconstructing a real, commercially successful sound into its constituent parameters is a far more motivating way to teach synthesis than abstract textbook examples. This project is an implicit curriculum.


The personal curiosity is the engine, but the output — a reproducible, measured, parametric model of a musical style — would stand on its own in a music information retrieval or music technology research context. The discipline imposed throughout (cite everything, measure everything, document everything) is exactly what separates a research contribution from a hobby project.


License

Mozilla Public License 2.0.

About

Procedural trance music generator in the style of Switch Angel's live-coded Strudel sets. No samples, DMCA-safe.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages