327 lines
8.3 KiB
Markdown
327 lines
8.3 KiB
Markdown
# AGENTS.md
|
|
|
|
## Strudel Live Coding Assistant Guidelines
|
|
|
|
### Overview
|
|
This document provides essential guidelines for AI agents assisting with Strudel live coding. Strudel is a JavaScript-based port of TidalCycles for algorithmic music composition and live coding performance.
|
|
|
|
### Critical Knowledge Requirements
|
|
|
|
#### 1. Core Syntax Understanding
|
|
- **NOT JavaScript**: Strudel uses its own pattern-based syntax, not standard JavaScript
|
|
- **NO variable declarations**: No `const`, `let`, `var`, `export`, `import`
|
|
- **NO function syntax**: Patterns are composed directly, not stored in functions
|
|
|
|
#### 2. Parallel Pattern Syntax
|
|
```javascript
|
|
// ✅ CORRECT - Multiple patterns in parallel
|
|
$: sound("bd*4")
|
|
$: sound("~ sd ~ sd")
|
|
$: sound("hh*8").gain(0.3)
|
|
|
|
// ❌ WRONG - JavaScript-style
|
|
const kick = sound("bd*4");
|
|
const snare = sound("~ sd ~ sd");
|
|
```
|
|
|
|
#### 3. Muting Patterns
|
|
```javascript
|
|
// ✅ Mute a pattern (for live performance)
|
|
_$: sound("bd*4") // underscore mutes
|
|
|
|
// ✅ Active pattern
|
|
$: sound("bd*4") // dollar sign plays
|
|
```
|
|
|
|
#### 4. Notes and Scales
|
|
```javascript
|
|
// ✅ CORRECT - Using scale degrees with n()
|
|
$: n("0 2 4 6").scale("C:minor").sound("piano")
|
|
|
|
// ❌ WRONG - Manual note names (this works but is not idiomatic)
|
|
$: note("c eb g bb").sound("piano")
|
|
|
|
// ✅ Scale variations
|
|
$: n("0 2 4 <[6,8] [7,9]>").scale("C:minor").sound("piano")
|
|
```
|
|
|
|
#### 5. Common Scale Names
|
|
- `C:major`, `C:minor`
|
|
- `A2:minor` (with octave)
|
|
- `D:dorian`, `G:mixolydian`
|
|
- `A2:minor:pentatonic`
|
|
- `F:major:pentatonic`
|
|
|
|
#### 6. Pattern Notation (Mini-notation)
|
|
```javascript
|
|
// Basic patterns
|
|
"bd ~ sd ~" // kick-rest-snare-rest
|
|
"bd*4" // four kicks per cycle
|
|
"bd*<2 4>" // alternating subdivisions
|
|
"<bd sd>" // alternating sounds
|
|
"[bd sd]" // both in one beat
|
|
"bd@3 sd" // bd three times longer
|
|
"bd!3 sd" // replicate bd three times
|
|
"[bd ~ sd ~]/2" // slow down by half
|
|
|
|
// Advanced patterns
|
|
"~ 0 ~ 2 ~ 4 ~ 6" // scale degrees with rests
|
|
"0*4 2*2 4*2 6*4" // complex subdivisions
|
|
"0,2,4,6" // chord (simultaneous)
|
|
```
|
|
|
|
#### 7. Sound Sources
|
|
```javascript
|
|
// Drum samples
|
|
$: sound("bd sd hh oh")
|
|
|
|
// Instrument banks
|
|
$: sound("bd sd").bank("RolandTR909")
|
|
|
|
// Synthesizers
|
|
$: n("0 2 4 6").sound("sawtooth") // waveforms
|
|
$: n("0 2 4 6").sound("piano") // samples
|
|
$: n("0 2 4 6").sound("gm_acoustic_bass") // GM sounds
|
|
```
|
|
|
|
#### 8. Effects and Modulation
|
|
```javascript
|
|
// Audio effects
|
|
.gain(0.8) // volume
|
|
.lpf(800) // low-pass filter
|
|
.hpf(2000) // high-pass filter
|
|
.reverb(0.4) // reverb amount
|
|
.room(0.6) // room reverb
|
|
.delay(0.25) // delay time
|
|
.pan(-0.3) // stereo pan (-1 to 1)
|
|
|
|
// Live modulation
|
|
.gain(sine.range(0.5, 1).slow(4)) // oscillating gain
|
|
.lpf(sine.range(200, 2000).slow(8)) // filter sweep
|
|
.pan(sine.range(-0.5, 0.5).slow(6)) // auto-pan
|
|
```
|
|
|
|
#### 9. Time Manipulation
|
|
```javascript
|
|
.slow(2) // half speed (2x longer cycles)
|
|
.fast(2) // double speed (half-length cycles)
|
|
.early(0.125) // timing offset
|
|
.late(0.125) // timing delay
|
|
```
|
|
|
|
#### 10. Probability and Randomness
|
|
```javascript
|
|
.sometimes(rev) // randomly apply reverse
|
|
.often(x => x.fast(2)) // frequently apply effect
|
|
.rarely(x => x.gain(0.5)) // rarely apply effect
|
|
.mask("1 0 1 1") // rhythmic gating
|
|
```
|
|
|
|
### Documentation Sources
|
|
|
|
Before providing Strudel code, agents should:
|
|
|
|
1. **Always check official documentation**: https://strudel.cc/
|
|
2. **Reference workshop tutorials**: https://strudel.cc/workshop/
|
|
3. **Verify syntax against examples** in the Strudel REPL
|
|
|
|
### Common Mistakes to Avoid
|
|
|
|
#### ❌ JavaScript Assumptions
|
|
```javascript
|
|
// WRONG - This is not JavaScript
|
|
const pattern = sound("bd*4");
|
|
export default pattern;
|
|
import { stack } from "strudel";
|
|
```
|
|
|
|
#### ❌ Function Declarations
|
|
```javascript
|
|
// WRONG - No function syntax
|
|
function createBeat() {
|
|
return sound("bd*4");
|
|
}
|
|
```
|
|
|
|
#### ❌ Stack Usage
|
|
```javascript
|
|
// WRONG - stack() doesn't exist in REPL
|
|
stack(
|
|
sound("bd*4"),
|
|
sound("~ sd ~ sd")
|
|
)
|
|
```
|
|
|
|
#### ❌ Variable References
|
|
```javascript
|
|
// WRONG - No variable system
|
|
const kick = sound("bd*4");
|
|
kick.gain(0.8);
|
|
```
|
|
|
|
### ✅ Correct Patterns
|
|
|
|
#### Live Performance Setup
|
|
```javascript
|
|
// Multiple parallel patterns
|
|
$: sound("bd*4").gain(0.8)
|
|
$: sound("~ sd ~ sd").gain(0.7)
|
|
$: sound("hh*8").gain(0.3).hpf(8000)
|
|
$: n("0 ~ 2 ~ 4 ~ 6 ~").scale("C2:minor").sound("sawtooth").lpf(120)
|
|
$: n("~ 0 ~ 2 ~ 4 ~ 6").scale("C4:minor").sound("piano").room(0.6)
|
|
```
|
|
|
|
#### Live Coding Effects
|
|
```javascript
|
|
// Instant filter sweep
|
|
$: sound("bd*4").lpf(sine.range(100, 2000).slow(4))
|
|
|
|
// Rhythmic gating
|
|
$: n("0 2 4 6").scale("C:minor").sound("piano").mask("1 0 1 1")
|
|
|
|
// Probability effects
|
|
$: sound("hh*8").sometimes(rev).often(x => x.gain(0.5))
|
|
```
|
|
|
|
### Agent Response Protocol
|
|
|
|
1. **Read documentation first** when unsure about syntax
|
|
2. **Provide working examples** that can be copy-pasted into REPL
|
|
3. **Use `$:` prefix** for all patterns
|
|
4. **Include muting options** with `_$:` for live performance
|
|
5. **Explain mini-notation** when using complex patterns
|
|
6. **Test understanding** by referring to official examples
|
|
|
|
### Git Commit Guidelines
|
|
|
|
- **Use lowercase commit messages**: All commit messages must be written in lowercase
|
|
|
|
### Development Environment Guidelines
|
|
|
|
- **Never use global npm installs**: Avoid `npm i -g` or `npm install -g` commands
|
|
- **Use NixOS shells/dev environments**: Always prefer Nix-based development environments for reproducible builds
|
|
- **Current NixOS version**: 25.05
|
|
|
|
#### Git Submodules
|
|
|
|
The Strudel codebase is managed as a git submodule in the 'src' directory. To work with the codebase:
|
|
|
|
1. Initialize submodules: `make submodules-init`
|
|
2. Update submodules: `make submodules-update`
|
|
|
|
This ensures the correct version of the Strudel repository is checked out and kept in sync.
|
|
|
|
#### Nix Development Shell
|
|
|
|
Use the following `flake.nix` for Strudel development:
|
|
|
|
```nix
|
|
{
|
|
description = "Strudel development environment";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
};
|
|
|
|
outputs = { self, nixpkgs, flake-utils }:
|
|
flake-utils.lib.eachDefaultSystem (system:
|
|
let
|
|
pkgs = nixpkgs.legacyPackages.${system};
|
|
in
|
|
{
|
|
devShells.default = pkgs.mkShell {
|
|
buildInputs = with pkgs; [
|
|
nodejs_20
|
|
pnpm
|
|
git
|
|
];
|
|
|
|
shellHook = ''
|
|
echo "Strudel development environment loaded"
|
|
echo "Node.js: $(node --version)"
|
|
echo "pnpm: $(pnpm --version)"
|
|
'';
|
|
};
|
|
});
|
|
}
|
|
```
|
|
|
|
#### Usage
|
|
|
|
**Option 1: Direct Nix commands**
|
|
1. **Enter development shell**:
|
|
```bash
|
|
nix develop
|
|
```
|
|
|
|
2. **Install dependencies**:
|
|
```bash
|
|
pnpm install
|
|
```
|
|
|
|
3. **Start development server**:
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
**Option 2: Using Makefile (recommended)**
|
|
The Makefile provides convenient shortcuts for common development tasks. First, ensure submodules are initialized:
|
|
|
|
```bash
|
|
# Initialize and update submodules
|
|
make update
|
|
|
|
# Install dependencies
|
|
make install
|
|
|
|
# Start development server
|
|
make dev
|
|
|
|
# Run tests
|
|
make test
|
|
|
|
# Run linter
|
|
make lint
|
|
|
|
# Format code
|
|
make format
|
|
|
|
# Run all checks
|
|
make check
|
|
|
|
# Enter Nix shell directly
|
|
make shell
|
|
|
|
# Show help
|
|
make help
|
|
```
|
|
|
|
All Makefile commands automatically run within the Nix development environment.
|
|
|
|
### Live Performance Considerations
|
|
|
|
When creating beats for live performance:
|
|
|
|
- **Each `$:` line is independently controllable**
|
|
- **Use `_$:` to mute sections during performance**
|
|
- **Include modulation for evolving sounds**
|
|
- **Provide multiple variations/sections**
|
|
- **Keep patterns readable for live modification**
|
|
|
|
### Example Response Structure
|
|
|
|
```javascript
|
|
// MAIN BEAT
|
|
$: sound("bd*4").gain(0.8) // kick
|
|
$: sound("~ sd ~ sd").gain(0.7) // snare
|
|
$: sound("hh*8").gain(0.3) // hats
|
|
$: n("0 ~ 2 ~ 4 ~ 6 ~").scale("C2:minor").sound("sawtooth") // bass
|
|
|
|
// LIVE VARIATIONS (comment/uncomment as needed)
|
|
// $: sound("bd*4").lpf(sine.range(200, 2000).slow(8)) // filter sweep
|
|
// $: sound("~ sd ~ sd").mask("1 0 1 1") // gated snare
|
|
```
|
|
|
|
This structure allows for immediate use and live manipulation in the Strudel REPL environment.
|