first strudel via AI

This commit is contained in:
Priec
2026-03-20 21:16:20 +01:00
commit 01c5230750
9 changed files with 1203 additions and 0 deletions

96
agentic_workflow/SKILL.md Normal file
View 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

View 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.

View 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 |

View 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
View 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
```