TidalCycles-like DSL for Turmeric - User Guide

Version: 1.0
Target: Phase 20+ (Post-algebraic effects)
Status: Implementation Complete

This guide introduces the TidalCycles-inspired pattern library for Turmeric, enabling live-coding of musical patterns with a powerful, composable DSL.


Table of Contents

  1. Quick Start
  2. Core Concepts
  3. Pattern Types
  4. Temporal Combinators
  5. Structural Combinators
  6. Value Transformations
  7. Polyrhythm & Polymeter
  8. Synth Integration
  9. Live Coding
  10. Mini-Notation
  11. Performance Optimization
  12. Examples

1. Quick Start

Installation

The tidal library is included in Turmeric's standard library. Import it to get started:

(import tidal)

Or import individual modules:

(import tidal/pattern tidal/temporal tidal/structural)

Your First Pattern

;; Create a simple cycle pattern
(def my-pattern (cycle 1 2 3 4))

;; Evaluate at different times
(my-pattern 0.0)  ;; => 1
(my-pattern 1.0)  ;; => 2
(my-pattern 2.0)  ;; => 3

Connecting to scsynth

To hear your patterns, connect to a running SuperCollider server (scsynth):

(import scscm tidal/synth)

;; Connect to server
(def session (scscm-connect))

;; Play a pattern
(def player (make-player session "sine" (cycle 220.0 440.0 880.0) 120.0))
(player-start player)

;; Stop playing
(player-stop player)

2. Core Concepts

Time Representation

Tidal uses beats as the primary time unit. One beat is one quarter note at the current tempo.

(def-type Beats float)

;; Convert between beats and seconds
(bpm->beats-per-sec 120.0)  ;; => 2.0 (120 BPM = 2 beats/sec)
(beats->sec 1.0 120.0)       ;; => 0.5 (1 beat at 120 BPM = 0.5 seconds)

Pattern Type

A pattern is a function from time (in beats) to a value:

(def-type Pattern<T> (fn [Beats] :T))

;; A pattern that returns 440 at any time
(def p (const 440))
(p 0.0)  ;; => 440
(p 100.0)  ;; => 440

Pull-Based Evaluation

Patterns are pull-based: they evaluate on demand when you call them with a time value. This makes them easy to compose and reason about.


3. Pattern Types

Constant Patterns

;; Always returns the same value
(def p (const 440.0))

;; Shorthand: P macro
(def p (P 440.0))  ;; Same as (const 440.0)

Cycle Patterns

;; Cycles through values
(def p (cycle 1 2 3 4))

(p 0.0)  ;; => 1
(p 0.5)  ;; => 1
(p 1.0)  ;; => 2
(p 3.0)  ;; => 4
(p 4.0)  ;; => 1 (wraps)

;; Shorthand: P macro with multiple args
(def p (P 1 2 3 4))  ;; Same as (cycle 1 2 3 4)

Phase Pattern

;; Returns position within cycle (0 to 1)
(def p (phase 1.0))

(p 0.0)   ;; => 0.0
(p 0.25)  ;; => 0.25
(p 0.5)   ;; => 0.5
(p 0.75)  ;; => 0.75
(p 1.0)   ;; => 0.0 (wraps)

;; Custom cycle length
(def p (phase 2.0))
(p 0.0)   ;; => 0.0
(p 1.0)   ;; => 0.5
(p 2.0)   ;; => 0.0

Evaluating Patterns

;; Apply pattern at specific time
(pat-at (cycle 1 2 3) 1.0)  ;; => 2

;; Map pattern over a range of times
(map (fn [t] ((cycle 1 2 3) t)) [0.0 1.0 2.0])  ;; => [1 2 3]

4. Temporal Combinators

Time Scaling

;; Slow down by factor (pattern takes longer to complete)
(def slow-melody (slow 2 (cycle 1 2 3)))
;; At time 0: 1, time 2: 2, time 4: 3

;; Speed up by factor (pattern completes faster)
(def fast-melody (fast 2 (cycle 1 2 3)))
;; At time 0: 1, time 0.5: 2, time 1: 3

;; Stretch and compress are aliases
(stretch 2 p)  ;; Same as (slow 2 p)
(compress 2 p) ;; Same as (fast 2 p)

Time Shifting

