1--- 2bibliography: citations.json 3date: 2022-06-24 4title: "Strudel: Algorithmic Patterns for the Web" 5--- 6 7# Introduction 8 9This paper introduces Strudel (or sometimes 'StrudelCycles'), an 10alternative implementation of the Tidal (or 'TidalCycles') live coding 11system, using the JavaScript programming language. Strudel is an attempt 12to make live coding more accessible, by creating a system that runs 13entirely in the browser, while opening Tidal's approach to algorithmic 14patterns [@mcleanAlgorithmicPattern2020a] up to modern audio/visual web 15technologies. The Strudel REPL is a live code editor dedicated to 16manipulating Strudel patterns while they play, with builtin visual 17feedback. While Strudel is written in JavaScript, the API is optimized 18for simplicity and readability by applying code transformations on the 19syntax tree level, allowing language operations that would otherwise be 20impossible. The application supports multiple ways to output sound, 21including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages, 22Web Serial and Web MIDI. The project is split into multiple packages, 23allowing granular reuse in other applications. Apart from TidalCycles, 24Strudel draws inspiration from many prior existing projects like 25TidalVortex [@mcleanTidalVortexZero2022], Gibber 26[@robertsGibberLiveCoding2012], Estuary 27[@ogbornEstuaryBrowserbasedCollaborative2017], Hydra [@jackHydra2022], 28Ocarina [@solomonPurescriptocarina2022] and Feedforward 29[@mcleanFeedforward2020]. 30 31# Porting from Haskell 32 33The original Tidal is implemented as a domain specific language (DSL), 34embedded in the Haskell pure functional programming language, taking 35advantage of Haskell's terse syntax and advanced, 'strong' type system. 36Javascript on the other hand, is a multi-paradigm programming language, 37with a dynamic type system. Because Tidal leans heavily on many of 38Haskell's more unique features, it was not always clear that it could 39meaningfully be ported to a multi-paradigm scripting language. However, 40this already proved to be the case with an earlier port to Python 41\[TidalVortex; @mcleanTidalVortexZero2022\], and we have now 42successfully implemented Tidal's pure functional representation of 43patterns in Strudel, including partial application, and functor, 44applicative and monad structures. Over the past few months since the 45project started in January 2022, a large part of Tidal's functionality 46has already been ported, including its mini-notation for polymetric 47sequences, and a large part of its library of pattern manipulations. The 48result is a terse and highly composable system, where just about 49everything is a pattern, that may be transformed and combined with other 50patterns in a myriad of ways. 51 52# Representing Patterns 53 54Patterns are the essence of Tidal. Its patterns are abstract entities 55that represent flows of time as functions, adapting a technique called 56pure functional reactive programming. Taking a time span as its input, a 57Pattern can output a set of events that happen within that time span. It 58depends on the structure of the Pattern how the events are located in 59time. From now on, this process of generating events from a time span 60will be called **querying**. Example: 61 62<MiniRepl tune={`const pattern = sequence(c3, [e3, g3]); 63const events = pattern.query(0, 1); 64console.log(events.map(e => e.show()))`} /> 65 66In this example, we create a pattern using the `sequence` function and 67**query** it for the time span from `0` to `1`. Those numbers represent 68units of time called **cycles**. The length of one cycle depends on the 69tempo, which defaults to one cycle per second. The resulting events are: 70 71<MiniRepl tune={`[{ value: 'c3', begin: 0, end: 1/2 }, 72{ value: 'e3', begin: 1/2, end: 3/4 }, 73{ value: 'g3', begin: 3/4, end: 1 }]`} /> 74 75Each event has a value, a begin time and an end time, where time is 76represented as a fraction. In the above case, the events are placed in 77sequential order, where c3 takes the first half, and e3 and g3 together 78take the second half. This temporal placement is the result of the 79`sequence` function, which divides its arguments equally over one cycle. 80If an argument is an array, the same rule applies to that part of the 81cycle. In the example, e3 and g3 are divided equally over the second 82half of the whole cycle. 83 84In the REPL, the user only has to type in the pattern itself, the 85querying will be handled by the scheduler. The scheduler will repeatedly 86query the pattern for events, which then will be used for playback. 87 88{width="43%"} 90 91# Making Patterns 92 93In practice, the end-user live coder will not deal with constructing 94patterns directly, but will rather build patterns using Strudel's 95extensive combinator library to create, combine and transform patterns. 96 97The live coder may use the `sequence` function as already seen above, or 98more often the mini-notation for even terser notation of rhythmic 99sequences. Such sequences are often treated only a starting point for 100manipulation, where they then are undergo pattern transformations such 101as repetition, symmetry, interference/combination or randomisation, 102potentially at multiple timescales. Because Strudel patterns are 103represented as pure functions of time rather than as data structures, 104very long and complex generative results can be represented and 105manipulated without having to store the resulting sequences in memory. 106 107# Pattern Example 108 109The following example showcases how patterns can be utilized to create 110musical complexity from simple parts, using repetition and interference: 111 112<MiniRepl tune={`"<0 2 [4 6](3,4,1) 3*2>".scale('D minor') 113.off(1/4, scaleTranspose(2)) 114.off(1/2, scaleTranspose(6)) 115.legato(.5) 116.echo(4, 1/8, .5) 117.tone((await piano()).chain(out())) 118.pianoroll()`} /> 119 120The pattern starts with a rhythm of numbers in mini notation, which are 121interpreted inside the scale of D minor. Without the scale function, the 122first line can be expressed as: 123 124<MiniRepl tune={`"<d3 f3 [a3 c3](3, 4, 1) g3*2>"`} /> 125 126This line could also be expressed without mini notation: 127 128<MiniRepl tune={`slowcat(d3, f3, [a3, c3].euclid(3, 4, 1), g3.fast(2))`} /> 129 130Here is a short description of all the functions used: 131 132- `slowcat`: play elements sequentially, where each lasts one cycle 133- `brackets`: elements inside brackets are divided equally over the 134 time of their parent 135- `euclid(p, s, o)`: place p pulses evenly over s steps, with offset o 136 [@toussaintEuclideanAlgorithmGenerates2005] 137- `fast(n)`: speed up by n. `g3.fast(2)` will play g3 two times. 138- `off(n, f)`: copy each event, offset it by n cycles and apply 139 function f 140- `legato(n)`: multiply duration of event with n 141- `echo(t, n, v)`: copy each event t times, with n cycles in between 142 each copy, decreasing velocity by v 143- `tone(instrument)`: play back each event with the given Tone.js 144 instrument 145- `pianoroll()`: visualize events as midi notes in a pianoroll 146 147# Ways to make Sound 148 149To generate sound, Strudel supports different outputs: 150 151- Tone.js 152- Web Audio API 153- WebDirt, a js recreation of Tidal's *Dirt* sample engine 154- OSC via osc-js 155- MIDI via WebMIDI 156 157Tone.js proved to be limited for the use case of Strudel, where each 158individual event could potentially have a completely different audio 159graph. While the Web Audio API takes a *fire-and-forget* approach, 160creating a lot of Tone.js instruments and effects causes performance 161issues quickly. For that reason, we chose to search for alternatives. 162 163Strudel's Web Audio API output creates a new audio graph for each event. 164It currently supports basic oscillators, sample playback, envelopes, 165filters and an experimental support for soundfonts. 166 167WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary 168Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and 169proved to be a solid choice for handling samples in Strudel as well. 170 171Using OSC, it is possible to send messages to SuperDirt 172[@SuperDirt2022], which is what Tidal does to generate sound. The 173downside of using OSC is that it requires the user to install 174SuperCollider and its sc3plugins library, which can be difficult. 175 176The MIDI output can be used to send MIDI messages to either external 177instruments or to other programs on the same device. Web MIDI is 178currently only supported on Chromium-based browsers. 179 180# Future Outlook 181 182The project is still young, with many features on the horizon. As 183general guiding principles, Strudel aims to be 184 1851. accessible 1862. consistent with Tidal's approach to pattern 1873. modular and extensible 188 189For the future, it is planned to integrate alternative sound engines 190such as Glicol [@lanChaosprintGlicol2022] and Faust 191[@FaustProgrammingLanguage2022]. To improve compatibility with Tidal, 192more Tidal functions are planned to be ported, as well as full 193compatibility with SuperDirt. Besides sound, other ways to render events 194are being explored, such as graphical, and choreographic output. We are 195also looking into alternative ways of editing patterns, including 196multi-user editing for network music, parsing a novel syntax to escape 197the constraints of javascript, and developing hardware/e-textile 198interfaces. 199 200# Links 201 202The Strudel REPL is available at <https://strudel.cc>, 203including an interactive tutorial. The repository is at 204<https://codeberg.org/uzu/strudel/releases>, all the code is open source 205under the GPL-3.0 License. 206 207# Acknowledgments 208 209Thanks to the Strudel and wider Tidal, live coding, webaudio and 210free/open source software communities for inspiration and support. Alex 211McLean's work on this project is supported by a UKRI Future Leaders 212Fellowship \[grant number MR/V025260/1\]. 213 214# References {#references .unnumbered}