import { Slides, Slide } from './Slides.jsx'; import { MaxiRepl } from './MaxiRepl'; import ImgTile from './ImgTile';

Strudel

Algorithmic Patterns for the Web

"The Analytical Engine weaves algebraic patterns, just as the Jacquard loom weaves flowers and leaves."
โ€œSupposing, for instance, that the fundamental relations of pitched sounds in the science of harmony and of musical composition were susceptible of such expression and adaptations, the [Analytical] Engine might compose elaborate and scientific pieces of music of any degree of complexity or extent." - Ada Lovelace
- How to represent and control music with code? - How to leverage unique features of computing? - How to break loose from old conventions?
## About Me
- ๐Ÿ’ป Studied Computer Science and Media, working as a software developer by day - ๐ŸŽบ Trumpet Player, studied Jazz and Popular Music - ๐ŸŽค Dilettante Music Producer of all kinds of genres - ๐Ÿ’ฟ ocassionally publishes music with Lui Mafuta & Puste - ๐Ÿ”Œ Modular Synth & Electronics Lover, Dilettante DJ .. - ๐Ÿ’Œ Coding Blog: [Loophole Letters](https://loophole-letters.vercel.app/) - ๐Ÿ‘€ Loves visualizations
{/* */} {/* */} {/* */}
## What is Live Coding? "In live coding performance, performers create time-based works by programming them while these same works are being executed."
Roberts & Wakefield, 2016

Screenshot: "Olivia Jack -{' '} Hydra , Live Coding Visuals in the Browser".{' '}

## What is Tidal Cycles?
- A very popular software for live coding music - a powerful pattern language for time-based art - find out more at tidalcycles.org

Screenshot:{' '} "Yaxu & hellocatfood - LIVE code for Noise Quest"

## What is Strudel?
- a port of Tidal Cycles to JavaScript - a browser based editor for zero-install live coding: strudel.tidalcycles.org - many ways to make sound: Tone.js, Webdirt, OSC, MIDI ...
- "Strudel" = internationally known as delicious pastry, but also "swirl" / "whirlpool" in german - (a tidal can cause a whirlpool!)

Screenshot:{' '} "Algorave 10th Birthday March 2022 - froos"

## A Taste of Strudel x.scaleTranspose("<0 2 4 3>/4").transpose(-2) const s = x => x.scale( cat('C3 minor pentatonic','G3 minor pentatonic') .slow(4)) const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out()); const chorus = new Chorus(1,2.5,0.5).start(); stack( // melody "<<10 7> <8 3>>/4".struct("x*3").apply(s) .scaleTranspose("<0 3 2> <1 4 3>") .superimpose(scaleTranspose(2).early(1/8)) .apply(t).tone(polysynth().set({ ...osc('triangle4'), ...adsr(0,.08,0) }) .chain(vol(0.2).connect(delay),chorus,out())) .mask("<~@3 x>/16".early(1/8)), // pad "[1,3]/4".scale('G3 minor pentatonic').apply(t) .tone(polysynth().set({ ...osc('square2'), ...adsr(0.1,.4,0.8) }).chain(vol(0.2),chorus,out())).mask("<~ x>/32"), // xylophone "c3,g3,c4".struct("").fast("<1 <2!3 [4 8]>>") .apply(s) .scaleTranspose("<0 <1 [2 [3 <4 5>]]>>").apply(t) .tone(polysynth().set({ ...osc('sawtooth4'), ...adsr(0,.1,0) }).chain(vol(0.4).connect(delay),out())) .mask("/16".early(1/8)), // bass "c2 [c2 ~]*2".scale('C hirajoshi').apply(t) .tone(synth({ ...osc('sawtooth6'), ...adsr(0,.03,.4,.1) }).chain(vol(0.4),out())), // kick "*2".tone(membrane().chain(vol(0.8),out())), // snare "~ ".tone(noise().chain(vol(0.8),out())), // hihat "c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.02)) .chain(vol(0.5).connect(delay),out())) ).slow(1)`, // `// froos - giant steps, chiptune version stack( // melody seq( "[F#5 D5] [B4 G4] Bb4 [B4 A4]", "[D5 Bb4] [G4 Eb4] F#4 [G4 F4]", "Bb4 [B4 A4] D5 [D#5 C#5]", "F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]", ), // chords seq( "[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]", "[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]", "Eb^7 [Am7 D7] G^7 [C#m7 F#7]", "B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]" ) .struct("~ [x ~]".fast(4*8)) .voicings(['E3', 'G4']), // bass seq( "[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]", "[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]", "[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]", "[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]" ) .struct("x ~".fast(4*8)) ).slow(25)`, // `// froos - piano tune #3829 "0 4 <5 7> 2" .off(1/16,x=>x.add(5).color('salmon')) .echo(4,.125,.7) .scale(cat( 'C hirajoshi', slowcat('F hirajoshi','E pentatonic') )) .slow(4) .tone((await piano()).toDestination()) .pianoroll({ cycles:16, fold:1, vertical:0 })`, // `// froos - if so what else const mix = (key) => vol({ chords: .2, lead: 0.8, bass: .4, snare: .95, kick: .9, hihat: .35, }[key]||0); const delay = new FeedbackDelay(1/6, .3).chain(vol(.7), out()); const delay2 = new FeedbackDelay(1/6, .2).chain(vol(.15), out()); const chorus = new Chorus(1,2.5,0.5).start(); // instruments const instr = (instrument) => ({ organ: polysynth().set({...osc('sawtooth4'), ...adsr(.01,.2,0)}).chain(mix('chords').connect(delay),out()), lead: polysynth().set({...osc('triangle4'),...adsr(0.01,.05,0)}).chain(mix('lead').connect(delay2), out()), bass: polysynth().set({...osc('sawtooth8'),...adsr(.02,.05,.3,.2)}).chain(mix('bass'),lowpass(3000), out()), pad: polysynth().set({...osc('square2'),...adsr(0.1,.4,0.8)}).chain(vol(0.15),chorus,out()), hihat: metal(adsr(0, .02, 0)).chain(mix('hihat'), out()), snare: noise(adsr(0, .15, 0.01)).chain(mix('snare'), lowpass(5000), out()), kick: membrane().chain(mix('kick'), out()) }[instrument]); // harmony const t = transpose("<0 0 1 0>/8"); const sowhat = scaleTranspose("0,3,6,9,11"); // track stack( "[<0 4 [3 [2 1]]>]/4".struct("[x]*3").mask("[~ x ~]").scale('D5 dorian').off(1/6, scaleTranspose(-7)).off(1/3, scaleTranspose(-5)).apply(t).tone(instr('lead')).mask("<~ ~ x x>/8"), "< <[d3 ~] [c3 f3] g3>>".scale('D dorian').apply(sowhat).apply(t).tone(instr('organ')).mask("/8"), "<[d2 [d2 ~]*3]!3 >".apply(t).tone(instr('bass')), "c1*6".tone(instr('hihat')), "~ c3".tone(instr('snare')), "<[c1@5 c1] >".tone(instr('kick')), "[2,4]/4".scale('D dorian').apply(t).tone(instr('pad')).mask("/8") ).fast(6/8)`, // `// froos - strudel schneckno samples({ clubkick: 'clubkick/2.wav', sd: ['808sd/SD0010.WAV','808sd/SD0050.WAV'], hh: 'hh/000_hh3closedhh.wav', clak: 'clak/000_clak1.wav', jvbass: ['jvbass/000_01.wav','jvbass/001_02.wav','jvbass/003_04.wav','jvbass/004_05.wav','jvbass/005_06.wav'] }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/'); stack( s(",[~ ],hh(3,4,2)").n("<0 1 2>").speed(.8), n("<0 1*2 4(3,8) 2>").speed(.6).off(1/8,x=>x.speed(1.2).degradeBy(.5)).s("jvbass") ).reset(">").cutoff(perlin.slow(7).range(100,20000)).webdirt().slow(3/2)`, `stack( // drum pattern by yaxu "24*2 [~ 25]".chunk(4, x => x.sub("12").fast(2)) .tone(membrane().toDestination()), "~ x ~ x".every(2, x => x.fast(2)).iter(4) .tone(noise().toDestination()), "c4*8".add(saw.mul(12).slow(1.5)).degrade(0.05) .tone(metal().set(adsr(0,.06,0,0)).chain(vol(0.5),out())) )`, ]} /> ## How it started ![strudel port quote](https://loophole-letters.vercel.app/img/strudel-port.png)
- Started by Alex McLean, based on a port of a port of a rewrite of Tidal - I implemented the REPL - Help from Tidal Community Members - Started with Tone.js, then added Web MIDI, OSC and Web Audio API outputs - Inspired by: Tidal Vortex, Gibber, Estuary, Hydra, WAGS and Feedforward.
## REPL = Read Eval Play Loop
1. Read: User code is parsed, syntax sugar is dissolved 2. Eval: Transformed code is evaluated, creating a **Pattern** 3. Play: The **Pattern** is queried for events, which trigger sounds and effects 4. Loop: The new output is fed back into the ears of the human, informing the next move
## Mini Notation
- Embedded Domain Specific Language - PEG.js grammar based on krill by Marc Resibois - \* \[\] ~ , / <\> ! @ \(\)
\` .legato(.95), // sub melody \`< [~ g4]!2 [~ ab4]!2 [~ a4]!2 [~ bb4]!2 [~ a4]!2 [~ g4]!2 [d4 e4] [f4 gb4] ~!2 [~ g4]!2 [~ ab4]!2 [~ a4]!2 [~ bb4]!2 [~ a4]!2 [~ g4]!2 [d4 e4] [f4 gb4] ~!2 [~ c5]!4 [~ a4]!2 [[c4 ~] [d4 ~]] [[eb4 ~] [e4 ~]] [~ c5]!4 [~ eb5]!2 [g4*2 [f4 ~]] [[e4 ~] [d4 ~]] >\`, // bass \`< c3!7 a3 f3!2 e3!2 ~!4 c3!7 a3 f3!2 e3!2 ~!4 f3!2 e3!2 d3!2 ~!2 f3!2 e3!2 ab3!2 ~!2 >\` .legato(.5) ).fast(2).pianoroll({fold:1,cycles:8,overscan:8})`, ]} />
## Phasing Oscillator Detune in slow motion x.fast(1.01).color('#ff00ff50')) .slow(2) .tone((await piano()).toDestination()) .pianoroll({fold:1,vertical:0,cycles:8})`, `// Steve Reich - Clapping Music s("hh").struct("x!3 ~ x!2 ~ x ~ x!2 ~").speed(.8) .jux(x=>x.speed(1.2) .late("<0 1 2 3 4 5 6 7 8 9 10 11>/8".div(12)) ).slow(2).webdirt().pianoroll()`, ]} /> ## Continuous Signals x.fast(3/2).color('#ff00ff50')) .fast(1) .segment(64) .pianoroll({fold:0,vertical:0,cycles:2}) // strudel disable-highlighting`, ]} />