;; Shift pattern forward in time
(def shifted (shift 0.5 (cycle 1 2 3)))
;; At time 0: evaluates at 0.5 => 2
;; At time 0.5: evaluates at 1.0 => 3

;; rot is an alias for shift
(rot 0.25 p)

Repetition

;; Repeat pattern n times within its cycle
(def repeated (repeat 3 (cycle 1 2)))
;; Cycle: [1, 1, 1, 2, 2, 2]
;; At time 0-0.33: 1, 0.33-0.66: 1, 0.66-1: 1, etc.

;; Play once, then silence
(def one-shot (once (const 1) 0))
;; At time 0-1: 1, time >1: 0

Time Mirroring

;; Mirror pattern within its cycle
(def mirrored (mirror (cycle 1 2 3)))
;; In a 1-beat cycle: [0, 0.5) forward, [0.5, 1) backward

;; Reverse time within each cycle
(def reversed (rev (cycle 1 2 3)))

5. Structural Combinators

Sequencing

;; Play patterns in sequence
(def seq-pattern (seq (cycle 1 2) (cycle 3 4)))
;; At time 0-1: [1, 2], time 1-2: [3, 4], time 2-3: [1, 2]

;; Sequence with custom lengths
(def seq-n-pattern (seq-n [(cycle 1 2) 2.0] [(cycle 3 4) 1.0]))
;; First pattern plays for 2 beats, second for 1 beat

Stacking

;; Play patterns simultaneously
(def stack-pattern (stack (cycle 1 2) (cycle 10 20)))
;; At time 0: [1, 10], time 1: [2, 20]

;; Stack with custom combiner
(def sum-pattern (stack-with (fn [v] (reduce + 0 v)) (cycle 1 2) (cycle 3 4)))
;; At time 0: 1+3=4, time 1: 2+4=6

;; Convenience combinators
(sum (cycle 1 2) (cycle 3 4))    ;; Add all values
(multiply (cycle 1 2) (cycle 3 4)) ;; Multiply all values

Conditional

;; Only play when condition is true
(def when-pattern (when (cycle true false) (const 1) 0))
;; At time 0: 1, time 1: 0, time 2: 1

;; If-then-else for patterns
(def if-pattern (if-pat (cycle true false) (const 1) (const 2)))

;; Play on every nth beat
(def every-pattern (every 2 (const 1) 0))
;; At time 0: 1, time 1: 0, time 2: 1, time 4: 1

;; Play on specific beats (nth)
(def nth-pattern (nth 2 (const 1) 0))
;; Same as every but uses floor(time) mod n

Euclidean Rhythm

;; Bjorklund's algorithm for evenly distributed hits
(def euclid-pattern (euclid 8 3))
;; 3 hits in 8 steps: 1 0 0 1 0 0 1 0

def euclid-pattern (euclid 8 5 1))
;; 5 hits in 8 steps with offset 1

Pattern Selection

;; Select from patterns based on index
(def select-pattern (select (cycle 0 1 0 1) (const 10) (const 20)))
;; At time 0: 10, time 1: 20, time 2: 10

;; First non-zero value
(def first-pattern (first-nonzero 0 (const 0) (const 0) (const 5)))
;; At any time: 5

6. Value Transformations

Arithmetic

;; Add constant or pattern
(add (cycle 1 2 3) 10)      ;; Add 10 to each value
(add (cycle 1 2) (cycle 3 4))  ;; Add two patterns

;; Multiply
(mul (const 10) 2)          ;; Multiply by 2
(mul (cycle 1 2) (const 2)) ;; Multiply pattern by 2

;; Subtract and divide
(sub (const 10) 3)
(div (const 10) 2)

Range Mapping

;; Map value range [from-low, from-high] to [to-low, to-high]
(def scaled (range (cycle 0.0 0.5 1.0) 0.0 1.0 440.0 880.0))
;; 0.0 -> 440, 0.5 -> 660, 1.0 -> 880

Clamping

;; Clamp values to range
(def clamped (clamp (cycle -5 5 15) 0.0 10.0))
;; -5 -> 0, 5 -> 5, 15 -> 10

Randomness

;; Add random jitter
(def jittered (jitter (const 440.0) 10.0 440.0))
;; Randomly selects from patterns
(def random-select (rand-select (const 1) (const 2) (const 3)))

