first strudel via AI
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
strudel/
|
||||||
|
strudel
|
||||||
96
agentic_workflow/SKILL.md
Normal file
96
agentic_workflow/SKILL.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
name: strudel-music
|
||||||
|
description: Generate professional music using Strudel (strudel.cc), a browser-based live coding environment. Use this skill whenever the user asks to create music, beats, loops, songs, melodies, basslines, drum patterns, or anything related to algorithmic music composition. Also trigger when the user mentions Strudel, TidalCycles, live coding music, beat making with code, generative music, or asks for patterns in mini-notation. This skill covers music theory, Strudel syntax, genre-specific production techniques, and full song composition. Use it even if the user just says "make me a beat" or "create a song" — Strudel code is the output format.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Strudel Music Production Skill
|
||||||
|
|
||||||
|
Generate professional-quality music as Strudel code for playback at https://strudel.cc
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
All output is valid Strudel code the user pastes into the REPL at strudel.cc and runs with Ctrl+Enter.
|
||||||
|
|
||||||
|
## Before Writing Any Code
|
||||||
|
|
||||||
|
1. Read `references/strudel-syntax.md` — complete language reference
|
||||||
|
2. Read `references/music-theory.md` — scales, chords, progressions, rhythm theory
|
||||||
|
3. Read `references/genre-recipes.md` — genre-specific templates and techniques
|
||||||
|
4. Read `references/production-tips.md` — mixing, arrangement, professional polish
|
||||||
|
|
||||||
|
## Core Workflow
|
||||||
|
|
||||||
|
### Step 1: Understand the Request
|
||||||
|
- What genre/mood/style? (trap, techno, ambient, pop, dnb, house, lo-fi, etc.)
|
||||||
|
- What tempo range? (see genre-recipes.md for defaults)
|
||||||
|
- Simple loop or full arrangement?
|
||||||
|
- Any reference artists? (use their style as guide)
|
||||||
|
|
||||||
|
### Step 2: Choose Musical Foundation
|
||||||
|
- Pick key and scale (see music-theory.md)
|
||||||
|
- Pick chord progression (see music-theory.md for common progressions per genre)
|
||||||
|
- Set tempo with `setcps(BPM/60/4)` or `setcpm(BPM/4)`
|
||||||
|
|
||||||
|
### Step 3: Build Layers (always use `$:` for multiple patterns)
|
||||||
|
Build in this order:
|
||||||
|
1. **Drums** — kick, snare/clap, hi-hats, percussion
|
||||||
|
2. **Bass** — sub bass or melodic bass
|
||||||
|
3. **Chords/Pads** — harmonic foundation
|
||||||
|
4. **Melody/Lead** — top-line or arpeggios
|
||||||
|
5. **FX/Texture** — atmosphere, risers, noise
|
||||||
|
|
||||||
|
### Step 4: Apply Effects & Polish
|
||||||
|
- Filter automation with signals (sine, saw, etc.)
|
||||||
|
- Reverb (room/size) and delay for space
|
||||||
|
- Gain dynamics for groove
|
||||||
|
- Pan for stereo width
|
||||||
|
- Pattern effects (rev, jux, off, every) for variation
|
||||||
|
|
||||||
|
### Step 5: Validate Output
|
||||||
|
- All code must be syntactically valid Strudel
|
||||||
|
- Must use `$:` prefix for each parallel pattern
|
||||||
|
- Tempo must be set explicitly
|
||||||
|
- No undefined functions or unsupported syntax
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
```js
|
||||||
|
// [Genre] — [Key] — [BPM] bpm
|
||||||
|
setcps(BPM/60/4)
|
||||||
|
|
||||||
|
$: // drums
|
||||||
|
...
|
||||||
|
|
||||||
|
$: // bass
|
||||||
|
...
|
||||||
|
|
||||||
|
$: // chords
|
||||||
|
...
|
||||||
|
|
||||||
|
$: // melody
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Always include a comment header with genre, key, and BPM.
|
||||||
|
|
||||||
|
## Critical Syntax Rules
|
||||||
|
|
||||||
|
1. Use `$:` before EACH pattern when playing multiple simultaneously
|
||||||
|
2. `setcps(BPM/60/4)` at the top — this converts BPM to cycles per second
|
||||||
|
3. Mini-notation goes inside quotes: `note("c3 e3 g3")`
|
||||||
|
4. Chain effects with dots: `.sound("piano").lpf(800).room(0.5)`
|
||||||
|
5. Parallel within one pattern: comma `,` — e.g. `s("bd sd, hh*8")`
|
||||||
|
6. Rest = `~` or `-`
|
||||||
|
7. Subsequence = `[a b]`, alternate per cycle = `<a b>`
|
||||||
|
8. Speed up = `*N`, slow down = `/N`
|
||||||
|
9. Elongate = `@N`, replicate = `!N`
|
||||||
|
10. Scale degrees with `n()` + `.scale()`, raw notes with `note()`
|
||||||
|
|
||||||
|
## Common Pitfalls to Avoid
|
||||||
|
|
||||||
|
- Do NOT use `stack()` AND `$:` together — use one or the other
|
||||||
|
- Do NOT forget `.sound()` when using `note()` — default is triangle
|
||||||
|
- Do NOT set `lpf` too low on sub bass — it will disappear
|
||||||
|
- Do NOT use `setbpm()` — it does not exist. Use `setcps()` or `setcpm()`
|
||||||
|
- Do NOT quote signal names: `.lpf(sine.range(200,2000))` not `.lpf("sine")`
|
||||||
|
- Do NOT put `$:` inside `stack()` — it's a top-level declaration only
|
||||||
251
agentic_workflow/genre_recipes.md
Normal file
251
agentic_workflow/genre_recipes.md
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
# Genre Recipes for Strudel
|
||||||
|
|
||||||
|
Copy-paste templates and production techniques for specific genres.
|
||||||
|
|
||||||
|
## Genre Quick Reference
|
||||||
|
|
||||||
|
| Genre | BPM | Key Feel | Drum Machine | Signature |
|
||||||
|
|-------|-----|----------|-------------|-----------|
|
||||||
|
| Lo-fi Hip-Hop | 70-90 | Minor/Dorian | Any vintage | Swing, dusty, vinyl crackle |
|
||||||
|
| Trap | 60-80 (half-time feel) | Minor | TR808 | Fast hats, sparse kicks, 808 sub |
|
||||||
|
| Boom-Bap | 85-100 | Minor | SP-1200/vintage | Chopped samples, swing |
|
||||||
|
| House | 120-130 | Minor/Major | TR909 | Four-on-floor, offbeat hats |
|
||||||
|
| Deep House | 118-125 | Minor 7ths | TR909 | Lush pads, jazzy chords |
|
||||||
|
| Techno | 125-140 | Minor/chromatic | TR909/TR808 | Driving, repetitive, industrial |
|
||||||
|
| Drum & Bass | 170-180 | Minor | Amen break style | Fast breaks, heavy bass |
|
||||||
|
| Ambient | 60-90 | Lydian/Major | None/minimal | Long reverb, slow pads |
|
||||||
|
| UK Garage | 130-140 | Minor | TR909 | Shuffled, 2-step |
|
||||||
|
| Dubstep | 140 (half-time) | Minor | Heavy | Sub bass wobbles |
|
||||||
|
| R&B / Neo-Soul | 65-85 | Minor 7/9 | Light drums | Lush chords, smooth |
|
||||||
|
| Synthwave | 80-120 | Minor | LinnDrum/TR707 | Retro synths, gated reverb |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lo-fi Hip-Hop
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Lo-fi Hip-Hop — Dm Dorian — 80 bpm
|
||||||
|
setcps(80/60/4)
|
||||||
|
|
||||||
|
$: s("[bd ~ ~ bd] [~ ~ ~ ~] [~ ~ bd ~] [~ ~ ~ ~]")
|
||||||
|
.bank("RolandTR808").gain(0.9).lpf(300)
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ [sd ~ ~ ~] ~ ~ ~ ~ [~ ~ ~ ~] ~ ~ [sd ~ ~ ~] ~ ~")
|
||||||
|
.bank("RolandTR808").gain(0.5).room(0.3)
|
||||||
|
|
||||||
|
$: s("[hh ~ hh ~]*2").bank("RolandTR808")
|
||||||
|
.gain("0.25 0.15 0.2 0.15").lpf(3000)
|
||||||
|
|
||||||
|
$: note("<[d3,f3,a3,c4] [g2,b2,d3,f3]>")
|
||||||
|
.sound("gm_electric_piano_1")
|
||||||
|
.lpf(2000).gain(0.2)
|
||||||
|
.room(0.5).size(0.8)
|
||||||
|
|
||||||
|
$: note("<d2 ~ d2 ~ g1 ~ ~ ~>")
|
||||||
|
.sound("sawtooth").lpf(400).gain(0.45)
|
||||||
|
.decay(0.4).sustain(0.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: swing feel, muted/warm tones, vinyl crackle, jazzy 7th chords, low-pass everything.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Trap
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Trap — Cm — 72 bpm (half-time feel)
|
||||||
|
setcps(72/60/4)
|
||||||
|
|
||||||
|
$: s("[bd ~ ~ bd] [~ ~ ~ ~] [~ ~ bd ~] [~ ~ ~ bd]")
|
||||||
|
.bank("RolandTR808").gain(1.1).lpf(200)
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ cp ~ ~ ~, ~ ~ ~ ~ cp ~ ~ ~")
|
||||||
|
.bank("RolandTR808").gain(0.7).room(0.2)
|
||||||
|
|
||||||
|
$: s("hh*16").bank("RolandTR808")
|
||||||
|
.gain(sine.range(0.05, 0.35).fast(4))
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ ~ ~ oh ~").bank("RolandTR808").gain(0.2)
|
||||||
|
|
||||||
|
$: note("<c1 ab0 f0 g0>")
|
||||||
|
.sound("sine").gain(0.65).lpf(100)
|
||||||
|
.decay(0.8).sustain(0)
|
||||||
|
|
||||||
|
$: note("<[c3,eb3,g3,bb3] [ab2,c3,eb3,g3] [f2,ab2,c3,eb3] [g2,bb2,d3,f3]>")
|
||||||
|
.sound("sawtooth").lpf(800).lpq(2).gain(0.15)
|
||||||
|
.room(0.4).size(0.7)
|
||||||
|
.attack(0.3).release(0.6)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: 808 sub bass (long sine decay), rapid hi-hat rolls (*16 to *32), sparse kick patterns, half-time snare, dark pads.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## House
|
||||||
|
|
||||||
|
```js
|
||||||
|
// House — Am — 124 bpm
|
||||||
|
setcps(124/60/4)
|
||||||
|
|
||||||
|
$: s("bd*4").bank("RolandTR909").gain(1.1)
|
||||||
|
|
||||||
|
$: s("~ cp ~ cp").bank("RolandTR909").gain(0.65).room(0.2)
|
||||||
|
|
||||||
|
$: s("[~ hh]*4").bank("RolandTR909").gain(0.3)
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ ~ ~ oh ~").bank("RolandTR909").gain(0.2)
|
||||||
|
|
||||||
|
$: note("<[a2,c3,e3,g3] [d3,f3,a3,c4]>")
|
||||||
|
.sound("sawtooth").lpf(sine.range(500, 2000).slow(8))
|
||||||
|
.lpq(3).gain(0.2)
|
||||||
|
.room(0.4).size(0.6)
|
||||||
|
.attack(0.2).release(0.5)
|
||||||
|
|
||||||
|
$: note("a1*4").sound("sine")
|
||||||
|
.gain(0.5).lpf(150).decay(0.15).sustain(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: four-on-the-floor kick, offbeat hi-hats, clap on 2&4, filter sweeps, looping.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Techno
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Techno — Cm — 132 bpm
|
||||||
|
setcps(132/60/4)
|
||||||
|
|
||||||
|
$: s("bd*4").bank("RolandTR909").gain(1.2).lpf(250)
|
||||||
|
|
||||||
|
$: s("[~ hh]*4").bank("RolandTR909").gain(0.3)
|
||||||
|
|
||||||
|
$: s("hh*16").bank("RolandTR909")
|
||||||
|
.gain("0.3 0.1 0.2 0.1 0.3 0.1 0.2 0.1 0.35 0.1 0.2 0.1 0.3 0.1 0.25 0.15")
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ cp ~ ~ ~, ~ ~ ~ ~ cp ~ ~ ~")
|
||||||
|
.bank("RolandTR909").gain(0.7).room(0.15)
|
||||||
|
|
||||||
|
$: note("c2 c2 [c2 eb2] c2 c2 [c2 g2] c2 [c2 c3]")
|
||||||
|
.sound("sawtooth")
|
||||||
|
.lpf(sine.range(300, 3000).slow(8)).lpq(8)
|
||||||
|
.gain(0.4).decay(0.1).sustain(0)
|
||||||
|
|
||||||
|
$: note("<[c4,eb4,g4] ~ ~ ~ [ab3,c4,eb4] ~ ~ ~>")
|
||||||
|
.sound("square").lpf(1200).gain(0.1)
|
||||||
|
.room(0.3).decay(0.05).sustain(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: driving kick, acid bassline (sawtooth + high resonance filter sweep), minimal, hypnotic repetition, industrial textures.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Drum & Bass
|
||||||
|
|
||||||
|
```js
|
||||||
|
// DnB — Am — 174 bpm
|
||||||
|
setcps(174/60/4)
|
||||||
|
|
||||||
|
$: s("[bd ~ ~ ~] [~ ~ sd ~] [~ bd ~ ~] [sd ~ ~ ~]")
|
||||||
|
.bank("RolandTR909").gain(1)
|
||||||
|
|
||||||
|
$: s("hh*16").bank("RolandTR909")
|
||||||
|
.gain(sine.range(0.05, 0.3).fast(2))
|
||||||
|
.pan(sine.range(0.2, 0.8).fast(3))
|
||||||
|
|
||||||
|
$: note("[a1 ~] [~ a1] [~ ~] [a1 ~], [~ ~] [c2 ~] [~ ~] [a1 ~]")
|
||||||
|
.sound("sawtooth")
|
||||||
|
.lpf(sine.range(200, 800).slow(8)).lpq(4)
|
||||||
|
.gain(0.55)
|
||||||
|
|
||||||
|
$: note("<[a3,c4,e4] [c4,e4,g4]>").slow(4)
|
||||||
|
.sound("triangle").lpf(2500).gain(0.08)
|
||||||
|
.room(0.7).size(0.9).attack(0.8).release(2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: fast tempo 170+, syncopated breaks, heavy "reese" bass (detuned sawtooth), atmospheric pads.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ambient
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Ambient — F Lydian — 60 bpm
|
||||||
|
setcps(60/60/4)
|
||||||
|
|
||||||
|
$: note("<[f3,a3,c4,e4] [g3,b3,d4,f#4] [a3,c4,e4,g4] [f3,a3,c4,e4]>").slow(2)
|
||||||
|
.sound("triangle")
|
||||||
|
.lpf(sine.range(800, 2500).slow(16))
|
||||||
|
.gain(0.12)
|
||||||
|
.room(0.9).size(0.99)
|
||||||
|
.attack(2).release(4)
|
||||||
|
|
||||||
|
$: note("~ f5 ~ a5, ~ ~ c6 ~").slow(4)
|
||||||
|
.sound("sine").gain(0.06)
|
||||||
|
.room(0.95).size(0.99)
|
||||||
|
.delay(0.6).delaytime(0.5).delayfeedback(0.6)
|
||||||
|
|
||||||
|
$: s("~ ~ bd ~").gain(0.3).lpf(100).room(0.8).slow(2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: extremely slow, huge reverb/delay, lydian for dreaminess, long attack/release, minimal drums.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## R&B / Neo-Soul (Saul, Frank Ocean, SZA style)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// R&B — Cm — 68 bpm
|
||||||
|
setcps(68/60/4)
|
||||||
|
|
||||||
|
$: s("[bd ~ ~ ~] [~ ~ ~ bd] [~ ~ bd ~] [~ ~ ~ ~]")
|
||||||
|
.bank("RolandTR808").gain(0.9).lpf(180)
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ [rim ~ ~ ~] ~ ~ ~ ~ ~ ~ [rim ~ ~ ~] ~ ~")
|
||||||
|
.bank("RolandTR808").gain(0.3).room(0.4)
|
||||||
|
|
||||||
|
$: s("[~ hh ~ hh]*2").bank("RolandTR808").gain(0.12)
|
||||||
|
|
||||||
|
$: note("<[c3,eb3,g3,bb3] [f2,ab2,c3,eb3] [ab2,c3,eb3,g3] [g2,bb2,d3,f3]>")
|
||||||
|
.sound("triangle")
|
||||||
|
.lpf(sine.range(600, 1500).slow(8)).lpq(2)
|
||||||
|
.gain(0.18)
|
||||||
|
.room(0.6).size(0.9)
|
||||||
|
.attack(0.4).release(1)
|
||||||
|
|
||||||
|
$: note("~ eb5 ~ g5, ~ ~ bb5 ~, ~ ~ ~ c6").slow(2)
|
||||||
|
.sound("sine").gain(0.08)
|
||||||
|
.room(0.8).size(0.95)
|
||||||
|
.delay(0.5).delaytime(0.375).delayfeedback(0.4)
|
||||||
|
|
||||||
|
$: note("<c1 f0 ab0 g0>")
|
||||||
|
.sound("sine").gain(0.55).lpf(100)
|
||||||
|
.decay(0.6).sustain(0.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: slow tempo, minor 7th/9th chords, sparse drums, warm pads, ethereal delayed melodies, big reverb, intimate feel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Synthwave
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Synthwave — Am — 100 bpm
|
||||||
|
setcps(100/60/4)
|
||||||
|
|
||||||
|
$: s("bd*4, [~ sd]*2, [~ hh]*4")
|
||||||
|
.bank("RolandTR707").gain("1 0.6 0.25")
|
||||||
|
|
||||||
|
$: note("a1 a1 [a1 c2] a1").sound("sawtooth")
|
||||||
|
.lpf(600).gain(0.45).decay(0.2).sustain(0.1)
|
||||||
|
|
||||||
|
$: note("<[a3,c4,e4] [f3,a3,c4] [d3,f3,a3] [e3,g3,b3]>")
|
||||||
|
.sound("square").lpf(1500).gain(0.12)
|
||||||
|
.room(0.5).size(0.7)
|
||||||
|
.attack(0.1).release(0.5)
|
||||||
|
|
||||||
|
$: n("<0 4 7 4 12 7 4 0>").scale("A4:minor")
|
||||||
|
.sound("sawtooth").lpf(2000).gain(0.12)
|
||||||
|
.delay(0.3).delaytime(0.125)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key characteristics: gated snare, arpeggiated leads, sawtooth/square synths, 80s drum machines, driving bass.
|
||||||
203
agentic_workflow/music_theory.md
Normal file
203
agentic_workflow/music_theory.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Music Theory for Strudel Production
|
||||||
|
|
||||||
|
Practical music theory for generating professional-sounding Strudel code.
|
||||||
|
|
||||||
|
## 1. Notes & Octaves
|
||||||
|
|
||||||
|
### Note Names (Strudel format)
|
||||||
|
```
|
||||||
|
C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B
|
||||||
|
```
|
||||||
|
Octave range: 0-8. Middle C = `c4` (MIDI 60). Sub bass = `c1`-`c2`. Melody sweet spot = `c4`-`c6`.
|
||||||
|
|
||||||
|
### MIDI Note Numbers
|
||||||
|
C1=24, C2=36, C3=48, C4=60, C5=72, C6=84
|
||||||
|
|
||||||
|
## 2. Scales
|
||||||
|
|
||||||
|
### When to Use Which Scale
|
||||||
|
| Scale | Mood/Feel | Best for |
|
||||||
|
|-------|-----------|----------|
|
||||||
|
| minor | Sad, dark, introspective | R&B, trap, lo-fi, ambient |
|
||||||
|
| major | Happy, bright, uplifting | Pop, dance, house |
|
||||||
|
| dorian | Minor but warm/jazzy | Neo-soul, funk, lo-fi hip-hop |
|
||||||
|
| mixolydian | Major but bluesy | Funk, rock, blues |
|
||||||
|
| minor:pentatonic | Universal minor | Any genre, safe choice |
|
||||||
|
| major:pentatonic | Universal major | Pop, EDM, any happy genre |
|
||||||
|
| phrygian | Dark, Spanish, exotic | Metal, flamenco, dark electronic |
|
||||||
|
| lydian | Dreamy, ethereal, floating | Ambient, film score, dream pop |
|
||||||
|
| blues | Gritty, raw | Blues, blues-rock, jazz |
|
||||||
|
| harmonicMinor | Dramatic, Middle Eastern | Dramatic builds, ethnic music |
|
||||||
|
| chromatic | Dissonant, all notes | Sound effects, transitions |
|
||||||
|
|
||||||
|
### Scale Formulas (semitones from root)
|
||||||
|
| Scale | Steps |
|
||||||
|
|-------|-------|
|
||||||
|
| major | 0 2 4 5 7 9 11 |
|
||||||
|
| minor | 0 2 3 5 7 8 10 |
|
||||||
|
| dorian | 0 2 3 5 7 9 10 |
|
||||||
|
| mixolydian | 0 2 4 5 7 9 10 |
|
||||||
|
| phrygian | 0 1 3 5 7 8 10 |
|
||||||
|
| lydian | 0 2 4 6 7 9 11 |
|
||||||
|
| minor:pentatonic | 0 3 5 7 10 |
|
||||||
|
| major:pentatonic | 0 2 4 7 9 |
|
||||||
|
| blues | 0 3 5 6 7 10 |
|
||||||
|
|
||||||
|
## 3. Chords
|
||||||
|
|
||||||
|
### Building Chords in Strudel
|
||||||
|
Chords are multiple notes played at once using commas or brackets:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Method 1: explicit notes
|
||||||
|
note("[c3,eb3,g3]") // Cm chord
|
||||||
|
|
||||||
|
// Method 2: scale degrees
|
||||||
|
n("[0,2,4]").scale("C:minor") // same Cm chord
|
||||||
|
|
||||||
|
// Method 3: voicing function
|
||||||
|
voicing("Cm7") // automatic voicing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Essential Chord Types
|
||||||
|
| Name | Formula (semitones) | Strudel notes (from C) | Feel |
|
||||||
|
|------|---------------------|------------------------|------|
|
||||||
|
| Major | 0,4,7 | `[c,e,g]` | Happy, resolved |
|
||||||
|
| Minor | 0,3,7 | `[c,eb,g]` | Sad, introspective |
|
||||||
|
| Major 7 | 0,4,7,11 | `[c,e,g,b]` | Dreamy, lush |
|
||||||
|
| Minor 7 | 0,3,7,10 | `[c,eb,g,bb]` | Warm, jazzy |
|
||||||
|
| Dominant 7 | 0,4,7,10 | `[c,e,g,bb]` | Tension, bluesy |
|
||||||
|
| Diminished | 0,3,6 | `[c,eb,gb]` | Tense, unstable |
|
||||||
|
| Suspended 2 | 0,2,7 | `[c,d,g]` | Open, ambiguous |
|
||||||
|
| Suspended 4 | 0,5,7 | `[c,f,g]` | Yearning |
|
||||||
|
| Add9 | 0,4,7,14 | `[c,e,g,d4]` | Modern, bright |
|
||||||
|
| Minor 9 | 0,3,7,10,14 | `[c,eb,g,bb,d4]` | Rich R&B |
|
||||||
|
|
||||||
|
## 4. Chord Progressions
|
||||||
|
|
||||||
|
### By Genre
|
||||||
|
|
||||||
|
**Pop / Dance / Happy**
|
||||||
|
| Name | Numerals | In C Major | Strudel |
|
||||||
|
|------|----------|------------|---------|
|
||||||
|
| Classic Pop | I-V-vi-IV | C-G-Am-F | `note("<[c3,e3,g3] [g2,b2,d3] [a2,c3,e3] [f2,a2,c3]>")` |
|
||||||
|
| Four Chord | I-IV-vi-V | C-F-Am-G | `note("<[c3,e3,g3] [f2,a2,c3] [a2,c3,e3] [g2,b2,d3]>")` |
|
||||||
|
|
||||||
|
**Sad / Dark / R&B / Trap**
|
||||||
|
| Name | Numerals | In C Minor | Strudel |
|
||||||
|
|------|----------|------------|---------|
|
||||||
|
| Sad Minor | i-VI-III-VII | Cm-Ab-Eb-Bb | `note("<[c3,eb3,g3] [ab2,c3,eb3] [eb3,g3,bb3] [bb2,d3,f3]>")` |
|
||||||
|
| Dark Trap | i-iv-VI-V | Cm-Fm-Ab-G | `note("<[c3,eb3,g3] [f2,ab2,c3] [ab2,c3,eb3] [g2,b2,d3]>")` |
|
||||||
|
| Neo-Soul | i7-iv7-VII7-III7 | Cm7-Fm7-Bb7-Eb7 | `note("<[c3,eb3,g3,bb3] [f2,ab2,c3,eb3] [bb2,d3,f3,ab3] [eb3,g3,bb3,db4]>")` |
|
||||||
|
|
||||||
|
**Lo-fi / Chill**
|
||||||
|
| Name | Numerals | In D Dorian | Strudel |
|
||||||
|
|------|----------|-------------|---------|
|
||||||
|
| Dorian Chill | i7-IV7 | Dm7-G7 | `note("<[d3,f3,a3,c4] [g2,b2,d3,f3]>")` |
|
||||||
|
| Jazz ii-V-I | ii7-V7-Imaj7 | Dm7-G7-Cmaj7 | `note("<[d3,f3,a3,c4] [g2,b2,d3,f3] [c3,e3,g3,b3]>")` |
|
||||||
|
|
||||||
|
**House / Techno**
|
||||||
|
| Name | Numerals | In A Minor | Strudel |
|
||||||
|
|------|----------|------------|---------|
|
||||||
|
| Minimal | i-VII | Am-G | `note("<[a2,c3,e3] [g2,b2,d3]>")` |
|
||||||
|
| Deep House | i7-iv7 | Am7-Dm7 | `note("<[a2,c3,e3,g3] [d3,f3,a3,c4]>")` |
|
||||||
|
|
||||||
|
**Dramatic / Cinematic**
|
||||||
|
| Name | In C Minor | Strudel |
|
||||||
|
|------|------------|---------|
|
||||||
|
| Epic | Cm-Ab-Bb-Cm | `note("<[c3,eb3,g3] [ab2,c3,eb3] [bb2,d3,f3] [c3,eb3,g3]>")` |
|
||||||
|
|
||||||
|
## 5. Rhythm Theory
|
||||||
|
|
||||||
|
### Time Signatures & Subdivision
|
||||||
|
Standard 4/4: 1 cycle = 1 bar = 4 beats.
|
||||||
|
- Whole notes: `"c"` — 1 per cycle
|
||||||
|
- Half notes: `"c c"` — 2 per cycle
|
||||||
|
- Quarter notes: `"c c c c"` — 4 per cycle
|
||||||
|
- Eighth notes: `"c*8"` or 8 items — 8 per cycle
|
||||||
|
- Sixteenth notes: `"c*16"` — 16 per cycle
|
||||||
|
- Triplets: `"c*3"` or `"c*6"` or `"c*12"`
|
||||||
|
|
||||||
|
### Swing / Shuffle
|
||||||
|
Shuffle feel: elongate first of each pair with `@`:
|
||||||
|
```js
|
||||||
|
note("[c@2 e] [g@2 b]") // swing feel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Drum Patterns (16-step grid notation)
|
||||||
|
Each `[ ]` = one beat (4 sixteenths), `x`=hit, `-`=rest:
|
||||||
|
|
||||||
|
**Four-on-the-floor (house/techno)**
|
||||||
|
```
|
||||||
|
Kick: [x---] [x---] [x---] [x---]
|
||||||
|
Snare: [----] [x---] [----] [x---]
|
||||||
|
HH: [--x-] [--x-] [--x-] [--x-]
|
||||||
|
```
|
||||||
|
→ `s("bd*4, [~ sd]*2, [~ hh]*4")`
|
||||||
|
|
||||||
|
**Boom-bap (hip-hop)**
|
||||||
|
```
|
||||||
|
Kick: [x---] [----] [--x-] [----]
|
||||||
|
Snare: [----] [x---] [----] [x---]
|
||||||
|
HH: [x-x-] [x-x-] [x-x-] [x-x-]
|
||||||
|
```
|
||||||
|
→ `s("bd ~ ~ ~ ~ ~ bd ~, ~ ~ ~ ~ sd ~ ~ ~, hh*8").slow(2)` adjusted to taste
|
||||||
|
|
||||||
|
**Trap**
|
||||||
|
```
|
||||||
|
Kick: [x------x] placed irregularly, sparse
|
||||||
|
Snare: on beat 3 (every 2 beats)
|
||||||
|
HH: very fast rolls *16 to *32 with velocity variation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Breakbeat / DnB**
|
||||||
|
```
|
||||||
|
Kick: [x---] [----] [--x-] [----]
|
||||||
|
Snare: [----] [x---] [----] [x---]
|
||||||
|
```
|
||||||
|
→ Syncopated, off-grid kicks, fast tempo 170+
|
||||||
|
|
||||||
|
## 6. Arrangement Principles
|
||||||
|
|
||||||
|
### Song Sections
|
||||||
|
Use `<... >` alternation and `/N` slowing for section changes:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 4-bar chord progression cycling
|
||||||
|
note("<[c3,e3,g3] [a2,c3,e3] [f2,a2,c3] [g2,b2,d3]>")
|
||||||
|
|
||||||
|
// 8-bar pattern: 4 bars of A then 4 bars of B
|
||||||
|
note("<[pattern A]*4 [pattern B]*4>/8")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building Tension
|
||||||
|
- Gradually open filter: `.lpf(sine.range(300, 3000).slow(16))`
|
||||||
|
- Add density: `.every(4, x => x.ply(2))`
|
||||||
|
- Raise pitch: `.add("<0 0 0 12>")`
|
||||||
|
- Increase delay feedback over time
|
||||||
|
|
||||||
|
### Drop Technique
|
||||||
|
- Build section: filtered, rising, less drums
|
||||||
|
- Drop: full drums, open filter, bass hits hard
|
||||||
|
|
||||||
|
## 7. Frequency Ranges for Mixing
|
||||||
|
|
||||||
|
| Range | Hz | Role | Strudel tip |
|
||||||
|
|-------|-----|------|-------------|
|
||||||
|
| Sub bass | 20-80 | Felt not heard | `.lpf(80)` on sub, `sine` waveform |
|
||||||
|
| Bass | 80-300 | Bass body | `.lpf(300)` for warmth |
|
||||||
|
| Low-mid | 300-800 | Warmth/mud | Be careful, can muddy mix |
|
||||||
|
| Mid | 800-2000 | Presence | Melody/vocal territory |
|
||||||
|
| High-mid | 2000-6000 | Clarity/bite | Lead synths, snares |
|
||||||
|
| High | 6000-20000 | Air/sparkle | Hi-hats, cymbals |
|
||||||
|
|
||||||
|
### Gain Staging Guidelines
|
||||||
|
| Element | Gain range | Notes |
|
||||||
|
|---------|------------|-------|
|
||||||
|
| Kick | 1.0-1.2 | Loudest element |
|
||||||
|
| Snare/Clap | 0.6-0.8 | Prominent but below kick |
|
||||||
|
| Hi-hats | 0.15-0.35 | Subtle, dynamic |
|
||||||
|
| Bass | 0.4-0.6 | Strong but not overpowering |
|
||||||
|
| Pads/Chords | 0.1-0.25 | Background wash |
|
||||||
|
| Lead melody | 0.15-0.3 | Present but not harsh |
|
||||||
|
| FX/Textures | 0.05-0.15 | Barely audible atmosphere |
|
||||||
231
agentic_workflow/prod_tips.md
Normal file
231
agentic_workflow/prod_tips.md
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
# Production Tips for Strudel
|
||||||
|
|
||||||
|
Professional-level techniques for polished output.
|
||||||
|
|
||||||
|
## 1. Mixing in Strudel
|
||||||
|
|
||||||
|
### Gain Staging
|
||||||
|
Always set explicit gain values. Elements should not fight for space:
|
||||||
|
|
||||||
|
```js
|
||||||
|
$: s("bd*4").gain(1.1) // kick = loudest
|
||||||
|
$: s("[~ sd]*2").gain(0.7) // snare = clear
|
||||||
|
$: s("[~ hh]*4").gain(0.25) // hats = subtle
|
||||||
|
$: note("c2*4").s("sine").gain(0.5) // bass = strong
|
||||||
|
$: note("[c4,e4,g4]").s("triangle").gain(0.15) // pad = background
|
||||||
|
$: note("c5 e5 g5").s("sine").gain(0.1) // melody = present but soft
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frequency Separation
|
||||||
|
Each layer should own a frequency range. Use `.lpf()` and `.hpf()` to carve space:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Sub bass: ONLY low frequencies
|
||||||
|
note("c1").sound("sine").lpf(80).gain(0.6)
|
||||||
|
|
||||||
|
// Mid bass: remove sub, keep warmth
|
||||||
|
note("c2").sound("sawtooth").lpf(600).hpf(80).gain(0.3)
|
||||||
|
|
||||||
|
// Pad: remove low-end mud
|
||||||
|
note("[c3,e3,g3]").sound("triangle").hpf(300).lpf(3000).gain(0.15)
|
||||||
|
|
||||||
|
// Hi-hats: only highs
|
||||||
|
s("hh*8").hpf(3000).gain(0.25)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stereo Width with Pan
|
||||||
|
Center: kick, bass, snare. Sides: hats, pads, FX.
|
||||||
|
|
||||||
|
```js
|
||||||
|
$: s("bd*4").gain(1) // center (default)
|
||||||
|
$: s("hh*8").gain(0.2).pan(sine.range(0.3, 0.7)) // gentle side movement
|
||||||
|
$: note("[c4,e4,g4]").s("saw").gain(0.1).jux(rev) // wide stereo
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Movement & Variation
|
||||||
|
|
||||||
|
### Filter Automation (Essential for Non-Static Sound)
|
||||||
|
```js
|
||||||
|
// Slow sweep over 8 cycles
|
||||||
|
.lpf(sine.range(300, 3000).slow(8))
|
||||||
|
|
||||||
|
// Fast wobble
|
||||||
|
.lpf(sine.range(200, 1500).fast(2))
|
||||||
|
|
||||||
|
// Random variation
|
||||||
|
.lpf(perlin.range(500, 2000))
|
||||||
|
|
||||||
|
// Stepped changes
|
||||||
|
.lpf("<400 800 1200 2000>")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rhythmic Variation with `every`
|
||||||
|
```js
|
||||||
|
// Reverse every 4th cycle
|
||||||
|
s("bd sd [~ bd] sd").every(4, x => x.rev())
|
||||||
|
|
||||||
|
// Double speed every 3rd cycle (fill)
|
||||||
|
s("bd sd [~ bd] sd").every(3, x => x.ply(2))
|
||||||
|
|
||||||
|
// Add filter sweep every 8th cycle
|
||||||
|
s("hh*16").every(8, x => x.lpf(sine.range(500, 5000)))
|
||||||
|
```
|
||||||
|
|
||||||
|
### The `off` Technique (Ghost Notes / Echoes)
|
||||||
|
```js
|
||||||
|
// Ghost notes: offset copy played softer and pitch-shifted
|
||||||
|
n("0 2 4 6").scale("C:minor")
|
||||||
|
.off(1/8, x => x.add(7).gain(0.3))
|
||||||
|
.sound("piano")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Humanize with Randomness
|
||||||
|
```js
|
||||||
|
// Random gain variation (human feel)
|
||||||
|
s("hh*16").gain(perlin.range(0.1, 0.35))
|
||||||
|
|
||||||
|
// Random panning
|
||||||
|
s("rim*4").pan(rand)
|
||||||
|
|
||||||
|
// Sometimes skip notes
|
||||||
|
s("hh*16").sometimesBy(0.2, x => x.gain(0))
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Sound Design Techniques
|
||||||
|
|
||||||
|
### Layering Synths
|
||||||
|
```js
|
||||||
|
// Fat bass: two detuned saws
|
||||||
|
note("c2").sound("sawtooth,sawtooth").detune("0 0.3")
|
||||||
|
|
||||||
|
// Rich pad: multiple waveforms
|
||||||
|
note("[c3,e3,g3]").sound("sawtooth,triangle,square")
|
||||||
|
.gain("0.15 0.1 0.05")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acid Bass (Resonant Filter)
|
||||||
|
```js
|
||||||
|
note("c2 c2 [c2 eb2] c2")
|
||||||
|
.sound("sawtooth")
|
||||||
|
.lpf(sine.range(200, 4000).fast(2))
|
||||||
|
.lpq(10) // high resonance = acid sound
|
||||||
|
.decay(0.1).sustain(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pluck Sound
|
||||||
|
```js
|
||||||
|
note("c4 e4 g4 c5")
|
||||||
|
.sound("triangle")
|
||||||
|
.lpf(3000)
|
||||||
|
.decay(0.15).sustain(0).release(0.3)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pad Sound
|
||||||
|
```js
|
||||||
|
note("[c3,e3,g3,b3]")
|
||||||
|
.sound("sawtooth")
|
||||||
|
.lpf(800).lpq(1)
|
||||||
|
.attack(0.5).release(1.5)
|
||||||
|
.room(0.7).size(0.9)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sub Bass (808 Style)
|
||||||
|
```js
|
||||||
|
note("c1").sound("sine")
|
||||||
|
.lpf(100).gain(0.6)
|
||||||
|
.decay(0.8).sustain(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Arrangement in a Single Loop
|
||||||
|
|
||||||
|
Since Strudel works in cycles, use `<>` and `/N` for section variation:
|
||||||
|
|
||||||
|
### A/B Pattern Alternation
|
||||||
|
```js
|
||||||
|
// Drums: minimal A, full B
|
||||||
|
$: s("<[bd ~ ~ ~]*4 [bd*4]>/8") // kick: sparse then full every 8 cycles
|
||||||
|
$: s("<[~ ~ ~ ~]*4 [[~ sd]*2]*4>/8") // snare: silent then enters
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build-up Effect
|
||||||
|
```js
|
||||||
|
// Filter opens gradually over 16 cycles
|
||||||
|
$: note("c2*4").sound("sawtooth")
|
||||||
|
.lpf(sine.range(200, 4000).slow(16))
|
||||||
|
|
||||||
|
// Hats get denser
|
||||||
|
$: s("hh*<4 8 12 16>")
|
||||||
|
.gain(0.2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Drop
|
||||||
|
```js
|
||||||
|
// Build: filtered, no kick, rising
|
||||||
|
// Drop: everything at once, filter open
|
||||||
|
$: s("<[~ ~ ~ ~]*4 [bd*4]*4>/8") // kick enters at drop
|
||||||
|
$: note("c2*4").sound("sawtooth")
|
||||||
|
.lpf(saw.range(200, 3000).slow(8)) // filter opens
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Advanced Pattern Techniques
|
||||||
|
|
||||||
|
### Polyrhythm
|
||||||
|
```js
|
||||||
|
// 3 against 4
|
||||||
|
note("c3 e3 g3, c4 e4 g4 b4").sound("piano")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Euclidean Rhythms
|
||||||
|
```js
|
||||||
|
// Euclidean distribution of 3 hits in 8 steps
|
||||||
|
sound("{bd bd bd}%8")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Probability
|
||||||
|
```js
|
||||||
|
// Random elements
|
||||||
|
s("bd sd? hh bd").bank("RolandTR909") // sd has 50% chance
|
||||||
|
```
|
||||||
|
|
||||||
|
### Progressive Transformation
|
||||||
|
```js
|
||||||
|
// Pattern morphs over time using every + sometimes
|
||||||
|
n("0 2 4 6").scale("C:minor").sound("piano")
|
||||||
|
.every(4, x => x.add(2))
|
||||||
|
.every(3, x => x.rev())
|
||||||
|
.sometimes(x => x.delay(0.5))
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Common Professional Patterns
|
||||||
|
|
||||||
|
### Side-chain Simulation
|
||||||
|
Strudel can't do real sidechain, but you can simulate pump:
|
||||||
|
```js
|
||||||
|
// Pad volume ducks on kick hits
|
||||||
|
note("[c3,e3,g3]").sound("sawtooth")
|
||||||
|
.gain("0.05 0.15 0.2 0.15") // quiet on beat 1 (where kick is)
|
||||||
|
.lpf(1200).room(0.5)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Call and Response
|
||||||
|
```js
|
||||||
|
note("<[c4 e4 g4 ~] [~ ~ ~ [b3 c4]]>")
|
||||||
|
.sound("piano")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rhythmic Chord Stabs
|
||||||
|
```js
|
||||||
|
note("<[c3,eb3,g3] ~ ~ ~ [ab2,c3,eb3] ~ ~ ~>")
|
||||||
|
.sound("square")
|
||||||
|
.decay(0.05).sustain(0).gain(0.15)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Debugging Checklist
|
||||||
|
|
||||||
|
If it sounds wrong:
|
||||||
|
1. **No sound?** — Check `.sound()` is set. Check gain > 0. Check `lpf` isn't too low.
|
||||||
|
2. **Too muddy?** — Add `.hpf()` to pads/mids. Reduce room on bass.
|
||||||
|
3. **Too static?** — Add signal modulation to filters. Use `every()` for variation.
|
||||||
|
4. **Timing weird?** — Check `setcps()` formula. Verify mini-notation subdivisions.
|
||||||
|
5. **Clashing notes?** — Use `.scale()` to stay in key. Check octave numbers.
|
||||||
|
6. **Too loud/quiet?** — Review gain staging. Kick ~1.0, everything else lower.
|
||||||
262
agentic_workflow/syntax.md
Normal file
262
agentic_workflow/syntax.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# Strudel Syntax Reference
|
||||||
|
|
||||||
|
Complete reference for the Strudel live coding language (strudel.cc).
|
||||||
|
|
||||||
|
## 1. Tempo & Timing
|
||||||
|
|
||||||
|
```js
|
||||||
|
setcps(BPM/60/4) // cycles per second — PREFERRED method
|
||||||
|
setcpm(BPM/4) // cycles per minute — alternative
|
||||||
|
// Default: 0.5 cps = 120 BPM = 1 cycle every 2 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
BPM to CPS conversion: `cps = BPM / 60 / 4` (assuming 4 beats per cycle).
|
||||||
|
|
||||||
|
## 2. Mini-Notation (Pattern Language)
|
||||||
|
|
||||||
|
Used inside quotes in functions like `sound("...")`, `note("...")`, `n("...")`.
|
||||||
|
|
||||||
|
### Sequencing
|
||||||
|
| Syntax | Meaning | Example |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| `a b c` | Sequence (space-separated) | `sound("bd sd hh")` |
|
||||||
|
| `[a b]` | Sub-sequence (squish into parent slot) | `sound("bd [hh hh] sd hh")` |
|
||||||
|
| `a , b` | Play in parallel (polyrhythm) | `sound("bd sd, hh*4")` |
|
||||||
|
| `< a b >` | Alternate: one per cycle | `sound("<bd sd hh>")` |
|
||||||
|
| `~` or `-` | Rest / silence | `sound("bd ~ sd ~")` |
|
||||||
|
|
||||||
|
### Speed & Duration
|
||||||
|
| Syntax | Meaning | Example |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| `a*N` | Repeat N times (speed up) | `sound("hh*16")` — 16 hi-hats per cycle |
|
||||||
|
| `a/N` | Slow down by factor N | `note("[c e g b]/2")` — over 2 cycles |
|
||||||
|
| `a@N` | Elongate: take N time units | `note("c@3 e")` — c is 3/4, e is 1/4 |
|
||||||
|
| `a!N` | Replicate N times (copy) | `note("c!3 e")` — c c c e |
|
||||||
|
|
||||||
|
### Sample Selection
|
||||||
|
| Syntax | Meaning | Example |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| `name:N` | Sample number N | `sound("bd:0 bd:1 bd:2")` |
|
||||||
|
|
||||||
|
### Advanced Mini-Notation
|
||||||
|
| Syntax | Meaning | Example |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| `a?` | 50% chance of playing | `sound("hh hh? hh hh?")` |
|
||||||
|
| `a?0.3` | 30% chance of playing | `sound("hh?0.3")` |
|
||||||
|
| `{a b c}%N` | Euclidean rhythm | `sound("{bd bd bd}%8")` |
|
||||||
|
|
||||||
|
## 3. Core Functions
|
||||||
|
|
||||||
|
### Sound Sources
|
||||||
|
```js
|
||||||
|
sound("bd sd hh cp") // play samples by name (alias: s)
|
||||||
|
note("c3 e3 g3 b3") // play pitched notes (letter or MIDI number)
|
||||||
|
n("0 2 4 6") // play by index (useful with .scale())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sound Selection
|
||||||
|
```js
|
||||||
|
.sound("piano") // set instrument/sample
|
||||||
|
.s("piano") // shorthand for .sound()
|
||||||
|
.bank("RolandTR909") // select drum machine bank
|
||||||
|
.n("0 1 2 3") // sample number within a sound
|
||||||
|
```
|
||||||
|
|
||||||
|
### Drum Sound Names
|
||||||
|
`bd` (bass drum), `sd` (snare), `hh` (hi-hat closed), `oh` (open hi-hat),
|
||||||
|
`cp` (clap), `rim` (rimshot), `lt` (low tom), `mt` (mid tom), `ht` (high tom),
|
||||||
|
`rd` (ride), `cr` (crash)
|
||||||
|
|
||||||
|
### Drum Machine Banks
|
||||||
|
`RolandTR808`, `RolandTR909`, `RolandTR707`, `RolandTR505`,
|
||||||
|
`RolandCompurhythm1000`, `AkaiLinn`, `RhythmAce`, `ViscoSpaceDrum`, `CasioRZ1`
|
||||||
|
|
||||||
|
### Synth Waveforms (use as sound name)
|
||||||
|
`sawtooth`, `square`, `triangle`, `sine`, `white`, `pink`, `brown`
|
||||||
|
|
||||||
|
### GM Instrument Sounds (selection)
|
||||||
|
`piano`, `gm_electric_guitar_muted`, `gm_acoustic_bass`, `gm_synth_bass_1`,
|
||||||
|
`gm_synth_bass_2`, `gm_voice_oohs`, `gm_blown_bottle`, `gm_synth_strings_1`,
|
||||||
|
`gm_synth_strings_2`, `gm_xylophone`, `gm_accordion`, `gm_electric_piano_1`
|
||||||
|
|
||||||
|
## 4. Effects
|
||||||
|
|
||||||
|
### Filter
|
||||||
|
```js
|
||||||
|
.lpf(800) // low-pass filter cutoff in Hz
|
||||||
|
.hpf(200) // high-pass filter cutoff in Hz
|
||||||
|
.bpf(1000) // band-pass filter
|
||||||
|
.lpq(5) // filter resonance (Q) — higher = more resonant
|
||||||
|
.vowel("a e i o") // vowel formant filter
|
||||||
|
```
|
||||||
|
|
||||||
|
### Amplitude
|
||||||
|
```js
|
||||||
|
.gain(0.8) // volume 0-1+
|
||||||
|
.velocity(0.7) // MIDI-style velocity
|
||||||
|
```
|
||||||
|
|
||||||
|
### Envelope (ADSR)
|
||||||
|
```js
|
||||||
|
.attack(0.1) // fade in time (seconds)
|
||||||
|
.decay(0.2) // fade to sustain time
|
||||||
|
.sustain(0.5) // sustain level 0-1
|
||||||
|
.release(0.3) // fade out time after note ends
|
||||||
|
.adsr(".1:.2:.5:.3") // shorthand: attack:decay:sustain:release
|
||||||
|
```
|
||||||
|
|
||||||
|
### Space
|
||||||
|
```js
|
||||||
|
.room(0.5) // reverb amount 0-1+
|
||||||
|
.size(0.9) // reverb room size 0-1
|
||||||
|
.delay(0.5) // delay amount 0-1
|
||||||
|
.delaytime(0.25) // delay time in seconds (or pattern)
|
||||||
|
.delayfeedback(0.5) // delay feedback 0-1
|
||||||
|
.pan(0.5) // stereo position 0=left, 0.5=center, 1=right
|
||||||
|
```
|
||||||
|
|
||||||
|
### Playback
|
||||||
|
```js
|
||||||
|
.speed(2) // playback speed (negative = reverse)
|
||||||
|
.begin(0.25) // start position in sample 0-1
|
||||||
|
.end(0.75) // end position in sample 0-1
|
||||||
|
.cut(1) // cut group — stops previous sound in same group
|
||||||
|
.loop(1) // loop the sample
|
||||||
|
```
|
||||||
|
|
||||||
|
### Distortion & Character
|
||||||
|
```js
|
||||||
|
.distort(0.5) // distortion amount
|
||||||
|
.crush(8) // bit crush (lower = crunchier)
|
||||||
|
.coarse(8) // sample rate reduction
|
||||||
|
.shape(0.5) // waveshaping distortion
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Signals (Continuous Modulation)
|
||||||
|
|
||||||
|
Signals produce smooth continuous values for automating parameters.
|
||||||
|
|
||||||
|
```js
|
||||||
|
sine // smooth wave 0-1
|
||||||
|
saw // ramp up 0-1
|
||||||
|
tri // triangle 0-1
|
||||||
|
square // on/off 0-1
|
||||||
|
cosine // cosine wave 0-1
|
||||||
|
rand // random values
|
||||||
|
perlin // smooth random (Perlin noise)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Signal Modifiers
|
||||||
|
```js
|
||||||
|
sine.range(200, 2000) // remap to range
|
||||||
|
sine.range(200, 2000).slow(4) // slow down the LFO
|
||||||
|
sine.range(200, 2000).fast(2) // speed up the LFO
|
||||||
|
sine.segment(8) // quantize to 8 steps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
```js
|
||||||
|
.lpf(sine.range(300, 3000).slow(4)) // sweeping filter
|
||||||
|
.gain(sine.range(0.2, 0.8)) // pulsing volume
|
||||||
|
.pan(sine.range(0, 1).slow(2)) // auto-pan
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Pattern Manipulation Functions
|
||||||
|
|
||||||
|
### Time
|
||||||
|
```js
|
||||||
|
.fast(2) // speed up pattern 2x
|
||||||
|
.slow(2) // slow down pattern 2x
|
||||||
|
.early(0.25) // shift pattern earlier by 1/4 cycle
|
||||||
|
.late(0.25) // shift pattern later
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structural
|
||||||
|
```js
|
||||||
|
.rev() // reverse the pattern
|
||||||
|
.palindrome() // play forward then backward
|
||||||
|
.ply(2) // repeat each event N times
|
||||||
|
.chop(8) // chop each sample into N pieces
|
||||||
|
.striate(8) // similar to chop but interleaved
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional
|
||||||
|
```js
|
||||||
|
.every(4, x => x.rev()) // apply every 4th cycle
|
||||||
|
.every(3, x => x.fast(2)) // double speed every 3rd cycle
|
||||||
|
.sometimes(x => x.speed(2)) // 50% chance
|
||||||
|
.sometimesBy(0.3, x => x.rev()) // 30% chance
|
||||||
|
.when(x => x % 2 === 0, x => x.rev()) // conditional
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combination
|
||||||
|
```js
|
||||||
|
.jux(rev) // play original left, modified right
|
||||||
|
.off(1/8, x => x.add(7)) // offset copy + modify
|
||||||
|
.add(2) // add to note values
|
||||||
|
.sub(1) // subtract from note values
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stacking Patterns
|
||||||
|
```js
|
||||||
|
// Method 1: $: prefix (PREFERRED for multi-pattern)
|
||||||
|
$: sound("bd sd")
|
||||||
|
$: note("c3 e3 g3")
|
||||||
|
|
||||||
|
// Method 2: stack() function (single expression)
|
||||||
|
stack(
|
||||||
|
sound("bd sd"),
|
||||||
|
note("c3 e3 g3")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Method 3: comma in mini-notation (within one function)
|
||||||
|
sound("bd sd, hh*8")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Scales & Tonal System
|
||||||
|
|
||||||
|
```js
|
||||||
|
n("0 2 4 6").scale("C:minor") // scale degrees
|
||||||
|
n("0 2 4 6").scale("C4:minor") // with octave
|
||||||
|
n("0 2 4 6").scale("<C:minor D:dorian>/4") // changing scales
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Scales
|
||||||
|
`major`, `minor`, `dorian`, `phrygian`, `lydian`, `mixolydian`, `locrian`,
|
||||||
|
`minor:pentatonic`, `major:pentatonic`, `blues`, `chromatic`,
|
||||||
|
`harmonicMinor`, `melodicMinor`, `whole`, `diminished`
|
||||||
|
|
||||||
|
### Voicings (Chord Shorthand)
|
||||||
|
```js
|
||||||
|
voicing("<Cm7 Fm7 G7 Cm7>").sound("piano")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. Sample Loading
|
||||||
|
|
||||||
|
```js
|
||||||
|
samples('github:tidalcycles/dirt-samples') // load from GitHub
|
||||||
|
samples({ mySound: ['url1.wav', 'url2.wav'] }) // custom
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Muting & Soloing
|
||||||
|
|
||||||
|
```js
|
||||||
|
$: sound("bd sd") // playing
|
||||||
|
_$: sound("hh*8") // muted (prefix with _)
|
||||||
|
// .hush() // append to mute one pattern
|
||||||
|
// hush() // global mute
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Variables & JavaScript
|
||||||
|
|
||||||
|
Strudel is JavaScript — you can use variables, functions, arrays:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const drums = s("bd sd [~ bd] sd").bank("RolandTR909")
|
||||||
|
const hats = s("hh*8").bank("RolandTR909").gain(0.3)
|
||||||
|
const bass = note("c2 ~ c2 ~ eb2 ~ g1 ~").sound("sawtooth").lpf(600)
|
||||||
|
|
||||||
|
$: drums
|
||||||
|
$: hats
|
||||||
|
$: bass
|
||||||
|
```
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773821835,
|
||||||
|
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
29
flake.nix
Normal file
29
flake.nix
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
description = "Strudel Local Development Environment";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
nodePackages.pnpm
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [ pkgs.alsa-lib pkgs.libjack2 ]}:$LD_LIBRARY_PATH"
|
||||||
|
echo "--- Strudel Dev Shell ---"
|
||||||
|
echo "Run: cd strudel && pnpm i && pnpm dev"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
68
music_output/01_slow_masterpiece.txt
Normal file
68
music_output/01_slow_masterpiece.txt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Slow Soul Groove - D minor - 74 bpm
|
||||||
|
setcps(74/60/4)
|
||||||
|
|
||||||
|
$: s("[bd ~ ~ ~] [~ ~ bd ~] [~ ~ ~ ~] [bd ~ ~ bd]")
|
||||||
|
.bank("RolandTR808")
|
||||||
|
.gain(0.82)
|
||||||
|
.lpf(180)
|
||||||
|
|
||||||
|
$: s("[~ ~ ~ ~] [sd ~ ~ ~] [~ ~ ~ ~] [sd ~ ~ ~]")
|
||||||
|
.bank("RolandTR808")
|
||||||
|
.gain(0.34)
|
||||||
|
.lpf(3200)
|
||||||
|
|
||||||
|
$: s("[~ hh ~ hh] [~ hh ~ hh] [~ hh hh hh] [~ hh ~ hh]")
|
||||||
|
.bank("RolandTR808")
|
||||||
|
.gain("0.08 0.11 0.08 0.1")
|
||||||
|
.hpf(4200)
|
||||||
|
|
||||||
|
$: s("~ ~ ~ ~ ~ ~ oh ~")
|
||||||
|
.bank("RolandTR808")
|
||||||
|
.gain(0.08)
|
||||||
|
.hpf(6500)
|
||||||
|
|
||||||
|
$: note("<[d3,f3,a3,c4,e4] [bb2,d3,f3,a3,c4] [g2,bb2,d3,f3,a3] [a2,c#3,e3,g3,b3]>")
|
||||||
|
.sound("gm_electric_piano_1")
|
||||||
|
.hpf(220)
|
||||||
|
.lpf(3200)
|
||||||
|
.gain(0.22)
|
||||||
|
.attack(0.01)
|
||||||
|
.release(0.55)
|
||||||
|
.room(0.12)
|
||||||
|
|
||||||
|
$: note("<d2 a1 c2 a1 bb1 f2 d2 f2 g1 d2 f2 g2 a1 e2 g2 a2>")
|
||||||
|
.sound("sine")
|
||||||
|
.lpf(140)
|
||||||
|
.gain(0.48)
|
||||||
|
.attack(0.01)
|
||||||
|
.decay(0.28)
|
||||||
|
.sustain(0.12)
|
||||||
|
.release(0.12)
|
||||||
|
|
||||||
|
$: note("<[a4 c5] [d5 f5] [c5 a4] [f4 a4] [f4 bb4] [d5 f5] [g4 bb4] [d5 f5] [f4 a4] [c5 e5] [d5 f5] [a4 d5] [e4 g4] [c#5 e5] [g4 a4] [c#5 e5]>/2")
|
||||||
|
.sound("piano")
|
||||||
|
.hpf(700)
|
||||||
|
.lpf(4200)
|
||||||
|
.gain(0.1)
|
||||||
|
.attack(0.01)
|
||||||
|
.release(0.2)
|
||||||
|
.pan("0.42 0.58")
|
||||||
|
.room(0.08)
|
||||||
|
|
||||||
|
$: note("<~ a4 c5 d5 ~ f5 e5 d5 c5 ~ a4 c5 d5 e5 d5 c5 a4>/2")
|
||||||
|
.sound("triangle")
|
||||||
|
.hpf(850)
|
||||||
|
.lpf(4600)
|
||||||
|
.gain(0.14)
|
||||||
|
.attack(0.03)
|
||||||
|
.release(0.28)
|
||||||
|
.room(0.1)
|
||||||
|
|
||||||
|
$: note("<d4 ~ f4 ~ a4 ~ c5 ~ bb4 ~ a4 ~ g4 ~ e4 ~>")
|
||||||
|
.sound("gm_voice_oohs")
|
||||||
|
.hpf(1000)
|
||||||
|
.lpf(2600)
|
||||||
|
.gain(0.04)
|
||||||
|
.attack(0.08)
|
||||||
|
.release(0.5)
|
||||||
|
.room(0.18)
|
||||||
Reference in New Issue
Block a user