1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2<html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <meta http-equiv="Content-Style-Type" content="text/css" /> 6 <meta name="generator" content="pandoc" /> 7 <meta name="date" content="2022-12-14" /> 8 <title>Strudel: live coding patterns on the Web</title> 9 <style type="text/css"> 10 code { 11 white-space: pre; 12 } 13 </style> 14 <style type="text/css"> 15 pre > code.sourceCode { 16 white-space: pre; 17 position: relative; 18 } 19 pre > code.sourceCode > span { 20 display: inline-block; 21 line-height: 1.25; 22 } 23 pre > code.sourceCode > span:empty { 24 height: 1.2em; 25 } 26 .sourceCode { 27 overflow: visible; 28 } 29 code.sourceCode > span { 30 color: inherit; 31 text-decoration: inherit; 32 } 33 div.sourceCode { 34 margin: 1em 0; 35 } 36 pre.sourceCode { 37 margin: 0; 38 } 39 @media screen { 40 div.sourceCode { 41 overflow: auto; 42 } 43 } 44 @media print { 45 pre > code.sourceCode { 46 white-space: pre-wrap; 47 } 48 pre > code.sourceCode > span { 49 text-indent: -5em; 50 padding-left: 5em; 51 } 52 } 53 pre.numberSource code { 54 counter-reset: source-line 0; 55 } 56 pre.numberSource code > span { 57 position: relative; 58 left: -4em; 59 counter-increment: source-line; 60 } 61 pre.numberSource code > span > a:first-child::before { 62 content: counter(source-line); 63 position: relative; 64 left: -1em; 65 text-align: right; 66 vertical-align: baseline; 67 border: none; 68 display: inline-block; 69 -webkit-touch-callout: none; 70 -webkit-user-select: none; 71 -khtml-user-select: none; 72 -moz-user-select: none; 73 -ms-user-select: none; 74 user-select: none; 75 padding: 0 4px; 76 width: 4em; 77 color: #aaaaaa; 78 } 79 pre.numberSource { 80 margin-left: 3em; 81 border-left: 1px solid #aaaaaa; 82 padding-left: 4px; 83 } 84 div.sourceCode { 85 } 86 @media screen { 87 pre > code.sourceCode > span > a:first-child::before { 88 text-decoration: underline; 89 } 90 } 91 code span.al { 92 color: #ff0000; 93 font-weight: bold; 94 } /* Alert */ 95 code span.an { 96 color: #60a0b0; 97 font-weight: bold; 98 font-style: italic; 99 } /* Annotation */ 100 code span.at { 101 color: #7d9029; 102 } /* Attribute */ 103 code span.bn { 104 color: #40a070; 105 } /* BaseN */ 106 code span.bu { 107 color: #008000; 108 } /* BuiltIn */ 109 code span.cf { 110 color: #007020; 111 font-weight: bold; 112 } /* ControlFlow */ 113 code span.ch { 114 color: #4070a0; 115 } /* Char */ 116 code span.cn { 117 color: #880000; 118 } /* Constant */ 119 code span.co { 120 color: #60a0b0; 121 font-style: italic; 122 } /* Comment */ 123 code span.cv { 124 color: #60a0b0; 125 font-weight: bold; 126 font-style: italic; 127 } /* CommentVar */ 128 code span.do { 129 color: #ba2121; 130 font-style: italic; 131 } /* Documentation */ 132 code span.dt { 133 color: #902000; 134 } /* DataType */ 135 code span.dv { 136 color: #40a070; 137 } /* DecVal */ 138 code span.er { 139 color: #ff0000; 140 font-weight: bold; 141 } /* Error */ 142 code span.ex { 143 } /* Extension */ 144 code span.fl { 145 color: #40a070; 146 } /* Float */ 147 code span.fu { 148 color: #06287e; 149 } /* Function */ 150 code span.im { 151 color: #008000; 152 font-weight: bold; 153 } /* Import */ 154 code span.in { 155 color: #60a0b0; 156 font-weight: bold; 157 font-style: italic; 158 } /* Information */ 159 code span.kw { 160 color: #007020; 161 font-weight: bold; 162 } /* Keyword */ 163 code span.op { 164 color: #666666; 165 } /* Operator */ 166 code span.ot { 167 color: #007020; 168 } /* Other */ 169 code span.pp { 170 color: #bc7a00; 171 } /* Preprocessor */ 172 code span.sc { 173 color: #4070a0; 174 } /* SpecialChar */ 175 code span.ss { 176 color: #bb6688; 177 } /* SpecialString */ 178 code span.st { 179 color: #4070a0; 180 } /* String */ 181 code span.va { 182 color: #19177c; 183 } /* Variable */ 184 code span.vs { 185 color: #4070a0; 186 } /* VerbatimString */ 187 code span.wa { 188 color: #60a0b0; 189 font-weight: bold; 190 font-style: italic; 191 } /* Warning */ 192 </style> 193 <link rel="stylesheet" href="css/iclc.css" /> 194 </head> 195 <body> 196 <div id="header"> 197 <h1 class="title">Strudel: live coding patterns on the Web</h1> 198 <ul id="authorlist"> 199 <li>true</li> 200 <li>true</li> 201 </ul> 202 <h3 class="date">2022-12-14</h3> 203 </div> 204 205 <h2 class="abstract">Abstract</h2> 206 <div id="abstract"> 207 <p> 208 This paper introduces Strudel, which brings the TidalCycles approach to live coding algorithmic patterns to 209 native JavaScript and the web. We begin by giving a little background of the first year of development, before 210 sharing some detail about its implementation and examples of use. We go on to outline the wide range of 211 synthesis and other outputs available in Strudel, including WebAudio, MIDI, OSC (for SuperDirt), WebSerial and 212 CSound, and introduce Strudel’s REPL live editor, including its built-in visualisations. We then compare Strudel 213 with Tidal, the trade-offs involved between JavaScript and Haskell, and the unique capabilities offered by 214 Strudel for aligning patterns. 215 </p> 216 </div> 217 218 <h1 data-number="1" id="introduction"><span class="header-section-number">1</span> Introduction</h1> 219 <p> 220 In the following paper, we introduce <em>Strudel</em>, an alternative implementation of the TidalCycles (or 221 ‘Tidal’ for short) live coding system, using the JavaScript programming language. Strudel is an attempt to make 222 live coding more accessible, by creating a system that runs entirely in the browser, while opening Tidal’s 223 approach to algorithmic patterns 224 <span class="citation" data-cites="mcleanAlgorithmicPattern2020a">(Mclean 2020)</span> up to modern audio/visual 225 web technologies. The Strudel REPL is a live code editor dedicated to manipulating patterns while they play, with 226 builtin visual feedback. While Strudel is written in JavaScript, the API is optimized for simplicity and 227 readability by applying code transformations on the syntax tree level, allowing language operations that would 228 otherwise be impossible. The application supports multiple ways to output sound, including Tone.js, Web Audio 229 Nodes, OSC (Open Sound Control) messages, Web Serial, Web MIDI and Csound. The project is split into multiple 230 packages, allowing granular reuse in other applications. Apart from TidalCycles, Strudel draws inspiration from 231 many prior existing projects like TidalVortex 232 <span class="citation" data-cites="mcleanTidalVortexZero2022">(McLean et al. 2022)</span>, Gibber 233 <span class="citation" data-cites="robertsGibberLiveCoding2012">(Roberts and Kuchera-morin 2012)</span>, Estuary 234 <span class="citation" data-cites="ogbornEstuaryBrowserbasedCollaborative2017">(Ogborn et al. 2017)</span>, Hydra 235 <span class="citation" data-cites="jackHydra2022">(Jack [2022] 2022)</span>, Ocarina 236 <span class="citation" data-cites="solomonPurescriptocarina2022">(Solomon [2021] 2022)</span> and Feedforward 237 <span class="citation" data-cites="mcleanFeedforward2020">(McLean 2020)</span>. This paper expands the Strudel 238 Demo paper for the Web Audio Conference 2022 239 <span class="citation" data-cites="StrudelWAC2022">(Roos and McLean 2022)</span>. 240 </p> 241 <p> 242 The first tentative commit to the Strudel project was on 22nd January 2022 by Alex McLean, with the core 243 representation implemented over the following few days. Although this was his first attempt at a JavaScript-based 244 application, by 27th January, Alex had managed to upload the initial version to the ‘npm’ javascript package 245 database, sharing with the wider community for comment. By 4th February, Felix Roos had discovered Strudel and 246 contributed a ‘REPL’ user interface to it, and then contributed a scheduler the next day, so that Strudel could 247 already make sound. At this point, Alex and Felix shared ownership to the repository, and the project has since 248 proved to be a productive confluence of Felix’s own work into music representation and visualisation, with Alex’s 249 experience with making Tidal. Felix has since become the primary contributor to Strudel, with Alex continuing to 250 jump between developing both Strudel and Tidal. Aspects of Strudel’s development have therefore fed back into 251 TidalCycles, and both systems have maintained a shared conceptual underpinning. We plan to continue working 252 towards feature parity between these systems, although within the syntactical trade-offs and library ecosystems of 253 JavaScript and Haskell, some divergence is inevitable and healthy. 254 </p> 255 <p> 256 Over the first year of its life, Strudel is now a fully-fledged live coding environment, porting Tidal’s core 257 represention of patterns, pattern transformations, and mininotation for polymetric sequences, combined with a 258 wealth of features for synthesising and visualising those patterns. 259 </p> 260 <h1 data-number="2" id="from-tidal-to-strudel-and-back"> 261 <span class="header-section-number">2</span> From Tidal to Strudel and back 262 </h1> 263 <p> 264 As mentioned above, the original Tidal is implemented as a domain specific language (DSL) embedded in the Haskell 265 pure functional programming language, and takes advantage of Haskell’s terse syntax and advanced, ‘strong’ type 266 system. JavaScript on the other hand, is a multi-paradigm programming language, with a dynamic type system. 267 Because Tidal leans heavily on many of Haskell’s more unique features, it was not always clear that it could 268 meaningfully be ported to a multi-paradigm scripting language. However, this possibility was already demonstrated 269 with an earlier port to Python [TidalVortex; 270 <span class="citation" data-cites="mcleanTidalVortexZero2022">McLean et al. (2022)</span>], and we have now 271 successfully implemented Tidal’s pure functional representation of patterns in Strudel, including partial 272 application, currying, and the functor, applicative and monadic structures that underlie Tidal’s expressive 273 pattern transformations. The result is a terse and highly composable system, where everything is either a pattern, 274 or a function for combining and manipulating patterns, offering a rich creative ground for exploration. 275 </p> 276 <p> 277 This development process has been far from a one-way port, however. The process of porting Tidal’s concepts has 278 also opened up new possibilities, some just from revisiting every design decision, and some from the particular 279 affordances and constraints offered by JavaScript. This has lead to new features (and indeed bugfixes) that have 280 found their way back to Tidal where appropriate, and ongoing work that we will return to in the conclusion of this 281 paper. 282 </p> 283 <h1 data-number="3" id="representing-patterns"> 284 <span class="header-section-number">3</span> Representing Patterns 285 </h1> 286 <p> 287 Patterns are the essence of Tidal. Its patterns are abstract entities that represent flows of time as functions, 288 adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can 289 output a set of events that happen within that time span. It depends on the structure of the Pattern how the 290 events are located in time. From now on, this process of generating events from a time span will be called 291 <strong>querying</strong>. Example: 292 </p> 293 <div class="sourceCode" id="cb1"> 294 <pre 295 class="sourceCode js" 296 ><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> pattern <span class="op">=</span> <span class="fu">sequence</span>(c3<span class="op">,</span> [e3<span class="op">,</span> g3])</span> 297<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> events <span class="op">=</span> pattern<span class="op">.</span><span class="fu">queryArc</span>(<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span>)</span> 298<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(events<span class="op">.</span><span class="fu">map</span>(e <span class="kw">=></span> e<span class="op">.</span><span class="fu">show</span>()))</span></code></pre> 299 </div> 300 <p> 301 In this example, we create a pattern using the <code>sequence</code> function and <strong>query</strong> it for 302 the time span from <code>0</code> to <code>1</code>. Those numbers represent units of time called 303 <strong>cycles</strong>. The length of one cycle depends on the tempo, which defaults to one cycle per second. The 304 resulting events are: 305 </p> 306 <div class="sourceCode" id="cb2"> 307 <pre 308 class="sourceCode js" 309 ><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>[{ <span class="dt">value</span><span class="op">:</span> <span class="st">'c3'</span><span class="op">,</span> <span class="dt">begin</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> <span class="dt">end</span><span class="op">:</span> <span class="dv">1</span><span class="op">/</span><span class="dv">2</span> }<span class="op">,</span></span> 310<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>{ <span class="dt">value</span><span class="op">:</span> <span class="st">'e3'</span><span class="op">,</span> <span class="dt">begin</span><span class="op">:</span> <span class="dv">1</span><span class="op">/</span><span class="dv">2</span><span class="op">,</span> <span class="dt">end</span><span class="op">:</span> <span class="dv">3</span><span class="op">/</span><span class="dv">4</span> }<span class="op">,</span></span> 311<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>{ <span class="dt">value</span><span class="op">:</span> <span class="st">'g3'</span><span class="op">,</span> <span class="dt">begin</span><span class="op">:</span> <span class="dv">3</span><span class="op">/</span><span class="dv">4</span><span class="op">,</span> <span class="dt">end</span><span class="op">:</span> <span class="dv">1</span> }]</span></code></pre> 312 </div> 313 <p> 314 Each event has a value, a begin time and an end time, where time is represented as a fraction. In the above case, 315 the events are placed in sequential order, where c3 takes the first half, and e3 and g3 together take the second 316 half. This temporal placement is the result of the <code>sequence</code> function, which divides its arguments 317 equally over one cycle. If an argument is an array, the same rule applies to that part of the cycle. In the 318 example, e3 and g3 are divided equally over the second half of the whole cycle. 319 </p> 320 <p> 321 The above examples do not represent how Strudel is used in practice. In the live coding editor, the user only has 322 to type in the pattern itself, the querying will be handled by the scheduler. The scheduler will repeatedly query 323 the pattern for events, which are then scheduled as sound synthesis or other event triggers. Also, the above event 324 data structure has been simplified for readability. 325 </p> 326 <figure> 327 <img 328 src="images/strudel-screenshot2.png" 329 style="width: 60%" 330 alt="Screenshot of the Strudel ‘REPL’ live coding editor, including piano-roll visualisation." 331 /> 332 <figcaption aria-hidden="true"> 333 Screenshot of the Strudel ‘REPL’ live coding editor, including piano-roll visualisation. 334 </figcaption> 335 </figure> 336 <h1 data-number="4" id="making-patterns"><span class="header-section-number">4</span> Making Patterns</h1> 337 <p> 338 In practice, the end-user live coder will not deal with constructing patterns directly, but will rather build 339 patterns using Strudel’s extensive combinator library to create, combine and transform patterns. 340 </p> 341 <p> 342 The live coder will rarely use the <code>sequence</code> function as seen above, as sequencing is implicit in many 343 functions. For example in the following, the <code>note</code> function constructs a pattern of notes, sequencing 344 its arguments in the same manner as the previous example. 345 </p> 346 <div class="sourceCode" id="cb3"> 347 <pre 348 class="sourceCode js" 349 ><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">note</span>(c3<span class="op">,</span> [e3<span class="op">,</span> g3])</span></code></pre> 350 </div> 351 <p> 352 Perhaps more often, they will use the mini-notation for even terser notation of rhythmic sequences: [^This last 353 example is also valid Tidal code, albeit the parenthesis is not required in its Haskell syntax in this case. Tidal 354 does not support passing sequences as lists directly to the <code>note</code> function, however.]. 355 </p> 356 <div class="sourceCode" id="cb4"> 357 <pre 358 class="sourceCode js" 359 ><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">note</span>(<span class="st">"c3 [e3 g3]"</span>)</span></code></pre> 360 </div> 361 <p> 362 Such sequences are often treated only a starting point for manipulation, where they then undergo pattern 363 transformations such as repetition, symmetry, interference/combination or randomisation, potentially at multiple 364 timescales. Because Strudel patterns are represented as pure functions of time rather than as data structures, 365 very long and complex generative results can be represented and manipulated without having to store the resulting 366 sequences in memory. 367 </p> 368 <h1 data-number="5" id="pattern-example"><span class="header-section-number">5</span> Pattern Example</h1> 369 <p> 370 The following example showcases how patterns can be utilized to create musical complexity from simple parts, using 371 repetition and interference: 372 </p> 373 <div class="sourceCode" id="cb5"> 374 <pre 375 class="sourceCode js" 376 ><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="st">"<0 2 [4 6](3,4,1) 3>"</span></span> 377<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">off</span>(<span class="dv">1</span><span class="op">/</span><span class="dv">4</span><span class="op">,</span> <span class="fu">add</span>(<span class="dv">2</span>))</span> 378<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">off</span>(<span class="dv">1</span><span class="op">/</span><span class="dv">2</span><span class="op">,</span> <span class="fu">add</span>(<span class="dv">6</span>))</span> 379<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">scale</span>(<span class="st">'D minor'</span>)</span> 380<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">legato</span>(<span class="op">.</span><span class="dv">25</span>)</span> 381<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">note</span>()<span class="op">.</span><span class="fu">s</span>(<span class="st">"sawtooth square"</span>)</span> 382<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">delay</span>(<span class="op">.</span><span class="dv">8</span>)<span class="op">.</span><span class="fu">delaytime</span>(<span class="op">.</span><span class="dv">125</span>)</span></code></pre> 383 </div> 384 <p> 385 The pattern starts with a rhythm of numbers in mini notation, which are later interpreted inside the scale of D 386 minor. The first line could also be expressed without mini notation: 387 </p> 388 <div class="sourceCode" id="cb6"> 389 <pre 390 class="sourceCode js" 391 ><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span>(<span class="dv">0</span><span class="op">,</span> <span class="dv">2</span><span class="op">,</span> [<span class="dv">4</span><span class="op">,</span> <span class="dv">6</span>]<span class="op">.</span><span class="fu">euclid</span>(<span class="dv">3</span><span class="op">,</span> <span class="dv">4</span><span class="op">,</span> <span class="dv">1</span>)<span class="op">,</span> <span class="dv">3</span>)</span></code></pre> 392 </div> 393 <p> 394 These numbers then undergo various pattern transformations. Here is a short description of all the functions used: 395 </p> 396 <ul> 397 <li><code>cat</code>: play elements sequentially, where each lasts one cycle</li> 398 <li><code>brackets</code>: elements inside brackets are divided equally over the time of their parent</li> 399 <li> 400 <code>.euclid(p, s, o)</code>: place p pulses evenly over s steps, with offset o 401 <span class="citation" data-cites="toussaintEuclideanAlgorithmGenerates2005">(Toussaint 2005)</span> 402 </li> 403 <li> 404 <code>.off(n, f)</code>: layers a pattern on top of itself, with the new layer offset by n cycles, and with 405 function f applied 406 </li> 407 <li><code>.legato(n)</code>: multiply the duration of all events in a pattern by a factor of n</li> 408 <li> 409 <code>.echo(t, n, v)</code>: copy each event t times, with n cycles in between each copy, decreasing velocity by 410 v 411 </li> 412 <li><code>.note()</code>: interpretes values as notes</li> 413 <li><code>.s(name)</code>: play back each event with the given sound</li> 414 <li><code>.delay(wet)</code>: add delay</li> 415 <li><code>.delaytime(t)</code>: set delay time</li> 416 </ul> 417 <p>Much of the above will be familiar to Tidal users.</p> 418 <!-- This example shows some of Strudel's unique support for chords and transposition familiar to students of Western music theory. This differs a little from Tidal's approach and thanks to the integration of the javascript library XXX (*TODO* ? or is this all your work Felix?), Strudel's support for tonal transformations such as voice leading is perhaps respects more advanced than Tidal. --> 419 <h1 data-number="6" id="ways-to-make-sound-and-other-events"> 420 <span class="header-section-number">6</span> Ways to make Sound (and other events) 421 </h1> 422 <p>To generate sound, Strudel supports bindings for different outputs:</p> 423 <ul> 424 <li>Tone.js (deprecated)</li> 425 <li>Web Audio API</li> 426 <li>WebDirt, a js recreation of Tidal’s <em>Dirt</em> sample engine (deprecated)</li> 427 <li>OSC via osc-js, compatible with superdirt</li> 428 <li>Csound via the Csound WebAssembly build</li> 429 <li>MIDI via WebMIDI</li> 430 <li>Serial via WebSerial</li> 431 </ul> 432 <p> 433 At first, we used Tone.js as sound output, but it proved to be limited for the use case of Strudel, where each 434 individual event could potentially have a completely different audio graph. While the Web Audio API takes a 435 <em>fire-and-forget</em> approach, creating a lot of Tone.js instruments and effects causes performance issues 436 quickly. For that reason, we chose to search for alternatives. 437 </p> 438 <p> 439 Strudel’s new default output uses the Web Audio API to create a new audio graph for each event. It currently 440 supports basic oscillators, sample playback, various effects and an experimental support for soundfonts. 441 </p> 442 <p> 443 WebDirt <span class="citation" data-cites="ogbornDktr0WebDirt2022">(Ogborn [2016] 2022)</span> was created as part 444 of the Estuary Live Coding System 445 <span class="citation" data-cites="ogbornEstuaryBrowserbasedCollaborative2017">(Ogborn et al. 2017)</span>, and 446 proved to be a solid choice for handling samples in Strudel as well. We are however focused on working more 447 directly with the Web Audio API to be able to integrate new features more tightly. 448 </p> 449 <p> 450 Using the OSC protocol via Strudel’s provided Node.js-based OSC proxy server, it is possible to send network 451 messages to trigger events. This is mainly used to render sound using SuperDirt 452 <span class="citation" data-cites="SuperDirt2022">(<em>SuperDirt</em> [2015] 2022)</span>, which is the 453 well-developed Supercollider-based synthesis framework that Tidal live coders generally use as standard. 454 </p> 455 <p> 456 Recently, the experimental integration of Csound proved to bring a new dimension of sound design capabilities to 457 Strudel. Thanks to the WebAssembly distribution of this classic system 458 <span class="citation" data-cites="CsoundWebAssembly">(Yi, Lazzarini, and Costello 2018)</span>, Csound 459 ‘orchestra’ synthesisers can be embedded in and then patterned with Strudel code. 460 </p> 461 <p> 462 MIDI output can also be used to send MIDI messages to either external instruments or to other programs on the same 463 device. Unlike OSC, Strudel is able to send MIDI directly without requiring additional proxy software, but only 464 from web browsers that support it (at the time of writing, this means Chromium-based browsers). 465 </p> 466 <p> 467 Finally, Strudel supports Serial output, for example to trigger events via microcontrollers. This has already been 468 explored for robot choreography by Kate Sicchio and Alex McLean, via a performance presented at the International 469 Conference on Live Interfaces 2022. 470 </p> 471 <h1 data-number="7" id="the-strudel-repl"><span class="header-section-number">7</span> The Strudel REPL</h1> 472 <p> 473 While Strudel can be used as a library in any JavaScript codebase, its main, reference user interface is the 474 Strudel REPL[^REPL stands for read, evaluate, print/play, loop. It is friendly jargon for an interactive 475 programming interface from computing heritage, usually for a commandline interface but also applied to live coding 476 editors.], which is a browser-based live coding environment. This live code editor is dedicated to manipulating 477 Strudel patterns while they play. The REPL features built-in visual feedback, which highlights which elements in 478 the patterned (mini-notation) sequences are influencing the event that is currently being played. This feedback is 479 designed to support both learning and live use of Strudel. 480 </p> 481 <p> 482 Besides a UI for playback control and meta information, the main part of the REPL interface is the code editor 483 powered by CodeMirror. In it, the user can edit and evaluate pattern code live, using one of the available 484 synthesis outputs to create music and/or sound art. The control flow of the REPL follows 3 basic steps: 485 </p> 486 <ol type="1"> 487 <li> 488 The user writes and updates code. Each update transpiles and evaluates it to create a 489 <code>Pattern</code> instance 490 </li> 491 <li> 492 While the REPL is running, the <code>Scheduler</code> queries the active <code>Pattern</code> by a regular 493 interval, generating <code>Events</code> (also known as <code>Haps</code> in Strudel) for the next time span. 494 </li> 495 <li> 496 For each scheduling tick, all generated <code>Events</code> are triggered by calling their 497 <code>onTrigger</code> method, which is set by the output. 498 </li> 499 </ol> 500 <figure> 501 <img 502 src="https://github.com/tidalcycles/strudel/raw/talk/talk/public/strudelflow.png?raw=true" 503 style="width: 43%" 504 alt="REPL control flow" 505 /> 506 <figcaption aria-hidden="true">REPL control flow</figcaption> 507 </figure> 508 <h2 data-number="7.1" id="user-code"><span class="header-section-number">7.1</span> User Code</h2> 509 <p>To create a <code>Pattern</code> from the user code, two steps are needed:</p> 510 <ol type="1"> 511 <li>Transpile the JS input code to make it functional</li> 512 <li>Evaluate the transpiled code</li> 513 </ol> 514 <h3 data-number="7.1.1" id="transpilation-evaluation"> 515 <span class="header-section-number">7.1.1</span> Transpilation & Evaluation 516 </h3> 517 <p> 518 In the JavaScript world, using transpilation is a common practise to be able to use language features that are not 519 supported by the base language. Tools like <code>babel</code> will transpile code that contains unsupported 520 language features into a version of the code without those features. 521 </p> 522 <p> 523 In the same tradition, Strudel can add a transpilation step to simplify the user code in the context of live 524 coding. For example, the Strudel REPL lets the user create mini notation patterns using just double quoted 525 strings, while single quoted strings remain what they are: 526 </p> 527 <div class="sourceCode" id="cb7"> 528 <pre 529 class="sourceCode js" 530 ><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="st">"c3 [e3 g3]*2"</span></span></code></pre> 531 </div> 532 <p>is transpiled to:</p> 533 <div class="sourceCode" id="cb8"> 534 <pre 535 class="sourceCode js" 536 ><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">mini</span>(<span class="st">"c3 [e3 g3]*2"</span>)<span class="op">.</span><span class="fu">withMiniLocation</span>([<span class="dv">1</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span>]<span class="op">,</span>[<span class="dv">1</span><span class="op">,</span><span class="dv">14</span><span class="op">,</span><span class="dv">14</span>])</span></code></pre> 537 </div> 538 <p> 539 Here, the string is wrapped in <code>mini</code>, which will create a pattern from a mini notation string. 540 Additionally, the <code>withMiniLocation</code> method passes the original source code location of the string to 541 the pattern, which enables highlighting active events. 542 </p> 543 <p> 544 Other convenient features like pseudo variables, operator overloading and top level await are possible with 545 transpilation. 546 </p> 547 <p>After the transpilation, the code is ready to be evaluated into a <code>Pattern</code>.</p> 548 <p> 549 Behind the scenes, the user code string is parsed with <code>acorn</code>, turning it into an Abstract Syntax Tree 550 (AST). The AST allows changing the structure of the code before generating the transpiled version using 551 <code>escodegen</code>. 552 </p> 553 <h3 data-number="7.1.2" id="mini-notation"><span class="header-section-number">7.1.2</span> Mini Notation</h3> 554 <p> 555 While the transpilation allows JavaScript to express Patterns in a less verbose way, it is still preferable to use 556 the Mini Notation as a more compact way to express rhythm. Strudel aims to provide the same Mini Notation features 557 and syntax as used in Tidal. 558 </p> 559 <p> 560 The Mini Notation parser is implemented using <code>peggy</code>, which allows generating performant parsers for 561 Domain Specific Languages (DSLs) using a concise grammar notation. The generated parser turns the Mini Notation 562 string into an AST which is used to call the respective Strudel functions with the given structure. For example, 563 <code>"c3 [e3 g3]*2"</code> will result in the following calls: 564 </p> 565 <div class="sourceCode" id="cb9"> 566 <pre 567 class="sourceCode js" 568 ><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">seq</span>(</span> 569<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">'c3'</span>)<span class="op">.</span><span class="fu">withLocation</span>([<span class="dv">1</span><span class="op">,</span><span class="dv">1</span><span class="op">,</span><span class="dv">1</span>]<span class="op">,</span> [<span class="dv">1</span><span class="op">,</span><span class="dv">4</span><span class="op">,</span><span class="dv">4</span>])<span class="op">,</span></span> 570<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">seq</span>(</span> 571<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">'e3'</span>)<span class="op">.</span><span class="fu">withLocation</span>([<span class="dv">1</span><span class="op">,</span><span class="dv">5</span><span class="op">,</span><span class="dv">5</span>]<span class="op">,</span> [<span class="dv">1</span><span class="op">,</span><span class="dv">8</span><span class="op">,</span><span class="dv">8</span>])<span class="op">,</span></span> 572<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">'g3'</span>)<span class="op">.</span><span class="fu">withLocation</span>([<span class="dv">1</span><span class="op">,</span><span class="dv">8</span><span class="op">,</span><span class="dv">8</span>]<span class="op">,</span> [<span class="dv">1</span><span class="op">,</span><span class="dv">10</span><span class="op">,</span><span class="dv">10</span>])<span class="op">,</span></span> 573<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> )<span class="op">.</span><span class="fu">fast</span>(<span class="dv">2</span>)</span> 574<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>)</span></code></pre> 575 </div> 576 <h3 data-number="7.1.3" id="highlighting-locations"> 577 <span class="header-section-number">7.1.3</span> Highlighting Locations 578 </h3> 579 <p> 580 As seen in the examples above, both the JS and the Mini Notation parser add source code locations using 581 <code>withMiniLocation</code> and <code>withLocation</code> methods. While the JS parser adds locations relative 582 to the user code as a whole, the Mini Notation adds locations relative to the position of the mini notation 583 string. The absolute location of elements within Mini Notation can be calculated by simply adding both locations 584 together. This absolute location can be used to highlight active events in real time. 585 </p> 586 <h2 data-number="7.2" id="scheduling-events"><span class="header-section-number">7.2</span> Scheduling Events</h2> 587 <p> 588 After an instance of <code>Pattern</code> is obtained from the user code, it is used by the scheduler to get 589 queried for events. Once started, the scheduler runs at a fixed interval to query active pattern for events 590 withing the current interval’s time span. A simplified implementation looks like this: 591 </p> 592 <div class="sourceCode" id="cb10"> 593 <pre 594 class="sourceCode js" 595 ><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> pattern <span class="op">=</span> <span class="fu">seq</span>(<span class="st">'c3'</span><span class="op">,</span> [<span class="st">'e3'</span><span class="op">,</span> <span class="st">'g3'</span>])<span class="op">;</span> <span class="co">// pattern from user</span></span> 596<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> interval <span class="op">=</span> <span class="fl">0.5</span><span class="op">;</span> <span class="co">// query interval in seconds</span></span> 597<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> time <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> <span class="co">// beginning of current time span</span></span> 598<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> minLatency <span class="op">=</span> <span class="op">.</span><span class="dv">1</span><span class="op">;</span> <span class="co">// min time before a hap should trigger</span></span> 599<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="pp">setInterval</span>(() <span class="kw">=></span> {</span> 600<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> haps <span class="op">=</span> pattern<span class="op">.</span><span class="fu">queryArc</span>(time<span class="op">,</span> time <span class="op">+</span> interval)<span class="op">;</span></span> 601<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> time <span class="op">+=</span> interval<span class="op">;</span> <span class="co">// increment time</span></span> 602<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> haps<span class="op">.</span><span class="fu">forEach</span>((hap) <span class="kw">=></span> {</span> 603<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> deadline <span class="op">=</span> hap<span class="op">.</span><span class="at">whole</span><span class="op">.</span><span class="at">begin</span> <span class="op">-</span> time <span class="op">+</span> minLatency<span class="op">;</span></span> 604<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> <span class="fu">onTrigger</span>(hap<span class="op">,</span> deadline<span class="op">,</span> duration)<span class="op">;</span></span> 605<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span> 606<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> interval <span class="op">*</span> <span class="dv">1000</span>)<span class="op">;</span> <span class="co">// query each "interval" seconds</span></span></code></pre> 607 </div> 608 <p> 609 Note that the above code is simplified for illustrative purposes. The actual implementation has to work around 610 imprecise callbacks of <code>setInterval</code>. More about the implementation details can be read in 611 <a href="https://loophole-letters.vercel.app/web-audio-scheduling">this blog post</a>. 612 </p> 613 <p> 614 The fact that <code>Pattern.queryArc</code> is a pure function that maps a time span to a set of events allows us 615 to choose any interval we like without changing the resulting output. It also means that when the pattern is 616 changed from outside, the next scheduling callback will work with the new pattern, keeping its clock running. 617 </p> 618 <p> 619 The latency between the time the pattern is evaluated and the change is heard is between 620 <code>minLatency</code> and <code>interval + minLatency</code>, in our example between 100ms and 600ms. In 621 Strudel, the current query interval is 50ms with a minLatency of 100ms, meaning the latency is between 50ms and 622 150ms. 623 </p> 624 <h2 data-number="7.3" id="output"><span class="header-section-number">7.3</span> Output</h2> 625 <p> 626 The last step is to trigger each event in the chosen output. This is where the given time and value of each event 627 is used to generate audio or any other form of time based output. The default output of the Strudel REPL is the 628 WebAudio output. To understand what an output does, we first have to understand what control parameters are. 629 </p> 630 <h3 data-number="7.3.1" id="control-parameters"> 631 <span class="header-section-number">7.3.1</span> Control Parameters 632 </h3> 633 <p> 634 To be able to manipulate multiple aspects of sound in parallel, so called control parameters are used to shape the 635 value of each event. Example: 636 </p> 637 <div class="sourceCode" id="cb11"> 638 <pre 639 class="sourceCode js" 640 ><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="fu">note</span>(<span class="st">"c3 e3"</span>)<span class="op">.</span><span class="fu">cutoff</span>(<span class="dv">1000</span>)<span class="op">.</span><span class="fu">s</span>(<span class="st">'sawtooth'</span>)</span> 641<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">queryArc</span>(<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span>)<span class="op">.</span><span class="fu">map</span>(hap <span class="kw">=></span> hap<span class="op">.</span><span class="at">value</span>)</span> 642<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co">/* [</span></span> 643<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co"> { note: 'c3', cutoff: 1000, s: 'sawtooth' }</span></span> 644<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co"> { note: 'e3', cutoff: 1000, s: 'sawtooth' }</span></span> 645<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="co">] */</span></span></code></pre> 646 </div> 647 <p> 648 Here, the control parameter functions <code>note</code>, <code>cutoff</code> and <code>s</code> are used, where 649 each controls a different property in the value object. Each control parameter function accepts a primitive value, 650 a list of values to be sequenced into a <code>Pattern</code>, or a <code>Pattern</code>. In the example, 651 <code>note</code> gets a <code>Pattern</code> from a Mini Notation expression (double quoted), while 652 <code>cutoff</code> and <code>s</code> are given a <code>Number</code> and a (single quoted) 653 <code>String</code> respectively. 654 </p> 655 <p> 656 Strudel comes with a large default set of control parameter functions that are based on the ones used by Tidal and 657 SuperDirt, focusing on music and audio terminology. It is however possible to create custom control paramters for 658 any purpose: 659 </p> 660 <div class="sourceCode" id="cb12"> 661 <pre 662 class="sourceCode js" 663 ><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> { x<span class="op">,</span> y } <span class="op">=</span> <span class="fu">createParams</span>(<span class="st">'x'</span><span class="op">,</span> <span class="st">'y'</span>)</span> 664<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="fu">x</span>(sine<span class="op">.</span><span class="fu">range</span>(<span class="dv">0</span><span class="op">,</span> <span class="dv">200</span>))<span class="op">.</span><span class="fu">y</span>(cosine<span class="op">.</span><span class="fu">range</span>(<span class="dv">0</span><span class="op">,</span><span class="dv">200</span>))</span></code></pre> 665 </div> 666 <p> 667 This example creates the custom control parameters <code>x</code> and <code>y</code> which are then used to form a 668 pattern that descibes the coordinates of a circle. 669 </p> 670 <h3 data-number="7.3.2" id="outputs"><span class="header-section-number">7.3.2</span> Outputs</h3> 671 <p> 672 Now that we know how the value of an event is manipulated using control parameters, we can look at how outputs can 673 use that value to generate anything. The scheduler above was calling the <code>onTrigger</code> function which is 674 used to implement the output. A very simple version of the web audio output could look like this: 675 </p> 676 <div class="sourceCode" id="cb13"> 677 <pre 678 class="sourceCode js" 679 ><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">onTrigger</span>(hap<span class="op">,</span> deadline<span class="op">,</span> duration) {</span> 680<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> { note } <span class="op">=</span> hap<span class="op">.</span><span class="at">value</span><span class="op">;</span></span> 681<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> time <span class="op">=</span> <span class="fu">getAudioContext</span>()<span class="op">.</span><span class="at">currentTime</span> <span class="op">+</span> deadline<span class="op">;</span></span> 682<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> o <span class="op">=</span> <span class="fu">getAudioContext</span>()<span class="op">.</span><span class="fu">createOscillator</span>()<span class="op">;</span></span> 683<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> o<span class="op">.</span><span class="at">frequency</span><span class="op">.</span><span class="at">value</span> <span class="op">=</span> <span class="fu">getFreq</span>(note)<span class="op">;</span></span> 684<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> o<span class="op">.</span><span class="fu">start</span>(time)<span class="op">;</span></span> 685<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> o<span class="op">.</span><span class="fu">stop</span>(time <span class="op">+</span> <span class="bu">event</span><span class="op">.</span><span class="at">duration</span>)<span class="op">;</span></span> 686<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> o<span class="op">.</span><span class="fu">connect</span>(<span class="fu">getAudioContext</span>()<span class="op">.</span><span class="at">destination</span>)<span class="op">;</span></span> 687<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>}</span></code></pre> 688 </div> 689 <p> 690 The above example will create an <code>OscillatorNode</code> for each event, where the frequency is controlled by 691 the <code>note</code> param. In essence, this is how the WebAudio API output of Strudel works, only with many more 692 parameters to control synths, samples and effects. 693 </p> 694 <h1 data-number="8" id="pattern-alignment-and-combination"> 695 <span class="header-section-number">8</span> Pattern alignment and combination 696 </h1> 697 <p> 698 One core aspect of Strudel, inherited from Tidal, is the flexible way that patterns can be combined, irrespective 699 of their structure. Its declarative approach means a live coder does not have to think about the details of 700 <em>how</em> this is done, only <em>what</em> is to be done. 701 </p> 702 <p> 703 As a simple example, consider two number patterns <code>"0 [1 2] 3"</code>, and <code>"10 20"</code>. The first 704 has three contiguous steps of equal lengths, with the second step broken down into two substeps, giving four 705 events in total. There are a very large number of ways in which the structure of these two patterns could be 706 combined, but the default method in both Strudel and Tidal is to line up the cycles of the two patterns, and then 707 take events from the first pattern and match them with those in the second pattern. Therefore, the following two 708 lines are equivalent: 709 </p> 710 <div class="sourceCode" id="cb14"> 711 <pre 712 class="sourceCode js" 713 ><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="st">"0 [1 2] 3"</span><span class="op">.</span><span class="fu">add</span>(<span class="st">"10 20"</span>)</span> 714<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="st">"10 [11 22] 23"</span></span></code></pre> 715 </div> 716 <p> 717 Where the events only partially overlap, they are treated as fragments of the event in the first pattern. This is 718 a little difficult to conceptualise, but lets start by comparing the two patterns in the following example: 719 </p> 720 <div class="sourceCode" id="cb15"> 721 <pre 722 class="sourceCode js" 723 ><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="st">"0 1 2"</span><span class="op">.</span><span class="fu">add</span>(<span class="st">"10 20"</span>)</span> 724<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="st">"10 [11 21] 20"</span></span></code></pre> 725 </div> 726 <p> 727 They are similar to the previous example in that the number <code>1</code> is split in two, with its two halves 728 added to <code>10</code> and <code>20</code> respectively. However, the <code>11</code> ‘remembers’ that it is a 729 fragment of that original <code>1</code> event, and so is treated as having a duration of a third of a cycle, 730 despite only being active for a sixth of a cycle. Likewise, the <code>21</code> is also a fragment of that 731 original <code>1</code> event, but a fragment of its second half. Because the start of its event is missing, it 732 wouldn’t actually trigger a sound (unless it underwent further pattern transformations/combinations). 733 </p> 734 <p> 735 In practice, the effect of this default, implicit method for combining two patterns is that the second pattern is 736 added <em>in</em> to the first one, and indeed this can be made explicit: 737 </p> 738 <div class="sourceCode" id="cb16"> 739 <pre 740 class="sourceCode js" 741 ><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="st">"0 1 2"</span><span class="op">.</span><span class="at">add</span><span class="op">.</span><span class="fu">in</span>(<span class="st">"10 20"</span>)</span></code></pre> 742 </div> 743 <p>This makes way for other ways to align the pattern, and several are already defined, in particular:</p> 744 <ul> 745 <li> 746 <code>in</code> - as explained above, aligns cycles, and applies values from the pattern on the right 747 <em>in</em> to the pattern on the left. 748 </li> 749 <li> 750 <code>out</code> - as with <code>in</code>, but values are applied <em>out</em> of the pattern on the left 751 (i.e. <em>in</em> to the one on the right). 752 </li> 753 <li> 754 <code>mix</code> - structures from both patterns are combined, so that the new events are not fragments but are 755 created at intersections of events from both sides. 756 </li> 757 <li> 758 <code>squeeze</code> - cycles from the pattern on the right are squeezed into events on the left. So that 759 e.g. <code>"0 1 2".add.squeeze("10 20")</code> is equivalent to <code>"[10 20] [11 21] [12 22]"</code>. 760 </li> 761 <li> 762 <code>squeezeout</code> - as with <code>squeeze</code>, but cycles from the left are squeezed into events on the 763 right. So, <code>"0 1 2".add.squeezeout("10 20")</code> is equivalent to <code>[10 11 12] [20 21 22]</code>. 764 </li> 765 <li> 766 <code>trig</code> is similar to <code>squeezeout</code> in that cycles from the right are aligned with events on 767 the left. However those cycles are not ‘squeezed’, rather they are truncated to fit the event. So 768 <code>"0 1 2 3 4 5 6 7".add.trig("10 [20 30]")</code> would be equivalent to 769 <code>10 11 12 13 20 21 30 31</code>. In effect, events on the right ‘trigger’ cycles on the left. 770 </li> 771 <li> 772 <code>trigzero</code> is similar to <code>trig</code>, but the pattern is ‘triggered’ from its very first cycle, 773 rather than from the current cycle. <code>trig</code> and <code>trigzero</code> therefore only give different 774 results where the leftmost pattern differs from one cycle to the next. 775 </li> 776 </ul> 777 <p> 778 We will save going deeper into the background, design and practicalities of these alignment functions for future 779 publications. However in the next section, we take them as a case study for looking at the different design 780 affordances offered by Haskell to Tidal, and JavaScript to Strudel. 781 </p> 782 <h1 data-number="9" id="comparing-strudel-and-haskell-in-use"> 783 <span class="header-section-number">9</span> Comparing Strudel and Haskell in use 784 </h1> 785 <p> 786 Unlike Haskell, JavaScript lacks the ability to define custom infix operators, or change the meaning of existing 787 ones. So the above Strudel example of <code>"0 1 2".add.out("10 20")</code> is equivalent to the Tidal expression 788 <code>"0 1 2" +| "10 20"</code>, where the vertical bar in the operator <code>+|</code> stands for 789 <code>out</code> (where <code>a |+ b</code> would be equivalent of <code>a.add.in(b)</code>). 790 </p> 791 <p> 792 From this we can already see that Tidal tends towards brevity through mixing infix operators with functions, and 793 Strudel tends towards spelling out operations which are joined together with the <code>.</code> operator. This 794 then is the design trade-off of Tidal’s tersity, versus Strudel’s simplicity. 795 </p> 796 <p>To demonstrate this, consider the following Tidal pattern:</p> 797 <pre 798 class="tidal" 799 ><code>iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4</code></pre> 800 <p>This can be directly translated to the Strudel equivalent:</p> 801 <div class="sourceCode" id="cb18"> 802 <pre 803 class="sourceCode js" 804 ><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="fu">iter</span>(<span class="dv">4</span><span class="op">,</span> <span class="fu">every</span>(<span class="dv">3</span><span class="op">,</span> add<span class="op">.</span><span class="fu">squeeze</span>(<span class="st">"10 20"</span>)<span class="op">,</span> <span class="fu">n</span>(<span class="st">"0 1 3"</span>)<span class="op">.</span><span class="fu">s</span>(<span class="st">"triangle"</span>)<span class="op">.</span><span class="fu">crush</span>(<span class="dv">4</span>)))</span></code></pre> 805 </div> 806 <p>Although for a more canonical Strudel expression, we would reorder it as:</p> 807 <div class="sourceCode" id="cb19"> 808 <pre 809 class="sourceCode js" 810 ><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">n</span>(<span class="st">"0 1 3"</span>)<span class="op">.</span><span class="fu">every</span>(<span class="dv">3</span><span class="op">,</span> add<span class="op">.</span><span class="fu">squeeze</span>(<span class="st">"10 20"</span>))<span class="op">.</span><span class="fu">iter</span>(<span class="dv">4</span>)<span class="op">.</span><span class="fu">s</span>(<span class="st">"triangle"</span>)<span class="op">.</span><span class="fu">crush</span>(<span class="dv">4</span>)</span></code></pre> 811 </div> 812 <p> 813 The Strudel example uses the <code>.</code> method call operator for all operations and combinations, whereas the 814 Tidal example has <code>#</code> for the default method for combining patterns and uses infix operators for other 815 methods. The lack of parenthesis in the Tidal example is partly due to the way that arguments are applied to 816 Haskell’s functions, and partly due to the use of the <code>$</code> operator as an alternative way to establish 817 precedence and control the order of evaluation. 818 </p> 819 <p> 820 Considering the above, we argue that the Haskell syntax is a little cleaner, but that the Strudel syntax is easier 821 to learn. Our informal observation is that while Haskell’s dollar <code>$</code> operator is very useful in making 822 code easier to work with, it is one of the most difficult aspects of Tidal use for beginners to learn. On the 823 other hand, the deeper levels of parenthesis in Strudel code can be difficult to keep track of, especially while 824 coding under pressure of live musical performance. However this difficulty can be largely be mitigated by 825 reordering expressions, and further mitigated by supporting editor features. 826 </p> 827 <p> 828 With Strudel, we have little choice but to embrace the affordances and constraints offered by JavaScript, and 829 while designing a domain-specific language entirely based on method calls is a challenge, through creative 830 adoption of functional programming techniques like partial application, we are so far very happy with the results. 831 Tidal’s functional reactive approach to pattern-making has in general translated well to JavaScript, and 832 opportunities and constraints have overall traded off to create a very approachable and useable live coding 833 environment. 834 </p> 835 <h2 data-number="9.1" id="the-trade-off-of-flexible-typing"> 836 <span class="header-section-number">9.1</span> The trade-off of flexible typing 837 </h2> 838 <p> 839 We have identified one problem with porting Tidal to JavaScript where we have missed Haskell’s strict typing and 840 type inference. In both Tidal and Strudel, time is rational, where any point in time is represented as the ratio 841 of two integers. This allows representation of musical ratios such that are impossible to represent accurately 842 using the more common floating point numbers. However while libraries are available that support rational numbers 843 in JavaScript, the lack of strict typing means that it is easy to implement pattern methods where computationally 844 expensive conversion from floating point to rational numbers are performed late, and therefore often enough to 845 overload the CPUs, due to the large number of iterative calculations required to estimate a ratio for a given 846 floating point number. To mitigate this problem, we might consider moving to TypeScript in the future. 847 </p> 848 <h1 data-number="10" id="future-outlook"><span class="header-section-number">10</span> Future Outlook</h1> 849 <p> 850 The project is still young, with many features on the horizon. As general guiding principles, Strudel aims to be 851 </p> 852 <ol type="1"> 853 <li>accessible</li> 854 <li>consistent with Tidal’s approach to pattern</li> 855 <li>modular and extensible</li> 856 </ol> 857 <p> 858 While Haskell’s type system makes it a great language for the ongoing development of Tidal’s inner representation 859 of pattern, JavaScript’s vibrant ecosystem, flexibility and accessibility makes it a great host for more ad-hoc 860 experiments, including interface design. For the future, it is planned to integrate additional alternative sound 861 engines such as Glicol <span class="citation" data-cites="lanChaosprintGlicol2022">(Lan [2020] 2022)</span> and 862 Faust 863 <span class="citation" data-cites="FaustProgrammingLanguage2022" 864 >(<em>Faust - Programming Language for Audio Applications and Plugins</em> [2016] 2022)</span 865 >. Strudel is already approaching feature parity with Tidal, but there are more Tidal functions to be ported, and 866 work to be done to improve compatibility with Tidal’s mininotation. Tidal version 2.0 is under development, which 867 brings a new representation for sequences to its patterns, which will then be brought to Strudel. Besides sound, 868 other ways to render events are being explored, such as graphical, and choreographic output. We are also looking 869 into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax 870 to escape the constraints of javascript, and developing hardware/e-textile interfaces. In summary, there is a lot 871 of fun ahead. 872 </p> 873 <h1 data-number="11" id="links"><span class="header-section-number">11</span> Links</h1> 874 <p> 875 The Strudel REPL is available at <a href="https://strudel.cc" class="uri">https://strudel.cc</a>, including an 876 interactive tutorial. The repository is at 877 <a href="https://github.com/tidalcycles/strudel" class="uri">https://github.com/tidalcycles/strudel</a>, all the 878 code is open source under the AGPL-3.0 License. 879 </p> 880 <h1 data-number="12" id="acknowledgments"><span class="header-section-number">12</span> Acknowledgments</h1> 881 <p> 882 Thanks to the Strudel and wider Tidal, live coding, WebAudio and free/open source software communities for 883 inspiration and support. Alex McLean’s work on this project is supported by a UKRI Future Leaders Fellowship 884 [grant number MR/V025260/1]. 885 </p> 886 <h1 class="unnumbered" id="references">References</h1> 887 <div id="refs" class="references csl-bib-body hanging-indent" role="doc-bibliography"> 888 <div id="ref-FaustProgrammingLanguage2022" class="csl-entry" role="doc-biblioentry"> 889 <em>Faust - Programming Language for Audio Applications and Plugins</em>. (2016) 2022. C++. GRAME. 890 <a href="https://github.com/grame-cncm/faust">https://github.com/grame-cncm/faust</a>. 891 </div> 892 <div id="ref-jackHydra2022" class="csl-entry" role="doc-biblioentry"> 893 Jack, Olivia. (2022) 2022. <em>Hydra</em>. 894 <a href="https://github.com/ojack/hydra">https://github.com/ojack/hydra</a>. 895 </div> 896 <div id="ref-lanChaosprintGlicol2022" class="csl-entry" role="doc-biblioentry"> 897 Lan, Qichao. (2020) 2022. <em>Chaosprint/Glicol</em>. Rust. 898 <a href="https://github.com/chaosprint/glicol">https://github.com/chaosprint/glicol</a>. 899 </div> 900 <div id="ref-mcleanAlgorithmicPattern2020a" class="csl-entry" role="doc-biblioentry"> 901 Mclean, Alex. 2020. <span>“Algorithmic Pattern.”</span> In 902 <em>Proceedings of the International Conference on New Interfaces for Musical Expression</em>, 265--270. 903 Birmingham, UK. <a href="https://zenodo.org/record/4813352">https://zenodo.org/record/4813352</a>. 904 </div> 905 <div id="ref-mcleanFeedforward2020" class="csl-entry" role="doc-biblioentry"> 906 McLean, Alex. 2020. <span>“Feedforward.”</span> In 907 <em>Proceedings of New Interfaces for Musical Expression</em>. Birmingham. 908 <a href="https://zenodo.org/record/6353969">https://zenodo.org/record/6353969</a>. 909 </div> 910 <div id="ref-mcleanTidalVortexZero2022" class="csl-entry" role="doc-biblioentry"> 911 McLean, Alex, Raphaël Forment, Sylvain Le Beux, and Damián Silvani. 2022. <span>“TidalVortex Zero.”</span> In 912 <em>Proceedings of the 7th International Conference on Live Coding</em>. Limerick, Ireland: Zenodo. 913 <a href="https://doi.org/10.5281/zenodo.6456380">https://doi.org/10.5281/zenodo.6456380</a>. 914 </div> 915 <div id="ref-ogbornDktr0WebDirt2022" class="csl-entry" role="doc-biblioentry"> 916 Ogborn, David. (2016) 2022. <em>Dktr0/WebDirt</em>. JavaScript. 917 <a href="https://github.com/dktr0/WebDirt">https://github.com/dktr0/WebDirt</a>. 918 </div> 919 <div id="ref-ogbornEstuaryBrowserbasedCollaborative2017" class="csl-entry" role="doc-biblioentry"> 920 Ogborn, David, Jamie Beverley, Luis Navarro del Angel, Eldad Tsabary, and Alex McLean. 2017. 921 <span>“Estuary: Browser-Based Collaborative Projectional Live Coding of Musical Patterns.”</span> In 922 <em>Proceedings of the International Conference on Live Coding</em>, 11. Morelia. 923 </div> 924 <div id="ref-robertsGibberLiveCoding2012" class="csl-entry" role="doc-biblioentry"> 925 Roberts, Charles, and Joann Kuchera-morin. 2012. <span>“Gibber: Live Coding Audio in the Browser.”</span> In 926 <em>In Proceedings of the 2012 International Computer Music Conference</em>. 927 </div> 928 <div id="ref-StrudelWAC2022" class="csl-entry" role="doc-biblioentry"> 929 Roos, Felix, and Alex McLean. 2022. <span>“Strudel: Algorithmic Patterns for the Web.”</span> In. Zenodo. 930 <a href="https://doi.org/10.5281/zenodo.6768844">https://doi.org/10.5281/zenodo.6768844</a>. 931 </div> 932 <div id="ref-solomonPurescriptocarina2022" class="csl-entry" role="doc-biblioentry"> 933 Solomon, Mike. (2021) 2022. <em>Purescript-Ocarina</em>. PureScript. 934 <a href="https://github.com/mikesol/purescript-ocarina">https://github.com/mikesol/purescript-ocarina</a>. 935 </div> 936 <div id="ref-SuperDirt2022" class="csl-entry" role="doc-biblioentry"> 937 <em>SuperDirt</em>. (2015) 2022. SuperCollider. musikinformatik. 938 <a href="https://github.com/musikinformatik/SuperDirt">https://github.com/musikinformatik/SuperDirt</a>. 939 </div> 940 <div id="ref-toussaintEuclideanAlgorithmGenerates2005" class="csl-entry" role="doc-biblioentry"> 941 Toussaint, Godfried. 2005. <span>“The Euclidean Algorithm Generates Traditional Musical Rhythms.”</span> In 942 <em>In Proceedings of BRIDGES: Mathematical Connections in Art, Music and Science</em>, 47–56. 943 <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231" 944 >http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231</a 945 >. 946 </div> 947 <div id="ref-CsoundWebAssembly" class="csl-entry" role="doc-biblioentry"> 948 Yi, Steven, Victor Lazzarini, and Edward Costello. 2018. 949 <span>“WebAssembly AudioWorklet Csound.”</span> In. Berlin, Germany. 950 <a href="https://mural.maynoothuniversity.ie/16018/">https://mural.maynoothuniversity.ie/16018/</a>. 951 </div> 952 </div> 953 </body> 954</html>