;; Noise patterns
(def perlin (noise 1.0 0.0))    ;; Perlin noise
(def white (white-noise))       ;; White noise

Waveform Patterns

;; Sine wave (freq, amp, phase)
(def sine-pattern (sine 1.0 1.0 0.0))

;; Square wave (freq, amp, duty cycle)
(def square-pattern (square 1.0 1.0 0.5))

;; Sawtooth wave (freq, amp)
(def saw-pattern (saw 1.0 1.0))

;; Triangle wave (freq, amp)
(def tri-pattern (tri 1.0 1.0))

Envelopes

;; Attack-Release envelope
(def ar-env (ar 0.01 0.1 (const true)))

;; ADSR envelope (attack, decay, sustain, release, gate)
(def adsr-env (adsr 0.01 0.1 0.5 0.1 (const true)))

7. Polyrhythm & Polymeter

Polymeter

;; Play pattern with different time signature
(def polymeter-pattern (polymeter 2.0 (cycle 1 2 3)))
;; Pattern runs at 2x speed: at time 0: 1, time 0.5: 2, time 1: 3

(def half-meter (polymeter 0.5 (cycle 1 2 3)))
;; Pattern runs at 0.5x speed: at time 0: 1, time 2: 2, time 4: 3

Polyrhythm

;; Two patterns with different cycle lengths
(def polyrhythm-pattern (polyrhythm (cycle 1 2) 1.0 (cycle 10 20) 2.0))
;; Returns tuple: (pattern1-value, pattern2-value)

Nested Patterns

;; Pattern of patterns
(def outer (cycle (const 1) (const 2)))
(def nested (nest outer 1.0))
;; At time 0-1: pattern returns 1, so nested returns 1 at any time
;; At time 1-2: pattern returns 2, so nested returns 2 at any time

Stack Polymeter

;; Stack patterns with different meters
(def stack-poly (stack-polymeter [
  [(cycle 1 2) 1.0]
  [(cycle 10 20) 2.0]
  [(cycle 100 200) 0.5]
]))

Canon

;; Delayed copies of the same pattern
(def canon-pattern (canon (cycle 1 2 3) 4 0.25))
;; 4 copies, each delayed by 0.25 beats

Spread

;; Phase-shifted copies
(def spread-pattern (spread (cycle 1 2 3) 4 1.0))
;; 4 copies, spread across 1 beat

Time Signature

;; Pattern that respects time signatures
(def timesig-pattern (timesig (cycle 1 2 3) [(4.0 4) (3.0 4)]))
;; First 4 beats in 4/4, next 3 beats in 3/4

8. Synth Integration

Note Patterns

;; Create note pattern with pitch, velocity, duration, gate
(def note-pattern (note-pattern 
  (cycle 60 62 64)    ;; Pitch (MIDI note)
  (const 100)         ;; Velocity
  (const 0.25)        ;; Duration (in beats)
  (const true)))      ;; Gate (on/off)

;; MIDI note to frequency
(note->freq 69)  ;; => 440.0 (A4)

Chord Patterns

;; Create chord from multiple note patterns
(def chord (chord-pattern 
  (const 48)   ;; C3
  (const 52)   ;; E3
  (const 55)))  ;; G3 (C major)

Arpeggio Patterns

;; Arpeggiate a chord
(def arpeggio (arp-pattern (const [48 52 55]) 4.0))
;; Plays notes [48 52 55] at 4x speed (16th notes)

Drum Patterns

;; Drum pattern with (note, velocity) pairs
(def drum-pattern (drum-pattern [(36 127) (none 0) (38 100) (none 0)]))

;; Convenience helpers
(def kick (kick [0.0 2.0]))      ;; Bass drum on beats 0 and 2
(def snare (snare [1.0 3.0]))    ;; Snare on beats 1 and 3
(def hat (hat [0.0 0.5 1.0 1.5]))  ;; Hi-hat on 8th notes

9. Live Coding

Setting up a Session

(import tidal/live)

;; Create a live session at 120 BPM
(def session (make-live-session 120.0))

;; Create a player
(def player (make-player "sine" (const 440.0) 120.0))

;; Start playing
(player-start player)

;; Stop playing
(player-stop player)

Pattern Replacement

;; Replace pattern in a running player
(player-update player (cycle 220.0 440.0 880.0))

;; Swap multiple patterns
(swap-patterns player new-patterns-map)

d1 and hush Shortcuts

;; Set default pattern (d1 in TidalCycles)
(def d1 (swap-patterns player my-pattern))

;; Silence all (hush in TidalCycles)
(def hush (swap-patterns player (const 0)))

Pattern Hot-Reloading

;; Watch a file for changes and reload
(def watcher (watch-pattern "my-pattern.tur" session "sine" 120.0))

;; Evaluate pattern from string
(def p (eval-pattern "(cycle 1 2 3)"))

Stateful Patterns

;; Pattern with internal state
(def counter (make-stateful-pattern 0 
  (fn [count time] 
    [(inc count) (const count)])))

;; Step the stateful pattern
(let [[new-state new-pat] (step-stateful counter 0.0)]
  ;; new-state: 1
  ;; new-pat: returns 0 at any time
  )

;; Counter with modulo
(def counter-10 (counter-pattern 10))

10. Mini-Notation

Parsing Mini-Notation

;; Parse TidalCycles-style mini-notation
(def p (parse-mini "1 2 3"))    ;; Same as (cycle 1 2 3)
(def p (parse-mini "1*2 2 3"))   ;; Fast first element

def p (parse-mini "[1 2] [3 4]")  ;; Stack patterns

s Macro

;; Shorthand for parse-mini
(def p (s "1 2 3"))

Drum Notation

;; Parse drum mini-notation
(def p (parse-drums "bd sd"))     ;; Bass drum, snare
(def p (parse-drums "bd*2 sd"))   ;; Two bass drums, snare

def p (s "bd(5,8) sd(3,8)")      ;; Euclidean drum patterns

Operators

Operator Description Example
+ Add "1+2" = (add (const 1) (const 2))
- Subtract "1-2" = (sub (const 1) (const 2))
* Multiply "1*2" = (mul (const 1) (const 2))
/ Divide "1/2" = (div (const 1) (const 2))
< Slow "1<2" = (slow 2 (const 1))
> Fast "1>2" = (fast 2 (const 1))
[] Stack "[1 2]" = (stack (const 1) (const 2))
(,) Euclidean "(3,8)" = (euclid 8 3)

11. Performance Optimization

Pattern Caching

;; Cache pattern results
(def cached-pat (cached (cycle 1 2 3) 1024))
;; Caches 1024 values for fast lookup

Pattern Inlining

;; Inline simple patterns for better performance
(def inlined (inline-pattern (slow 2 (cycle 1 2))))

Pattern Fusion

;; Fuse consecutive operations into one function
(def fused (fuse-pattern (slow 2 (fast 2 (cycle 1 2)))))
;; slow 2 * fast 2 = identity, so fuses to (cycle 1 2)

12. Examples

Basic Example

(import tidal)

;; Simple melody
(def melody (cycle 60 62 64 65 67 69 71 72))

;; Add harmony
(def harmony (stack 
  melody
  (add melody (const 12))  ;; Octave up
  (add melody (const 7))))  ;; Fifth

;; Add rhythm
(def rhythm (stack 
  (kick [0.0 2.0])
  (snare [1.0 3.0])
  (hat [0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5])))

;; Combine everything
(def full-pattern (stack melody harmony rhythm))

Polyrhythmic Example

(import tidal/polyrhythm)

;; Kick in 4/4, snare in 3/4, hats in 6/8
(def poly-beat (stack 
  (polymeter 1.0 (kick [0.0 1.0 2.0 3.0]))
  (polymeter 1.333 (snare [0.0 1.0 2.0]))
  (polymeter 0.667 (hat [0.0 0.5 1.0 1.5 2.0 2.5]))))

Generative Example

(import tidal/polyrhythm tidal/transform)

;; Euclidean melody
(def euclid-melody (when (fn [t] (> ((euclid 16 5) t) 0.5)) 
  (cycle 60 64 67 72) 0))

;; With random variation
(def generative (jitter euclid-melody 2.0 60.0))

;; Add harmony
(def full-generative (stack 
  generative
  (add generative (const 12))
  (add generative (const 7))))

API Reference

See tidal-api.md for complete API documentation.

Cookbook

See tidal-cookbook.md for common patterns and recipes.


Support

License

MIT License

Generated for Turmeric project - TidalCycles-like DSL