move iclc paper under docs/

This commit is contained in:
alex 2025-06-14 23:26:59 +01:00
parent a7f044e1aa
commit 2cb22e94b5
27 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,16 @@
all: iclc2023.pdf iclc2023.html
clean:
rm iclc2023.pdf iclc2023.html
iclc2023.html: iclc2023.md citations.json
pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html
iclc2023.pdf: iclc2023.md citations.json pandoc/iclc.latex pandoc/iclc.sty
pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf
iclc2023.docx: iclc2023.md citations.json
pandoc --citeproc --number-sections iclc2023.md -o iclc2023.docx
iclc2023x.pdf: iclc2023.md citations.json pandoc/iclc.latex pandoc/iclc.sty
pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md --pdf-engine=xelatex -o iclc2023x.pdf

View file

@ -0,0 +1,22 @@
# paper
## iclc2023
from the strudel project root:
```sh
npm run iclc
npm run iclc-nocite # try this if you get an error
```
## old
Work in progress on a paper about strudel
To build you will need
- pandoc
- pandoc-url2cite (`npm install -g pandoc-url2cite`)
- latex/xelatex
- python
- pandocfilters (`pip3 install pandocfilters`)

View file

@ -0,0 +1,12 @@
#!/usr/bin/env python3
import sys
from pandocfilters import toJSONFilter, RawBlock
def toMiniREPL(key, value, format, meta):
# print(value, file=sys.stderr)
if key == 'CodeBlock':
return RawBlock("markdown", "<MiniRepl tune={`" + value[1] + "`} />")
if __name__ == "__main__":
toJSONFilter(toMiniREPL)

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,214 @@
---
bibliography: citations.json
date: 2022-06-24
title: "Strudel: Algorithmic Patterns for the Web"
---
# Introduction
This paper introduces Strudel (or sometimes 'StrudelCycles'), an
alternative implementation of the Tidal (or 'TidalCycles') live coding
system, using the JavaScript programming language. Strudel is an attempt
to make live coding more accessible, by creating a system that runs
entirely in the browser, while opening Tidal's approach to algorithmic
patterns [@mcleanAlgorithmicPattern2020a] up to modern audio/visual web
technologies. The Strudel REPL is a live code editor dedicated to
manipulating Strudel patterns while they play, with builtin visual
feedback. While Strudel is written in JavaScript, the API is optimized
for simplicity and readability by applying code transformations on the
syntax tree level, allowing language operations that would otherwise be
impossible. The application supports multiple ways to output sound,
including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages,
Web Serial and Web MIDI. The project is split into multiple packages,
allowing granular reuse in other applications. Apart from TidalCycles,
Strudel draws inspiration from many prior existing projects like
TidalVortex [@mcleanTidalVortexZero2022], Gibber
[@robertsGibberLiveCoding2012], Estuary
[@ogbornEstuaryBrowserbasedCollaborative2017], Hydra [@jackHydra2022],
Ocarina [@solomonPurescriptocarina2022] and Feedforward
[@mcleanFeedforward2020].
# Porting from Haskell
The original Tidal is implemented as a domain specific language (DSL),
embedded in the Haskell pure functional programming language, taking
advantage of Haskell's terse syntax and advanced, 'strong' type system.
Javascript on the other hand, is a multi-paradigm programming language,
with a dynamic type system. Because Tidal leans heavily on many of
Haskell's more unique features, it was not always clear that it could
meaningfully be ported to a multi-paradigm scripting language. However,
this already proved to be the case with an earlier port to Python
\[TidalVortex; @mcleanTidalVortexZero2022\], and we have now
successfully implemented Tidal's pure functional representation of
patterns in Strudel, including partial application, and functor,
applicative and monad structures. Over the past few months since the
project started in January 2022, a large part of Tidal's functionality
has already been ported, including its mini-notation for polymetric
sequences, and a large part of its library of pattern manipulations. The
result is a terse and highly composable system, where just about
everything is a pattern, that may be transformed and combined with other
patterns in a myriad of ways.
# Representing Patterns
Patterns are the essence of Tidal. Its patterns are abstract entities
that represent flows of time as functions, adapting a technique called
pure functional reactive programming. Taking a time span as its input, a
Pattern can output a set of events that happen within that time span. It
depends on the structure of the Pattern how the events are located in
time. From now on, this process of generating events from a time span
will be called **querying**. Example:
<MiniRepl tune={`const pattern = sequence(c3, [e3, g3]);
const events = pattern.query(0, 1);
console.log(events.map(e => e.show()))`} />
In this example, we create a pattern using the `sequence` function and
**query** it for the time span from `0` to `1`. Those numbers represent
units of time called **cycles**. The length of one cycle depends on the
tempo, which defaults to one cycle per second. The resulting events are:
<MiniRepl tune={`[{ value: 'c3', begin: 0, end: 1/2 },
{ value: 'e3', begin: 1/2, end: 3/4 },
{ value: 'g3', begin: 3/4, end: 1 }]`} />
Each event has a value, a begin time and an end time, where time is
represented as a fraction. In the above case, the events are placed in
sequential order, where c3 takes the first half, and e3 and g3 together
take the second half. This temporal placement is the result of the
`sequence` function, which divides its arguments equally over one cycle.
If an argument is an array, the same rule applies to that part of the
cycle. In the example, e3 and g3 are divided equally over the second
half of the whole cycle.
In the REPL, the user only has to type in the pattern itself, the
querying will be handled by the scheduler. The scheduler will repeatedly
query the pattern for events, which then will be used for playback.
![Screenshot of the Strudel editor, including piano-roll
visualisation.](images/strudel-screenshot.png){width="43%"}
# Making Patterns
In practice, the end-user live coder will not deal with constructing
patterns directly, but will rather build patterns using Strudel's
extensive combinator library to create, combine and transform patterns.
The live coder may use the `sequence` function as already seen above, or
more often the mini-notation for even terser notation of rhythmic
sequences. Such sequences are often treated only a starting point for
manipulation, where they then are undergo pattern transformations such
as repetition, symmetry, interference/combination or randomisation,
potentially at multiple timescales. Because Strudel patterns are
represented as pure functions of time rather than as data structures,
very long and complex generative results can be represented and
manipulated without having to store the resulting sequences in memory.
# Pattern Example
The following example showcases how patterns can be utilized to create
musical complexity from simple parts, using repetition and interference:
<MiniRepl tune={`"<0 2 [4 6](3,4,1) 3*2>".scale('D minor')
.off(1/4, scaleTranspose(2))
.off(1/2, scaleTranspose(6))
.legato(.5)
.echo(4, 1/8, .5)
.tone((await piano()).chain(out()))
.pianoroll()`} />
The pattern starts with a rhythm of numbers in mini notation, which are
interpreted inside the scale of D minor. Without the scale function, the
first line can be expressed as:
<MiniRepl tune={`"<d3 f3 [a3 c3](3, 4, 1) g3*2>"`} />
This line could also be expressed without mini notation:
<MiniRepl tune={`slowcat(d3, f3, [a3, c3].euclid(3, 4, 1), g3.fast(2))`} />
Here is a short description of all the functions used:
- `slowcat`: play elements sequentially, where each lasts one cycle
- `brackets`: elements inside brackets are divided equally over the
time of their parent
- `euclid(p, s, o)`: place p pulses evenly over s steps, with offset o
[@toussaintEuclideanAlgorithmGenerates2005]
- `fast(n)`: speed up by n. `g3.fast(2)` will play g3 two times.
- `off(n, f)`: copy each event, offset it by n cycles and apply
function f
- `legato(n)`: multiply duration of event with n
- `echo(t, n, v)`: copy each event t times, with n cycles in between
each copy, decreasing velocity by v
- `tone(instrument)`: play back each event with the given Tone.js
instrument
- `pianoroll()`: visualize events as midi notes in a pianoroll
# Ways to make Sound
To generate sound, Strudel supports different outputs:
- Tone.js
- Web Audio API
- WebDirt, a js recreation of Tidal's *Dirt* sample engine
- OSC via osc-js
- MIDI via WebMIDI
Tone.js proved to be limited for the use case of Strudel, where each
individual event could potentially have a completely different audio
graph. While the Web Audio API takes a *fire-and-forget* approach,
creating a lot of Tone.js instruments and effects causes performance
issues quickly. For that reason, we chose to search for alternatives.
Strudel's Web Audio API output creates a new audio graph for each event.
It currently supports basic oscillators, sample playback, envelopes,
filters and an experimental support for soundfonts.
WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary
Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and
proved to be a solid choice for handling samples in Strudel as well.
Using OSC, it is possible to send messages to SuperDirt
[@SuperDirt2022], which is what Tidal does to generate sound. The
downside of using OSC is that it requires the user to install
SuperCollider and its sc3plugins library, which can be difficult.
The MIDI output can be used to send MIDI messages to either external
instruments or to other programs on the same device. Web MIDI is
currently only supported on Chromium-based browsers.
# Future Outlook
The project is still young, with many features on the horizon. As
general guiding principles, Strudel aims to be
1. accessible
2. consistent with Tidal's approach to pattern
3. modular and extensible
For the future, it is planned to integrate alternative sound engines
such as Glicol [@lanChaosprintGlicol2022] and Faust
[@FaustProgrammingLanguage2022]. To improve compatibility with Tidal,
more Tidal functions are planned to be ported, as well as full
compatibility with SuperDirt. Besides sound, other ways to render events
are being explored, such as graphical, and choreographic output. We are
also looking into alternative ways of editing patterns, including
multi-user editing for network music, parsing a novel syntax to escape
the constraints of javascript, and developing hardware/e-textile
interfaces.
# Links
The Strudel REPL is available at <https://strudel.cc>,
including an interactive tutorial. The repository is at
<https://github.com/tidalcycles/strudel>, all the code is open source
under the GPL-3.0 License.
# Acknowledgments
Thanks to the Strudel and wider Tidal, live coding, webaudio and
free/open source software communities for inspiration and support. Alex
McLean's work on this project is supported by a UKRI Future Leaders
Fellowship \[grant number MR/V025260/1\].
# References {#references .unnumbered}

137
docs/iclc2023-paper/demo.md Normal file
View file

@ -0,0 +1,137 @@
---
title: 'Strudel: Algorithmic Patterns for the Web'
date: '2022-06-24'
bibliography: citations.json
---
# Introduction
This paper introduces Strudel (or sometimes 'StrudelCycles'), an alternative implementation of the Tidal (or 'TidalCycles') live coding system, using the JavaScript programming language. Strudel is an attempt to make live coding more accessible, by creating a system that runs entirely in the browser, while opening Tidal's approach to algorithmic patterns [@mcleanAlgorithmicPattern2020a] up to modern audio/visual web technologies. The Strudel REPL is a live code editor dedicated to manipulating Strudel patterns while they play, with builtin visual feedback. While Strudel is written in JavaScript, the API is optimized for simplicity and readability by applying code transformations on the syntax tree level, allowing language operations that would otherwise be impossible. The application supports multiple ways to output sound, including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages, Web Serial and Web MIDI. The project is split into multiple packages, allowing granular reuse in other applications. Apart from TidalCycles, Strudel draws inspiration from many prior existing projects like TidalVortex [@mcleanTidalVortexZero2022], Gibber [@robertsGibberLiveCoding2012], Estuary [@ogbornEstuaryBrowserbasedCollaborative2017], Hydra [@jackHydra2022], Ocarina [@solomonPurescriptocarina2022] and Feedforward [@mcleanFeedforward2020].
# Porting from Haskell
The original Tidal is implemented as a domain specific language (DSL), embedded in the Haskell pure functional programming language, taking advantage of Haskell's terse syntax and advanced, 'strong' type system. Javascript on the other hand, is a multi-paradigm programming language, with a dynamic type system. Because Tidal leans heavily on many of Haskell's more unique features, it was not always clear that it could meaningfully be ported to a multi-paradigm scripting language. However, this already proved to be the case with an earlier port to Python [TidalVortex; @mcleanTidalVortexZero2022], and we have now successfully implemented Tidal's pure functional representation of patterns in Strudel, including partial application, and functor, applicative and monad structures. Over the past few months since the project started in January 2022, a large part of Tidal's functionality has already been ported, including its mini-notation for polymetric sequences, and a large part of its library of pattern manipulations. The result is a terse and highly composable system, where just about everything is a pattern, that may be transformed and combined with other patterns in a myriad of ways.
# Representing Patterns
Patterns are the essence of Tidal. Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming.
Taking a time span as its input, a Pattern can output a set of events that happen within that time span.
It depends on the structure of the Pattern how the events are located in time.
From now on, this process of generating events from a time span will be called **querying**.
Example:
```js
const pattern = sequence(c3, [e3, g3]);
const events = pattern.query(0, 1);
console.log(events.map(e => e.show()))
```
In this example, we create a pattern using the `sequence` function and **query** it for the time span from `0` to `1`.
Those numbers represent units of time called **cycles**. The length of one cycle depends on the tempo, which defaults to one cycle per second.
The resulting events are:
```js
[{ value: 'c3', begin: 0, end: 1/2 },
{ value: 'e3', begin: 1/2, end: 3/4 },
{ value: 'g3', begin: 3/4, end: 1 }]
```
Each event has a value, a begin time and an end time, where time is represented as a fraction.
In the above case, the events are placed in sequential order, where c3 takes the first half, and e3 and g3 together take the second half.
This temporal placement is the result of the `sequence` function, which divides its arguments equally over one cycle.
If an argument is an array, the same rule applies to that part of the cycle. In the example, e3 and g3 are divided equally over the second half of the whole cycle.
In the REPL, the user only has to type in the pattern itself, the querying will be handled by the scheduler.
The scheduler will repeatedly query the pattern for events, which then will be used for playback.
![Screenshot of the Strudel editor, including piano-roll visualisation.](images/strudel-screenshot.png){ width=43% }
# Making Patterns
In practice, the end-user live coder will not deal with constructing patterns directly, but will rather build patterns using Strudel's extensive combinator library to create, combine and transform patterns.
The live coder may use the `sequence` function as already seen above, or more often the mini-notation for even terser notation of rhythmic sequences. Such sequences are often treated only a starting point for manipulation, where they then are undergo pattern transformations such as repetition, symmetry, interference/combination or randomisation, potentially at multiple timescales. Because Strudel patterns are represented as pure functions of time rather than as data structures, very long and complex generative results can be represented and manipulated without having to store the resulting sequences in memory.
# Pattern Example
The following example showcases how patterns can be utilized to create musical complexity from simple parts, using repetition and interference:
```js
"<0 2 [4 6](3,4,1) 3*2>".scale('D minor')
.off(1/4, scaleTranspose(2))
.off(1/2, scaleTranspose(6))
.legato(.5)
.echo(4, 1/8, .5)
.tone((await piano()).chain(out()))
.pianoroll()
```
The pattern starts with a rhythm of numbers in mini notation, which are interpreted inside the scale of D minor.
Without the scale function, the first line can be expressed as:
```js
"<d3 f3 [a3 c3](3, 4, 1) g3*2>"
```
This line could also be expressed without mini notation:
```js
slowcat(d3, f3, [a3, c3].euclid(3, 4, 1), g3.fast(2))
```
Here is a short description of all the functions used:
- `slowcat`: play elements sequentially, where each lasts one cycle
- `brackets`: elements inside brackets are divided equally over the time of their parent
- `euclid(p, s, o)`: place p pulses evenly over s steps, with offset o [@toussaintEuclideanAlgorithmGenerates2005]
- `fast(n)`: speed up by n. `g3.fast(2)` will play g3 two times.
- `off(n, f)`: copy each event, offset it by n cycles and apply function f
- `legato(n)`: multiply duration of event with n
- `echo(t, n, v)`: copy each event t times, with n cycles in between each copy, decreasing velocity by v
- `tone(instrument)`: play back each event with the given Tone.js instrument
- `pianoroll()`: visualize events as midi notes in a pianoroll
# Ways to make Sound
To generate sound, Strudel supports different outputs:
- Tone.js
- Web Audio API
- WebDirt, a js recreation of Tidal's *Dirt* sample engine
- OSC via osc-js
- MIDI via WebMIDI
Tone.js proved to be limited for the use case of Strudel, where each individual event could potentially have a completely different audio graph.
While the Web Audio API takes a *fire-and-forget* approach, creating a lot of Tone.js instruments and effects causes performance issues quickly. For that reason, we chose to search for alternatives.
Strudel's Web Audio API output creates a new audio graph for each event. It currently supports basic oscillators, sample playback, envelopes, filters and
an experimental support for soundfonts.
WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and proved to be a solid choice for handling samples in Strudel as well.
Using OSC, it is possible to send messages to SuperDirt [@SuperDirt2022], which is what Tidal does to generate sound.
The downside of using OSC is that it requires the user to install SuperCollider and its sc3plugins library, which can be difficult.
The MIDI output can be used to send MIDI messages to either external instruments or to other programs on the same device.
Web MIDI is currently only supported on Chromium-based browsers.
# Future Outlook
The project is still young, with many features on the horizon. As general guiding principles, Strudel aims to be
1. accessible
2. consistent with Tidal's approach to pattern
3. modular and extensible
For the future, it is planned to integrate alternative sound engines such as Glicol [@lanChaosprintGlicol2022] and Faust [@FaustProgrammingLanguage2022]. To improve compatibility with Tidal, more Tidal functions are planned to be ported, as well as full compatibility with SuperDirt. Besides sound, other ways to render events are being explored, such as graphical, and choreographic output. We are also looking into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax to escape the constraints of javascript, and developing hardware/e-textile interfaces.
# Links
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the GPL-3.0 License.
# Acknowledgments
Thanks to the Strudel and wider Tidal, live coding, webaudio and free/open source software communities for inspiration and support. Alex McLean's work on this project is supported by a UKRI Future Leaders Fellowship [grant number MR/V025260/1].
# References

Binary file not shown.

View file

@ -0,0 +1,97 @@
**Submission ID:** 8
**Title:** Strudel: live coding patterns on the Web
**Status:** Accept
**Reviewer 1:**
Author Comments: * Summary of the contribution:\
This paper describes Strudel, a Web-based implementation of Tidal Cycles. It is generally a good overview of the system which could help prospective users understand how it works beyond the manual, and it provides many code examples. The most interesting contribution is the comparison between the Haskell and JavaScript features for this case.
* On theme (1-5):\
1
* Citation quality (1-5):\
5
* Missing references:\
not answered
* Suggested improvements:\
This paper is a good introduction to Strudel. The style is mostly illustrative, but it is hard to understand where the Tidal syntax is being introduced vs where the specific Strudel features are used. Thus, the best way to improve it would be to focus on Strudel. Also, it would be good to give a better hint on what output method would be preferable. Also the title of Section 9 should compare Strudel to Tidal or Haskell to JavaScript.
The paper is driven by examples, but many are a bit difficult to follow, here are some issues:\
Section 4 - the mini-notation example does not look terser despite the text.\
Section 5 - echo funciton is not used.\
Sections 7.11 and 7.12 - the same (mini-notation) example is used so it is not clear what relation there is between transpilation and using mini-notation. Also please reference peggy\
Sectin 7.3.2 The sawtooth waveform has been used in all examples, so it would be clearer if it was used here (maybe cutoff as well).
Generally, beyond the language differences, what does Strudel contribute with respect to Haskell-based Tidal? I would say that there is a lot to be gained in terms of installation, availability and potential for collaboration.
* Clarity (1-5):\
4
* Corrections required:\
No contentious / incorrect claims
* Other Changes if Published:\
not answered
* Correct Template used?\
Yes
* All materials provided?\
Yes
* Wordcount respected?\
Yes
* Have any deviations, errors or omissions impacted your ability to give a fair review?\
No
**Reviewer 2:**
Author Comments: * Summary of the contribution:\
This proposal presents Strudel, an online, JavaScript-native approach to algorithmic pattern composition that in many ways mirrors TidalCycles. TidalCycles, developed by one of the authors, is a key software piece for live coding. Perhaps the most difficult aspects of Tidal are related to Haskell and Haskell's dependency system that makes it very difficult to install. Haskell in itself is a difficult language to grasp; if Tidal users would like to expand and 'personalise' the possibilities of the software, they would have to get familiar with the particularities of Haskell. JavaScript is a multi-paradigm language that is used for web development can make pattern-based, algorithmic composition more popular. The process of bringing Tidal's paradigm to a dynamic type system seems to be an important part of this research. I enjoy and find very interesting the dialectical process that occurs between Strudel and Tidal, where the implementation of Tidal concepts in Strudel simultaneously transforms Tidal, either by introspection (finding bugs, etc.) or by bringing back to Tidal new insights that are clear in JavaScript. I think moving to TypeScript makes sense; perhaps a small section where the decision to use JavaScript is discussed a little further could be helpful for readers (beyond accessibility). Overall, this paper is clear and presents software that is a key development for live coding and will be a great addition to the next ICLC.
* On theme (1-5):\
4
* Citation quality (1-5):\
5
* Missing references:\
not answered
* Suggested improvements:\
Tidal-Strudel parity seems to be on the immediate horizon, but I wonder if this parity is transient and if the ultimate goal of Strudel is to ultimately diverge from Tidal. Perhaps the project is in its early stages, and this is not clear, but it would be interesting to get some insights on the authors' intentions. Personally, I think that a key discussion that needs to happen in live coding communities and development nodes is that of what is understood as multilingual live coding. Tidal's paradigm is implemented in a number of programming languages (as well as platforms such as Estuary), each with its own set of features and challenges: some are functional reactive programming, type strict oriented, while others are multi-paradigm, dynamic type oriented, etc. This seems to suggest that it is possible for many programming languages to adopt one single understanding of how to make music. Perhaps further considerations should be made as to whether we need one single grammar to name and express all music structures or whether we need environments and software that help difference to proliferate in terms of music/sound art/art idiosyncrasies. Perhaps questions in this direction are beyond the scope of this paper and I hope this comment is taken as productive feedback for the authors.
* Clarity (1-5):\
4
* Corrections required:\
Nothing contentious or incorrect to note.
* Other Changes if Published:\
The example shown on page 3 under "Pattern Example" seems to be different from its explanation. The explanations do not mention scale but mention echo, which is not present in the example. I would double check all the code examples inc ase I missed something like this.
* Correct Template used?\
Yes
* All materials provided?\
Yes
* Wordcount respected?\
Yes
* Have any deviations, errors or omissions impacted your ability to give a fair review?\
no
* General remarks:\
This proposal presents Strudel, an online, JavaScript-native approach to algorithmic pattern composition that in many ways mirrors TidalCycles. TidalCycles, developed by one of the authors, is a key software piece for live coding. Perhaps the most difficult aspects of Tidal are related to Haskell and Haskell's dependency system that makes it very difficult to install. Haskell in itself is a difficult language to grasp; if Tidal users would like to expand and 'personalise' the possibilities of the software, they would have to get familiar with the particularities of Haskell. JavaScript is a multi-paradigm language that can make pattern-based, algorithmic composition more popular. The process of bringing Tidal's paradigm to a dynamic type system seems to be an important part of this research. I enjoy and find very interesting the dialectical process that occurs between Strudel and Tidal, where the implementation of Tidal concepts in Strudel simultaneously transforms Tidal, either by introspection (finding bugs, etc.) or by bringing back to Tidal new insights that are clear in JavaScript. I think moving to TypeScript makes sense; perhaps a small section where the decision to use JavaScript is discussed a little further could be helpful for readers (beyond accessibility). I am curious, did the authors consider PureScript? I can see how, in terms of popularity, PureScript is a lateral move coming from Haskell, but an explicit explanation about the (maybe) obvious candidates would be interesting.\
Tidal-Strudel parity seems to be on the immediate horizon, but I wonder if this parity is transient and if the ultimate goal of Strudel is to ultimately diverge from Tidal. Perhaps the project is in its early stages, and this is not clear, but it would be interesting to get some insights on the authors' intentions. Personally, I think that a key discussion that needs to happen in live coding communities and development nodes is that of what is understood as multilingual live coding. Tidal's paradigm is implemented in a number of programming languages (as well as platforms such as Estuary), each with its own set of features and challenges: some are functional reactive programming, type strict oriented, while others are multi-paradigm, dynamic type oriented, etc. This seems to suggest that it is possible for many programming languages to adopt one single understanding of how to make music. Perhaps further considerations should be made as to whether we need one single grammar to name and express all music structures or whether we need environments and software that help difference to proliferate in terms of music/sound art/art idiosyncrasies. Perhaps questions in this direction are beyond the scope of this paper and I hope this last comment is taken as productive feedback for the authors.
Overall, this paper is clear and presents software that is a key development for live coding and will be a great addition to the next ICLC.
PS. The example shown on page 3 under "Pattern Example" seems to be different from its explanation. The explanations do not mention scale but mention echo, which is not present in the example.

View file

@ -0,0 +1,818 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<meta name="date" content="2022-12-14" />
<title>Strudel: live coding patterns on the Web</title>
<style type="text/css">code{white-space: pre;}</style>
<style type="text/css">
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="css/iclc.css" />
</head>
<body>
<div id="header">
<h1 class="title">Strudel: live coding patterns on the Web</h1>
<ul id="authorlist">
<li>true</li>
<li>true</li>
</ul>
<h3 class="date">2022-12-14</h3>
</div>
<h2 class="abstract">Abstract</h2>
<div id="abstract">
<p>This paper introduces Strudel, which brings the TidalCycles approach
to live coding algorithmic patterns to native JavaScript and the web. We
begin by giving a little background of the first year of development,
before sharing some detail about its implementation and examples of use.
We go on to outline the wide range of synthesis and other outputs
available in Strudel, including WebAudio, MIDI, OSC (for SuperDirt),
WebSerial and CSound, and introduce Strudels REPL live editor,
including its built-in visualisations. We then compare Strudel with
Tidal, the trade-offs involved between JavaScript and Haskell, and the
unique capabilities offered by Strudel for aligning patterns.</p>
</div>
<h1 data-number="1" id="introduction"><span
class="header-section-number">1</span> Introduction</h1>
<p>In the following paper, we introduce <em>Strudel</em>, an alternative
implementation of the TidalCycles (or Tidal for short) live coding
system, using the JavaScript programming language. Strudel is an attempt
to make live coding more accessible, by creating a system that runs
entirely in the browser, while opening Tidals approach to algorithmic
patterns <span class="citation"
data-cites="mcleanAlgorithmicPattern2020a">(Mclean 2020)</span> up to
modern audio/visual web technologies. The Strudel REPL is a live code
editor dedicated to manipulating patterns while they play, with builtin
visual feedback. While Strudel is written in JavaScript, the API is
optimized for simplicity and readability by applying code
transformations on the syntax tree level, allowing language operations
that would otherwise be impossible. The application supports multiple
ways to output sound, including Tone.js, Web Audio Nodes, OSC (Open
Sound Control) messages, Web Serial, Web MIDI and Csound. The project is
split into multiple packages, allowing granular reuse in other
applications. Apart from TidalCycles, Strudel draws inspiration from
many prior existing projects like TidalVortex <span class="citation"
data-cites="mcleanTidalVortexZero2022">(McLean et al. 2022)</span>,
Gibber <span class="citation"
data-cites="robertsGibberLiveCoding2012">(Roberts and Kuchera-morin
2012)</span>, Estuary <span class="citation"
data-cites="ogbornEstuaryBrowserbasedCollaborative2017">(Ogborn et al.
2017)</span>, Hydra <span class="citation"
data-cites="jackHydra2022">(Jack [2022] 2022)</span>, Ocarina <span
class="citation" data-cites="solomonPurescriptocarina2022">(Solomon
[2021] 2022)</span> and Feedforward <span class="citation"
data-cites="mcleanFeedforward2020">(McLean 2020)</span>. This paper
expands the Strudel Demo paper for the Web Audio Conference 2022 <span
class="citation" data-cites="StrudelWAC2022">(Roos and McLean
2022)</span>.</p>
<p>The first tentative commit to the Strudel project was on 22nd January
2022 by Alex McLean, with the core representation implemented over the
following few days. Although this was his first attempt at a
JavaScript-based application, by 27th January, Alex had managed to
upload the initial version to the npm javascript package database,
sharing with the wider community for comment. By 4th February, Felix
Roos had discovered Strudel and contributed a REPL user interface to
it, and then contributed a scheduler the next day, so that Strudel could
already make sound. At this point, Alex and Felix shared ownership to
the repository, and the project has since proved to be a productive
confluence of Felixs own work into music representation and
visualisation, with Alexs experience with making Tidal. Felix has since
become the primary contributor to Strudel, with Alex continuing to jump
between developing both Strudel and Tidal. Aspects of Strudels
development have therefore fed back into TidalCycles, and both systems
have maintained a shared conceptual underpinning. We plan to continue
working towards feature parity between these systems, although within
the syntactical trade-offs and library ecosystems of JavaScript and
Haskell, some divergence is inevitable and healthy.</p>
<p>Over the first year of its life, Strudel is now a fully-fledged live
coding environment, porting Tidals core represention of patterns,
pattern transformations, and mininotation for polymetric sequences,
combined with a wealth of features for synthesising and visualising
those patterns.</p>
<h1 data-number="2" id="from-tidal-to-strudel-and-back"><span
class="header-section-number">2</span> From Tidal to Strudel and
back</h1>
<p>As mentioned above, the original Tidal is implemented as a domain
specific language (DSL) embedded in the Haskell pure functional
programming language, and takes advantage of Haskells terse syntax and
advanced, strong type system. JavaScript on the other hand, is a
multi-paradigm programming language, with a dynamic type system. Because
Tidal leans heavily on many of Haskells more unique features, it was
not always clear that it could meaningfully be ported to a
multi-paradigm scripting language. However, this possibility was already
demonstrated with an earlier port to Python [TidalVortex; <span
class="citation" data-cites="mcleanTidalVortexZero2022">McLean et al.
(2022)</span>], and we have now successfully implemented Tidals pure
functional representation of patterns in Strudel, including partial
application, currying, and the functor, applicative and monadic
structures that underlie Tidals expressive pattern transformations. The
result is a terse and highly composable system, where everything is
either a pattern, or a function for combining and manipulating patterns,
offering a rich creative ground for exploration.</p>
<p>This development process has been far from a one-way port, however.
The process of porting Tidals concepts has also opened up new
possibilities, some just from revisiting every design decision, and some
from the particular affordances and constraints offered by JavaScript.
This has lead to new features (and indeed bugfixes) that have found
their way back to Tidal where appropriate, and ongoing work that we will
return to in the conclusion of this paper.</p>
<h1 data-number="3" id="representing-patterns"><span
class="header-section-number">3</span> Representing Patterns</h1>
<p>Patterns are the essence of Tidal. Its patterns are abstract entities
that represent flows of time as functions, adapting a technique called
pure functional reactive programming. Taking a time span as its input, a
Pattern can output a set of events that happen within that time span. It
depends on the structure of the Pattern how the events are located in
time. From now on, this process of generating events from a time span
will be called <strong>querying</strong>. Example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><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>
<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>
<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">=&gt;</span> e<span class="op">.</span><span class="fu">show</span>()))</span></code></pre></div>
<p>In this example, we create a pattern using the <code>sequence</code>
function and <strong>query</strong> it for the time span from
<code>0</code> to <code>1</code>. Those numbers represent units of time
called <strong>cycles</strong>. The length of one cycle depends on the
tempo, which defaults to one cycle per second. The resulting events
are:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode js"><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">&#39;c3&#39;</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>
<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">&#39;e3&#39;</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>
<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">&#39;g3&#39;</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></div>
<p>Each event has a value, a begin time and an end time, where time is
represented as a fraction. In the above case, the events are placed in
sequential order, where c3 takes the first half, and e3 and g3 together
take the second half. This temporal placement is the result of the
<code>sequence</code> function, which divides its arguments equally over
one cycle. If an argument is an array, the same rule applies to that
part of the cycle. In the example, e3 and g3 are divided equally over
the second half of the whole cycle.</p>
<p>The above examples do not represent how Strudel is used in practice.
In the live coding editor, the user only has to type in the pattern
itself, the querying will be handled by the scheduler. The scheduler
will repeatedly query the pattern for events, which are then scheduled
as sound synthesis or other event triggers. Also, the above event data
structure has been simplified for readability.</p>
<figure>
<img src="images/strudel-screenshot2.png" style="width:60.0%"
alt="Screenshot of the Strudel REPL live coding editor, including piano-roll visualisation." />
<figcaption aria-hidden="true">Screenshot of the Strudel REPL live
coding editor, including piano-roll visualisation.</figcaption>
</figure>
<h1 data-number="4" id="making-patterns"><span
class="header-section-number">4</span> Making Patterns</h1>
<p>In practice, the end-user live coder will not deal with constructing
patterns directly, but will rather build patterns using Strudels
extensive combinator library to create, combine and transform
patterns.</p>
<p>The live coder will rarely use the <code>sequence</code> function as
seen above, as sequencing is implicit in many functions. For example in
the following, the <code>note</code> function constructs a pattern of
notes, sequencing its arguments in the same manner as the previous
example.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode js"><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></div>
<p>Perhaps more often, they will use the mini-notation for even terser
notation of rhythmic sequences: [^This last example is also valid Tidal
code, albeit the parenthesis is not required in its Haskell syntax in
this case. Tidal does not support passing sequences as lists directly to
the <code>note</code> function, however.].</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode js"><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">&quot;c3 [e3 g3]&quot;</span>)</span></code></pre></div>
<p>Such sequences are often treated only a starting point for
manipulation, where they then undergo pattern transformations such as
repetition, symmetry, interference/combination or randomisation,
potentially at multiple timescales. Because Strudel patterns are
represented as pure functions of time rather than as data structures,
very long and complex generative results can be represented and
manipulated without having to store the resulting sequences in
memory.</p>
<h1 data-number="5" id="pattern-example"><span
class="header-section-number">5</span> Pattern Example</h1>
<p>The following example showcases how patterns can be utilized to
create musical complexity from simple parts, using repetition and
interference:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;&lt;0 2 [4 6](3,4,1) 3&gt;&quot;</span></span>
<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>
<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>
<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">&#39;D minor&#39;</span>)</span>
<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>
<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">&quot;sawtooth square&quot;</span>)</span>
<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></div>
<p>The pattern starts with a rhythm of numbers in mini notation, which
are later interpreted inside the scale of D minor. The first line could
also be expressed without mini notation:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode js"><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></div>
<p>These numbers then undergo various pattern transformations. Here is a
short description of all the functions used:</p>
<ul>
<li><code>cat</code>: play elements sequentially, where each lasts one
cycle</li>
<li><code>brackets</code>: elements inside brackets are divided equally
over the time of their parent</li>
<li><code>.euclid(p, s, o)</code>: place p pulses evenly over s steps,
with offset o <span class="citation"
data-cites="toussaintEuclideanAlgorithmGenerates2005">(Toussaint
2005)</span></li>
<li><code>.off(n, f)</code>: layers a pattern on top of itself, with the
new layer offset by n cycles, and with function f applied</li>
<li><code>.legato(n)</code>: multiply the duration of all events in a
pattern by a factor of n</li>
<li><code>.echo(t, n, v)</code>: copy each event t times, with n cycles
in between each copy, decreasing velocity by v</li>
<li><code>.note()</code>: interpretes values as notes</li>
<li><code>.s(name)</code>: play back each event with the given
sound</li>
<li><code>.delay(wet)</code>: add delay</li>
<li><code>.delaytime(t)</code>: set delay time</li>
</ul>
<p>Much of the above will be familiar to Tidal users.</p>
<!-- 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. -->
<h1 data-number="6" id="ways-to-make-sound-and-other-events"><span
class="header-section-number">6</span> Ways to make Sound (and other
events)</h1>
<p>To generate sound, Strudel supports bindings for different
outputs:</p>
<ul>
<li>Tone.js (deprecated)</li>
<li>Web Audio API</li>
<li>WebDirt, a js recreation of Tidals <em>Dirt</em> sample engine
(deprecated)</li>
<li>OSC via osc-js, compatible with superdirt</li>
<li>Csound via the Csound WebAssembly build</li>
<li>MIDI via WebMIDI</li>
<li>Serial via WebSerial</li>
</ul>
<p>At first, we used Tone.js as sound output, but it proved to be
limited for the use case of Strudel, where each individual event could
potentially have a completely different audio graph. While the Web Audio
API takes a <em>fire-and-forget</em> approach, creating a lot of Tone.js
instruments and effects causes performance issues quickly. For that
reason, we chose to search for alternatives.</p>
<p>Strudels new default output uses the Web Audio API to create a new
audio graph for each event. It currently supports basic oscillators,
sample playback, various effects and an experimental support for
soundfonts.</p>
<p>WebDirt <span class="citation"
data-cites="ogbornDktr0WebDirt2022">(Ogborn [2016] 2022)</span> was
created as part of the Estuary Live Coding System <span class="citation"
data-cites="ogbornEstuaryBrowserbasedCollaborative2017">(Ogborn et al.
2017)</span>, and proved to be a solid choice for handling samples in
Strudel as well. We are however focused on working more directly with
the Web Audio API to be able to integrate new features more tightly.</p>
<p>Using the OSC protocol via Strudels provided Node.js-based OSC proxy
server, it is possible to send network messages to trigger events. This
is mainly used to render sound using SuperDirt <span class="citation"
data-cites="SuperDirt2022">(<em>SuperDirt</em> [2015] 2022)</span>,
which is the well-developed Supercollider-based synthesis framework that
Tidal live coders generally use as standard.</p>
<p>Recently, the experimental integration of Csound proved to bring a
new dimension of sound design capabilities to Strudel. Thanks to the
WebAssembly distribution of this classic system <span class="citation"
data-cites="CsoundWebAssembly">(Yi, Lazzarini, and Costello
2018)</span>, Csound orchestra synthesisers can be embedded in and
then patterned with Strudel code.</p>
<p>MIDI output can also be used to send MIDI messages to either external
instruments or to other programs on the same device. Unlike OSC, Strudel
is able to send MIDI directly without requiring additional proxy
software, but only from web browsers that support it (at the time of
writing, this means Chromium-based browsers).</p>
<p>Finally, Strudel supports Serial output, for example to trigger
events via microcontrollers. This has already been explored for robot
choreography by Kate Sicchio and Alex McLean, via a performance
presented at the International Conference on Live Interfaces 2022.</p>
<h1 data-number="7" id="the-strudel-repl"><span
class="header-section-number">7</span> The Strudel REPL</h1>
<p>While Strudel can be used as a library in any JavaScript codebase,
its main, reference user interface is the Strudel REPL[^REPL stands for
read, evaluate, print/play, loop. It is friendly jargon for an
interactive programming interface from computing heritage, usually for a
commandline interface but also applied to live coding editors.], which
is a browser-based live coding environment. This live code editor is
dedicated to manipulating Strudel patterns while they play. The REPL
features built-in visual feedback, which highlights which elements in
the patterned (mini-notation) sequences are influencing the event that
is currently being played. This feedback is designed to support both
learning and live use of Strudel.</p>
<p>Besides a UI for playback control and meta information, the main part
of the REPL interface is the code editor powered by CodeMirror. In it,
the user can edit and evaluate pattern code live, using one of the
available synthesis outputs to create music and/or sound art. The
control flow of the REPL follows 3 basic steps:</p>
<ol type="1">
<li>The user writes and updates code. Each update transpiles and
evaluates it to create a <code>Pattern</code> instance</li>
<li>While the REPL is running, the <code>Scheduler</code> queries the
active <code>Pattern</code> by a regular interval, generating
<code>Events</code> (also known as <code>Haps</code> in Strudel) for the
next time span.</li>
<li>For each scheduling tick, all generated <code>Events</code> are
triggered by calling their <code>onTrigger</code> method, which is set
by the output.</li>
</ol>
<figure>
<img
src="https://github.com/tidalcycles/strudel/raw/talk/talk/public/strudelflow.png?raw=true"
style="width:43.0%" alt="REPL control flow" />
<figcaption aria-hidden="true">REPL control flow</figcaption>
</figure>
<h2 data-number="7.1" id="user-code"><span
class="header-section-number">7.1</span> User Code</h2>
<p>To create a <code>Pattern</code> from the user code, two steps are
needed:</p>
<ol type="1">
<li>Transpile the JS input code to make it functional</li>
<li>Evaluate the transpiled code</li>
</ol>
<h3 data-number="7.1.1" id="transpilation-evaluation"><span
class="header-section-number">7.1.1</span> Transpilation &amp;
Evaluation</h3>
<p>In the JavaScript world, using transpilation is a common practise to
be able to use language features that are not supported by the base
language. Tools like <code>babel</code> will transpile code that
contains unsupported language features into a version of the code
without those features.</p>
<p>In the same tradition, Strudel can add a transpilation step to
simplify the user code in the context of live coding. For example, the
Strudel REPL lets the user create mini notation patterns using just
double quoted strings, while single quoted strings remain what they
are:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;c3 [e3 g3]*2&quot;</span></span></code></pre></div>
<p>is transpiled to:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode js"><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">&quot;c3 [e3 g3]*2&quot;</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></div>
<p>Here, the string is wrapped in <code>mini</code>, which will create a
pattern from a mini notation string. Additionally, the
<code>withMiniLocation</code> method passes the original source code
location of the string to the pattern, which enables highlighting active
events.</p>
<p>Other convenient features like pseudo variables, operator overloading
and top level await are possible with transpilation.</p>
<p>After the transpilation, the code is ready to be evaluated into a
<code>Pattern</code>.</p>
<p>Behind the scenes, the user code string is parsed with
<code>acorn</code>, turning it into an Abstract Syntax Tree (AST). The
AST allows changing the structure of the code before generating the
transpiled version using <code>escodegen</code>.</p>
<h3 data-number="7.1.2" id="mini-notation"><span
class="header-section-number">7.1.2</span> Mini Notation</h3>
<p>While the transpilation allows JavaScript to express Patterns in a
less verbose way, it is still preferable to use the Mini Notation as a
more compact way to express rhythm. Strudel aims to provide the same
Mini Notation features and syntax as used in Tidal.</p>
<p>The Mini Notation parser is implemented using <code>peggy</code>,
which allows generating performant parsers for Domain Specific Languages
(DSLs) using a concise grammar notation. The generated parser turns the
Mini Notation string into an AST which is used to call the respective
Strudel functions with the given structure. For example,
<code>"c3 [e3 g3]*2"</code> will result in the following calls:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">seq</span>(</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">&#39;c3&#39;</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>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">seq</span>(</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">&#39;e3&#39;</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>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">reify</span>(<span class="st">&#39;g3&#39;</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>
<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>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>)</span></code></pre></div>
<h3 data-number="7.1.3" id="highlighting-locations"><span
class="header-section-number">7.1.3</span> Highlighting Locations</h3>
<p>As seen in the examples above, both the JS and the Mini Notation
parser add source code locations using <code>withMiniLocation</code> and
<code>withLocation</code> methods. While the JS parser adds locations
relative to the user code as a whole, the Mini Notation adds locations
relative to the position of the mini notation string. The absolute
location of elements within Mini Notation can be calculated by simply
adding both locations together. This absolute location can be used to
highlight active events in real time.</p>
<h2 data-number="7.2" id="scheduling-events"><span
class="header-section-number">7.2</span> Scheduling Events</h2>
<p>After an instance of <code>Pattern</code> is obtained from the user
code, it is used by the scheduler to get queried for events. Once
started, the scheduler runs at a fixed interval to query active pattern
for events withing the current intervals time span. A simplified
implementation looks like this:</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode js"><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">&#39;c3&#39;</span><span class="op">,</span> [<span class="st">&#39;e3&#39;</span><span class="op">,</span> <span class="st">&#39;g3&#39;</span>])<span class="op">;</span> <span class="co">// pattern from user</span></span>
<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>
<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>
<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>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="pp">setInterval</span>(() <span class="kw">=&gt;</span> {</span>
<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>
<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>
<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">=&gt;</span> {</span>
<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>
<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>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<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 &quot;interval&quot; seconds</span></span></code></pre></div>
<p>Note that the above code is simplified for illustrative purposes. The
actual implementation has to work around imprecise callbacks of
<code>setInterval</code>. More about the implementation details can be
read in <a
href="https://loophole-letters.vercel.app/web-audio-scheduling">this
blog post</a>.</p>
<p>The fact that <code>Pattern.queryArc</code> is a pure function that
maps a time span to a set of events allows us to choose any interval we
like without changing the resulting output. It also means that when the
pattern is changed from outside, the next scheduling callback will work
with the new pattern, keeping its clock running.</p>
<p>The latency between the time the pattern is evaluated and the change
is heard is between <code>minLatency</code> and
<code>interval + minLatency</code>, in our example between 100ms and
600ms. In Strudel, the current query interval is 50ms with a minLatency
of 100ms, meaning the latency is between 50ms and 150ms.</p>
<h2 data-number="7.3" id="output"><span
class="header-section-number">7.3</span> Output</h2>
<p>The last step is to trigger each event in the chosen output. This is
where the given time and value of each event is used to generate audio
or any other form of time based output. The default output of the
Strudel REPL is the WebAudio output. To understand what an output does,
we first have to understand what control parameters are.</p>
<h3 data-number="7.3.1" id="control-parameters"><span
class="header-section-number">7.3.1</span> Control Parameters</h3>
<p>To be able to manipulate multiple aspects of sound in parallel, so
called control parameters are used to shape the value of each event.
Example:</p>
<div class="sourceCode" id="cb11"><pre
class="sourceCode js"><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">&quot;c3 e3&quot;</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">&#39;sawtooth&#39;</span>)</span>
<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">=&gt;</span> hap<span class="op">.</span><span class="at">value</span>)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co">/* [</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co"> { note: &#39;c3&#39;, cutoff: 1000, s: &#39;sawtooth&#39; }</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co"> { note: &#39;e3&#39;, cutoff: 1000, s: &#39;sawtooth&#39; }</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="co">] */</span></span></code></pre></div>
<p>Here, the control parameter functions <code>note</code>,
<code>cutoff</code> and <code>s</code> are used, where each controls a
different property in the value object. Each control parameter function
accepts a primitive value, a list of values to be sequenced into a
<code>Pattern</code>, or a <code>Pattern</code>. In the example,
<code>note</code> gets a <code>Pattern</code> from a Mini Notation
expression (double quoted), while <code>cutoff</code> and <code>s</code>
are given a <code>Number</code> and a (single quoted)
<code>String</code> respectively.</p>
<p>Strudel comes with a large default set of control parameter functions
that are based on the ones used by Tidal and SuperDirt, focusing on
music and audio terminology. It is however possible to create custom
control paramters for any purpose:</p>
<div class="sourceCode" id="cb12"><pre
class="sourceCode js"><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">&#39;x&#39;</span><span class="op">,</span> <span class="st">&#39;y&#39;</span>)</span>
<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></div>
<p>This example creates the custom control parameters <code>x</code> and
<code>y</code> which are then used to form a pattern that descibes the
coordinates of a circle.</p>
<h3 data-number="7.3.2" id="outputs"><span
class="header-section-number">7.3.2</span> Outputs</h3>
<p>Now that we know how the value of an event is manipulated using
control parameters, we can look at how outputs can use that value to
generate anything. The scheduler above was calling the
<code>onTrigger</code> function which is used to implement the output. A
very simple version of the web audio output could look like this:</p>
<div class="sourceCode" id="cb13"><pre
class="sourceCode js"><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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>The above example will create an <code>OscillatorNode</code> for each
event, where the frequency is controlled by the <code>note</code> param.
In essence, this is how the WebAudio API output of Strudel works, only
with many more parameters to control synths, samples and effects.</p>
<h1 data-number="8" id="pattern-alignment-and-combination"><span
class="header-section-number">8</span> Pattern alignment and
combination</h1>
<p>One core aspect of Strudel, inherited from Tidal, is the flexible way
that patterns can be combined, irrespective of their structure. Its
declarative approach means a live coder does not have to think about the
details of <em>how</em> this is done, only <em>what</em> is to be
done.</p>
<p>As a simple example, consider two number patterns
<code>"0 [1 2] 3"</code>, and <code>"10 20"</code>. The first has three
contiguous steps of equal lengths, with the second step broken down into
two substeps, giving four events in total. There are a very large number
of ways in which the structure of these two patterns could be combined,
but the default method in both Strudel and Tidal is to line up the
cycles of the two patterns, and then take events from the first pattern
and match them with those in the second pattern. Therefore, the
following two lines are equivalent:</p>
<div class="sourceCode" id="cb14"><pre
class="sourceCode js"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;0 [1 2] 3&quot;</span><span class="op">.</span><span class="fu">add</span>(<span class="st">&quot;10 20&quot;</span>)</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;10 [11 22] 23&quot;</span></span></code></pre></div>
<p>Where the events only partially overlap, they are treated as
fragments of the event in the first pattern. This is a little difficult
to conceptualise, but lets start by comparing the two patterns in the
following example:</p>
<div class="sourceCode" id="cb15"><pre
class="sourceCode js"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;0 1 2&quot;</span><span class="op">.</span><span class="fu">add</span>(<span class="st">&quot;10 20&quot;</span>)</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;10 [11 21] 20&quot;</span></span></code></pre></div>
<p>They are similar to the previous example in that the number
<code>1</code> is split in two, with its two halves added to
<code>10</code> and <code>20</code> respectively. However, the
<code>11</code> remembers that it is a fragment of that original
<code>1</code> event, and so is treated as having a duration of a third
of a cycle, despite only being active for a sixth of a cycle. Likewise,
the <code>21</code> is also a fragment of that original <code>1</code>
event, but a fragment of its second half. Because the start of its event
is missing, it wouldnt actually trigger a sound (unless it underwent
further pattern transformations/combinations).</p>
<p>In practice, the effect of this default, implicit method for
combining two patterns is that the second pattern is added <em>in</em>
to the first one, and indeed this can be made explicit:</p>
<div class="sourceCode" id="cb16"><pre
class="sourceCode js"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;0 1 2&quot;</span><span class="op">.</span><span class="at">add</span><span class="op">.</span><span class="fu">in</span>(<span class="st">&quot;10 20&quot;</span>)</span></code></pre></div>
<p>This makes way for other ways to align the pattern, and several are
already defined, in particular:</p>
<ul>
<li><code>in</code> - as explained above, aligns cycles, and applies
values from the pattern on the right <em>in</em> to the pattern on the
left.</li>
<li><code>out</code> - as with <code>in</code>, but values are applied
<em>out</em> of the pattern on the left (i.e. <em>in</em> to the one on
the right).</li>
<li><code>mix</code> - structures from both patterns are combined, so
that the new events are not fragments but are created at intersections
of events from both sides.</li>
<li><code>squeeze</code> - cycles from the pattern on the right are
squeezed into events on the left. So that
e.g. <code>"0 1 2".add.squeeze("10 20")</code> is equivalent to
<code>"[10 20] [11 21] [12 22]"</code>.</li>
<li><code>squeezeout</code> - as with <code>squeeze</code>, but cycles
from the left are squeezed into events on the right. So,
<code>"0 1 2".add.squeezeout("10 20")</code> is equivalent to
<code>[10 11 12] [20 21 22]</code>.</li>
<li><code>trig</code> is similar to <code>squeezeout</code> in that
cycles from the right are aligned with events on the left. However those
cycles are not squeezed, rather they are truncated to fit the event.
So <code>"0 1 2 3 4 5 6 7".add.trig("10 [20 30]")</code> would be
equivalent to <code>10 11 12 13 20 21 30 31</code>. In effect, events on
the right trigger cycles on the left.</li>
<li><code>trigzero</code> is similar to <code>trig</code>, but the
pattern is triggered from its very first cycle, rather than from the
current cycle. <code>trig</code> and <code>trigzero</code> therefore
only give different results where the leftmost pattern differs from one
cycle to the next.</li>
</ul>
<p>We will save going deeper into the background, design and
practicalities of these alignment functions for future publications.
However in the next section, we take them as a case study for looking at
the different design affordances offered by Haskell to Tidal, and
JavaScript to Strudel.</p>
<h1 data-number="9" id="comparing-strudel-and-haskell-in-use"><span
class="header-section-number">9</span> Comparing Strudel and Haskell in
use</h1>
<p>Unlike Haskell, JavaScript lacks the ability to define custom infix
operators, or change the meaning of existing ones. So the above Strudel
example of <code>"0 1 2".add.out("10 20")</code> is equivalent to the
Tidal expression <code>"0 1 2" +| "10 20"</code>, where the vertical bar
in the operator <code>+|</code> stands for <code>out</code> (where
<code>a |+ b</code> would be equivalent of
<code>a.add.in(b)</code>).</p>
<p>From this we can already see that Tidal tends towards brevity through
mixing infix operators with functions, and Strudel tends towards
spelling out operations which are joined together with the
<code>.</code> operator. This then is the design trade-off of Tidals
tersity, versus Strudels simplicity.</p>
<p>To demonstrate this, consider the following Tidal pattern:</p>
<pre class="tidal"><code>iter 4 $ every 3 (||+ n &quot;10 20&quot;) $ (n &quot;0 1 3&quot;) # s &quot;triangle&quot; # crush 4</code></pre>
<p>This can be directly translated to the Strudel equivalent:</p>
<div class="sourceCode" id="cb18"><pre
class="sourceCode js"><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">&quot;10 20&quot;</span>)<span class="op">,</span> <span class="fu">n</span>(<span class="st">&quot;0 1 3&quot;</span>)<span class="op">.</span><span class="fu">s</span>(<span class="st">&quot;triangle&quot;</span>)<span class="op">.</span><span class="fu">crush</span>(<span class="dv">4</span>)))</span></code></pre></div>
<p>Although for a more canonical Strudel expression, we would reorder it
as:</p>
<div class="sourceCode" id="cb19"><pre
class="sourceCode js"><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">&quot;0 1 3&quot;</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">&quot;10 20&quot;</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">&quot;triangle&quot;</span>)<span class="op">.</span><span class="fu">crush</span>(<span class="dv">4</span>)</span></code></pre></div>
<p>The Strudel example uses the <code>.</code> method call operator for
all operations and combinations, whereas the Tidal example has
<code>#</code> for the default method for combining patterns and uses
infix operators for other methods. The lack of parenthesis in the Tidal
example is partly due to the way that arguments are applied to Haskells
functions, and partly due to the use of the <code>$</code> operator as
an alternative way to establish precedence and control the order of
evaluation.</p>
<p>Considering the above, we argue that the Haskell syntax is a little
cleaner, but that the Strudel syntax is easier to learn. Our informal
observation is that while Haskells dollar <code>$</code> operator is
very useful in making code easier to work with, it is one of the most
difficult aspects of Tidal use for beginners to learn. On the other
hand, the deeper levels of parenthesis in Strudel code can be difficult
to keep track of, especially while coding under pressure of live musical
performance. However this difficulty can be largely be mitigated by
reordering expressions, and further mitigated by supporting editor
features.</p>
<p>With Strudel, we have little choice but to embrace the affordances
and constraints offered by JavaScript, and while designing a
domain-specific language entirely based on method calls is a challenge,
through creative adoption of functional programming techniques like
partial application, we are so far very happy with the results. Tidals
functional reactive approach to pattern-making has in general translated
well to JavaScript, and opportunities and constraints have overall
traded off to create a very approachable and useable live coding
environment.</p>
<h2 data-number="9.1" id="the-trade-off-of-flexible-typing"><span
class="header-section-number">9.1</span> The trade-off of flexible
typing</h2>
<p>We have identified one problem with porting Tidal to JavaScript where
we have missed Haskells strict typing and type inference. In both Tidal
and Strudel, time is rational, where any point in time is represented as
the ratio of two integers. This allows representation of musical ratios
such that are impossible to represent accurately using the more common
floating point numbers. However while libraries are available that
support rational numbers in JavaScript, the lack of strict typing means
that it is easy to implement pattern methods where computationally
expensive conversion from floating point to rational numbers are
performed late, and therefore often enough to overload the CPUs, due to
the large number of iterative calculations required to estimate a ratio
for a given floating point number. To mitigate this problem, we might
consider moving to TypeScript in the future.</p>
<h1 data-number="10" id="future-outlook"><span
class="header-section-number">10</span> Future Outlook</h1>
<p>The project is still young, with many features on the horizon. As
general guiding principles, Strudel aims to be</p>
<ol type="1">
<li>accessible</li>
<li>consistent with Tidals approach to pattern</li>
<li>modular and extensible</li>
</ol>
<p>While Haskells type system makes it a great language for the ongoing
development of Tidals inner representation of pattern, JavaScripts
vibrant ecosystem, flexibility and accessibility makes it a great host
for more ad-hoc experiments, including interface design. For the future,
it is planned to integrate additional alternative sound engines such as
Glicol <span class="citation" data-cites="lanChaosprintGlicol2022">(Lan
[2020] 2022)</span> and Faust <span class="citation"
data-cites="FaustProgrammingLanguage2022">(<em>Faust - Programming
Language for Audio Applications and Plugins</em> [2016] 2022)</span>.
Strudel is already approaching feature parity with Tidal, but there are
more Tidal functions to be ported, and work to be done to improve
compatibility with Tidals mininotation. Tidal version 2.0 is under
development, which brings a new representation for sequences to its
patterns, which will then be brought to Strudel. Besides sound, other
ways to render events are being explored, such as graphical, and
choreographic output. We are also looking into alternative ways of
editing patterns, including multi-user editing for network music,
parsing a novel syntax to escape the constraints of javascript, and
developing hardware/e-textile interfaces. In summary, there is a lot of
fun ahead.</p>
<h1 data-number="11" id="links"><span
class="header-section-number">11</span> Links</h1>
<p>The Strudel REPL is available at <a
href="https://strudel.cc"
class="uri">https://strudel.cc</a>, including an
interactive tutorial. The repository is at <a
href="https://github.com/tidalcycles/strudel"
class="uri">https://github.com/tidalcycles/strudel</a>, all the code is
open source under the AGPL-3.0 License.</p>
<h1 data-number="12" id="acknowledgments"><span
class="header-section-number">12</span> Acknowledgments</h1>
<p>Thanks to the Strudel and wider Tidal, live coding, WebAudio and
free/open source software communities for inspiration and support. Alex
McLeans work on this project is supported by a UKRI Future Leaders
Fellowship [grant number MR/V025260/1].</p>
<h1 class="unnumbered" id="references">References</h1>
<div id="refs" class="references csl-bib-body hanging-indent"
role="doc-bibliography">
<div id="ref-FaustProgrammingLanguage2022" class="csl-entry"
role="doc-biblioentry">
<em>Faust - Programming Language for Audio Applications and
Plugins</em>. (2016) 2022. C++. GRAME. <a
href="https://github.com/grame-cncm/faust">https://github.com/grame-cncm/faust</a>.
</div>
<div id="ref-jackHydra2022" class="csl-entry" role="doc-biblioentry">
Jack, Olivia. (2022) 2022. <em>Hydra</em>. <a
href="https://github.com/ojack/hydra">https://github.com/ojack/hydra</a>.
</div>
<div id="ref-lanChaosprintGlicol2022" class="csl-entry"
role="doc-biblioentry">
Lan, Qichao. (2020) 2022. <em>Chaosprint/Glicol</em>. Rust. <a
href="https://github.com/chaosprint/glicol">https://github.com/chaosprint/glicol</a>.
</div>
<div id="ref-mcleanAlgorithmicPattern2020a" class="csl-entry"
role="doc-biblioentry">
Mclean, Alex. 2020. <span>“Algorithmic Pattern.”</span> In
<em>Proceedings of the International Conference on New Interfaces for
Musical Expression</em>, 265--270. Birmingham, UK. <a
href="https://zenodo.org/record/4813352">https://zenodo.org/record/4813352</a>.
</div>
<div id="ref-mcleanFeedforward2020" class="csl-entry"
role="doc-biblioentry">
McLean, Alex. 2020. <span>“Feedforward.”</span> In <em>Proceedings of
New Interfaces for Musical Expression</em>. Birmingham. <a
href="https://zenodo.org/record/6353969">https://zenodo.org/record/6353969</a>.
</div>
<div id="ref-mcleanTidalVortexZero2022" class="csl-entry"
role="doc-biblioentry">
McLean, Alex, Raphaël Forment, Sylvain Le Beux, and Damián Silvani.
2022. <span>“TidalVortex Zero.”</span> In <em>Proceedings of the 7th
International Conference on Live Coding</em>. Limerick, Ireland: Zenodo.
<a
href="https://doi.org/10.5281/zenodo.6456380">https://doi.org/10.5281/zenodo.6456380</a>.
</div>
<div id="ref-ogbornDktr0WebDirt2022" class="csl-entry"
role="doc-biblioentry">
Ogborn, David. (2016) 2022. <em>Dktr0/WebDirt</em>. JavaScript. <a
href="https://github.com/dktr0/WebDirt">https://github.com/dktr0/WebDirt</a>.
</div>
<div id="ref-ogbornEstuaryBrowserbasedCollaborative2017"
class="csl-entry" role="doc-biblioentry">
Ogborn, David, Jamie Beverley, Luis Navarro del Angel, Eldad Tsabary,
and Alex McLean. 2017. <span>“Estuary: Browser-Based Collaborative
Projectional Live Coding of Musical Patterns.”</span> In <em>Proceedings
of the International Conference on Live Coding</em>, 11. Morelia.
</div>
<div id="ref-robertsGibberLiveCoding2012" class="csl-entry"
role="doc-biblioentry">
Roberts, Charles, and Joann Kuchera-morin. 2012. <span>“Gibber: Live
Coding Audio in the Browser.”</span> In <em>In Proceedings of the 2012
International Computer Music Conference</em>.
</div>
<div id="ref-StrudelWAC2022" class="csl-entry" role="doc-biblioentry">
Roos, Felix, and Alex McLean. 2022. <span>“Strudel: Algorithmic Patterns
for the Web.”</span> In. Zenodo. <a
href="https://doi.org/10.5281/zenodo.6768844">https://doi.org/10.5281/zenodo.6768844</a>.
</div>
<div id="ref-solomonPurescriptocarina2022" class="csl-entry"
role="doc-biblioentry">
Solomon, Mike. (2021) 2022. <em>Purescript-Ocarina</em>. PureScript. <a
href="https://github.com/mikesol/purescript-ocarina">https://github.com/mikesol/purescript-ocarina</a>.
</div>
<div id="ref-SuperDirt2022" class="csl-entry" role="doc-biblioentry">
<em>SuperDirt</em>. (2015) 2022. SuperCollider. musikinformatik. <a
href="https://github.com/musikinformatik/SuperDirt">https://github.com/musikinformatik/SuperDirt</a>.
</div>
<div id="ref-toussaintEuclideanAlgorithmGenerates2005" class="csl-entry"
role="doc-biblioentry">
Toussaint, Godfried. 2005. <span>“The Euclidean Algorithm Generates
Traditional Musical Rhythms.”</span> In <em>In Proceedings of BRIDGES:
Mathematical Connections in Art, Music and Science</em>, 4756. <a
href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231">http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231</a>.
</div>
<div id="ref-CsoundWebAssembly" class="csl-entry"
role="doc-biblioentry">
Yi, Steven, Victor Lazzarini, and Edward Costello. 2018.
<span>“WebAssembly AudioWorklet Csound.”</span> In. Berlin, Germany. <a
href="https://mural.maynoothuniversity.ie/16018/">https://mural.maynoothuniversity.ie/16018/</a>.
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,460 @@
---
title: 'Strudel: live coding patterns on the Web'
author:
- name: Felix Roos
affiliation: Unaffiliated
email: flix91@gmail.com
- name: Alex McLean
affiliation: Then Try This
email: alex@slab.org
abstract: |
This paper introduces Strudel, which brings the TidalCycles approach to live coding algorithmic patterns to native JavaScript and the web. We begin by giving a little background of the first year of development, before sharing some detail about its implementation and examples of use. We go on to outline the wide range of synthesis and other outputs available in Strudel, including WebAudio, MIDI, OSC (for SuperDirt), WebSerial and CSound, and introduce Strudel's REPL live editor, including its built-in visualisations. We then compare Strudel with Tidal, the trade-offs involved between JavaScript and Haskell, and the unique capabilities offered by Strudel for aligning patterns, before concluding with some thoughts about the future.
bibliography: citations.json
fontsize: 11pt
geometry: margin=2cm
fontfamily: libertine
fontfamily: inconsolata
mainfont: Linux Libertine O
monofont: Inconsolata
date: '2022-12-14'
---
# Introduction
In the following paper, we introduce *Strudel*, an alternative
implementation of the TidalCycles (or 'Tidal' for short) live coding
system, using the JavaScript programming language. Strudel is an
attempt to make live coding more accessible, by creating a system that
runs entirely in the browser, while opening Tidal's approach to
algorithmic patterns [@mcleanAlgorithmicPattern2020a] up to modern
audio/visual web technologies. The Strudel REPL is a live code editor
dedicated to manipulating patterns while they play, with builtin
visual feedback. While Strudel is written in JavaScript, the API is
optimized for simplicity and readability by applying code
transformations on the syntax tree level, allowing language operations
that would otherwise be impossible. The application supports multiple
ways to output sound, including Tone.js, Web Audio Nodes, OSC (Open
Sound Control) messages, Web Serial, Web MIDI and Csound. The project
is split into multiple packages, allowing granular reuse in other
applications. Apart from TidalCycles, Strudel draws inspiration from
many prior existing projects like TidalVortex
[@mcleanTidalVortexZero2022], Gibber [@robertsGibberLiveCoding2012],
Estuary [@ogbornEstuaryBrowserbasedCollaborative2017], Hydra
[@jackHydra2022], Ocarina [@solomonPurescriptocarina2022] and
Feedforward [@mcleanFeedforward2020]. This paper expands the Strudel
Demo paper for the Web Audio Conference 2022 [@StrudelWAC2022].
The first tentative commit to the Strudel project was on 22nd January
2022 by Alex McLean, with the core representation implemented over the
following few days. Although this was his first attempt at a
JavaScript-based application, by 27th January, Alex had managed to
upload the initial version to the 'npm' javascript package database,
sharing with the wider community for comment. By 4th February, Felix
Roos had discovered Strudel and contributed a 'REPL' user interface to
it, and then contributed a scheduler the next day, so that Strudel
could already make sound. At this point, Alex and Felix shared
ownership to the repository, and the project has since proved to be a
productive confluence of Felix's own work into music representation
and visualisation, with Alex's experience with making Tidal. Felix has
since become the primary contributor to Strudel, with Alex continuing
to jump between developing both Strudel and Tidal. Aspects of
Strudel's development have therefore fed back into TidalCycles, and
both systems have maintained a shared conceptual underpinning. We plan
to continue working towards feature parity between these systems,
although within the syntactical trade-offs and library ecosystems of
JavaScript and Haskell, some divergence is inevitable and healthy.
Over the first year of its life, Strudel is now a fully-fledged live
coding environment, porting Tidal's core represention of patterns,
pattern transformations, and mini-notation for polymetric sequences,
combined with a wealth of features for synthesising and visualising
those patterns.
# From Tidal to Strudel and back
As mentioned above, the original Tidal is implemented as a domain specific language (DSL) embedded in the Haskell pure functional programming language, and takes advantage of Haskell's terse syntax and advanced, 'strong' type system. JavaScript on the other hand, is a multi-paradigm programming language, with a dynamic type system. Because Tidal leans heavily on many of Haskell's more unique features, it was not always clear that it could meaningfully be ported to a multi-paradigm scripting language. However, this possibility was already demonstrated with an earlier port to Python [TidalVortex; @mcleanTidalVortexZero2022], and we have now successfully implemented Tidal's pure functional representation of patterns in Strudel, including partial application, currying, and the functor, applicative and monadic structures that underlie Tidal's expressive pattern transformations. The result is a terse and highly composable system, where everything is either a pattern, or a function for combining and manipulating patterns, offering a rich creative ground for exploration.
This development process has been far from a one-way port, however. The process of porting Tidal's concepts has also opened up new possibilities, some just from revisiting every design decision, and some from the particular affordances and constraints offered by JavaScript. This has lead to new features (and indeed bugfixes) that have found their way back to Tidal where appropriate, and ongoing work that we will return to in the conclusion of this paper.
# Representing Patterns
Patterns are the essence of Tidal. Its patterns are abstract entities that represent flows of time as functions, adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can output a set of events that happen within that time span. It depends on the structure of the Pattern how the events are located in time.
From now on, this process of generating events from a time span will be called **querying**.
Example:
```js
const pattern = sequence(c3, [e3, g3])
const events = pattern.queryArc(0, 1)
console.log(events.map(e => e.show()))
```
In this example, we create a pattern using the `sequence` function and **query** it for the time span from `0` to `1`.
Those numbers represent units of time called **cycles**. The length of one cycle depends on the tempo, which defaults to one cycle per second.
The resulting events are:
```js
["[ 0/1 -> 1/2 | c3 ]",
"[ 1/2 -> 3/4 | e3 ]",
"[ 3/4 -> 1/1 | g3 ]"
]
```
Each event has a value, a begin time and an end time, where time is represented as a fraction. In the above case, the events are placed in sequential order, where c3 takes the first half, and e3 and g3 together take the second half. This temporal placement is the result of the `sequence` function, which divides its arguments equally over one cycle. If an argument is an array, the same rule applies to that part of the cycle. In the example, e3 and g3 are divided equally over the second half of the whole cycle.
Note that the query function is not just a way to access a pattern, but true to the principles of functional programming, is the pattern itself. This means that in theory there is no way to change a pattern, it is opaque as a pure function. In practice though, Strudel and Tidal are all about transforming patterns, so how is this done? The answer is, by replacing the pattern with a new one, that calls the old one. This new one is only able to manipulate the query before passing it to the old pattern, and manipulate the results from it before returning them to caller. But, this is enough to support all the temporal and structural manipulations provided by Strudel (and Tidal's) extensive library of functions.
The above examples do not represent how Strudel is used in practice. In the live coding editor, the user only has to type in the pattern itself, the querying will be handled by the scheduler. The scheduler will repeatedly query the pattern for events, which are then scheduled as sound synthesis or other event triggers.
Also, the above event data structure has been simplified for readability.
![Screenshot of the Strudel 'REPL' live coding editor, including piano-roll visualisation.](images/strudel-screenshot2.png){ width=60% }
# Making Patterns
In practice, the end-user live coder will not deal with constructing patterns directly, but will rather build patterns using Strudel's extensive combinator library to create, combine and transform patterns.
The live coder will rarely use the `sequence` function as seen above, as sequencing is implicit in many functions. For example in the following, the `note` function constructs a pattern of notes, sequencing its arguments in the same manner as the previous example.
```js
note(c3, [e3, g3])
```
Perhaps more often, they will use the mini-notation for even terser notation of rhythmic sequences: ^[This last example is also valid Tidal code, albeit the parenthesis is not required in its Haskell syntax in this case. Tidal does not support passing sequences as lists directly to the `note` function, however.].
```js
note("c3 [e3 g3]")
```
Such sequences are often treated only as a starting point for manipulation, where they then undergo pattern transformations such as repetition, symmetry, interference/combination or randomisation, potentially at multiple timescales. Because Strudel patterns are represented as pure functions of time rather than as data structures, very long and complex generative results can be represented and manipulated without having to store the resulting sequences in memory.
# Pattern Example
The following example showcases how patterns can be utilized to create musical complexity from simple parts, using repetition and interference:
```js
"<0 2 [4 6](3,4,1) 3>"
.off(1/4, add(2))
.off(1/2, add(6))
.scale('D minor')
.legato(.25)
.note().s("sawtooth square")
.delay(.8).delaytime(.125)
```
The pattern starts with a rhythm of numbers in mini-notation, which are later interpreted inside the scale of D minor.
The first line could also be expressed without mini-notation:
```js
cat(0, 2, [4, 6].euclid(3, 4, 1), 3)
```
These numbers then undergo various pattern transformations. Here is a short description of all the functions used:
- `cat`: play elements sequentially, where each lasts one cycle
- `brackets`: elements inside brackets are divided equally over the time of their parent
- `.euclid(p, s, o)`: place p pulses evenly over s steps, with offset o [@toussaintEuclideanAlgorithmGenerates2005]
- `.off(n, f)`: layers a pattern on top of itself, with the new layer offset by n cycles, and with function f applied
- `.legato(n)`: multiply the duration of all events in a pattern by a factor of n
- `.echo(t, n, v)`: copy each event t times, with n cycles in between each copy, decreasing velocity by v
- `.note()`: interpretes values as notes
- `.s(name)`: play back each event with the given sound
- `.delay(wet)`: add delay
- `.delaytime(t)`: set delay time
Much of the above will be familiar to Tidal users.
<!-- 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. -->
# Ways to make Sound (and other events)
To generate sound, Strudel supports bindings for different outputs:
- Tone.js (deprecated)
- Web Audio API
- WebDirt, a js recreation of Tidal's *Dirt* sample engine (deprecated)
- OSC via osc-js, compatible with superdirt
- Csound via the Csound WebAssembly build
- MIDI via WebMIDI
- Serial via WebSerial
At first, we used Tone.js as sound output, but it proved to be limited for the use case of Strudel, where each individual event could potentially have a completely different audio graph.
While the Web Audio API takes a *fire-and-forget* approach, creating a lot of Tone.js instruments and effects causes performance issues quickly. For that reason, we chose to search for alternatives.
Strudel's new default output uses the Web Audio API to create a new audio graph for each event. It currently supports basic oscillators, sample playback, various effects and an experimental support for soundfonts.
WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and proved to be a solid choice for handling samples in Strudel as well. We are however focused on working more directly with the Web Audio API to be able to integrate new features more tightly.
Using the OSC protocol via Strudel's provided Node.js-based OSC proxy server, it is possible to send network messages to trigger events. This is mainly used to render sound using SuperDirt [@SuperDirt2022], which is the well-developed Supercollider-based synthesis framework that Tidal live coders generally use as standard.
Recently, the experimental integration of Csound proved to bring a new dimension of sound design capabilities to Strudel. Thanks to the WebAssembly distribution of this classic system [@CsoundWebAssembly], Csound 'orchestra' synthesisers can be embedded in and then patterned with Strudel code.
MIDI output can also be used to send MIDI messages to either external instruments or to other programs on the same device. Unlike OSC, Strudel is able to send MIDI directly without requiring additional proxy software, but only from web browsers that support it (at the time of writing, this means Chromium-based browsers).
Finally, Strudel supports Serial output, for example to trigger events
via microcontrollers. This has already been explored for robot
choreography by Kate Sicchio and Alex McLean, via a performance
presented at the International Conference on Live Interfaces 2022.
# The Strudel REPL
While Strudel can be used as a library in any JavaScript codebase, its main, reference user interface is the Strudel REPL^[REPL stands for read, evaluate, print/play, loop. It is friendly jargon for an interactive programming interface from computing heritage, usually for a commandline interface but also applied to live coding editors.], which is a browser-based live coding environment. This live code editor is dedicated to manipulating Strudel patterns while they play. The REPL features built-in visual feedback, highlighting which elements in the patterned (mini-notation) sequences are influencing the event that is currently being played. This feedback is designed to support both learning and live use of Strudel.
Besides a UI for playback control and meta information, the main part of the REPL interface is the code editor powered by CodeMirror. In it, the user can edit and evaluate pattern code live, using one of the available synthesis outputs to create music and/or sound art. The control flow of the REPL follows 3 basic steps:
1. The user writes and updates code. Each update transpiles and evaluates it to create a `Pattern` instance
2. While the REPL is running, the `Scheduler` queries the active `Pattern` by a regular interval, generating `Events` (also known as `Haps` in Strudel) for the next time span.
3. For each scheduling tick, all generated `Events` are triggered by calling their `onTrigger` method, which is set by the output.
![REPL control flow](images/strudelflow.png){ width=43% }
## User Code
To create a `Pattern` from the user code, two steps are needed:
1. Transpile the JS input code to make it functional
2. Evaluate the transpiled code
### Transpilation & Evaluation
In the JavaScript world, using transpilation is a common practise to be able to use language features that are not supported by the base language. Tools like `babel` will transpile code that contains unsupported language features into a version of the code without those features.
In the same tradition, Strudel can add a transpilation step to simplify the user code in the context of live coding. For example, the Strudel REPL lets the user create mini-notation patterns using just double quoted strings, while single quoted strings remain what they are:
```js
"c3 [e3 g3]*2"
```
is transpiled to:
```js
mini("c3 [e3 g3]*2").withMiniLocation([1,0,0],[1,14,14])
```
Here, the string is wrapped in `mini`, which will create a pattern from a mini-notation string. Additionally, the `withMiniLocation` method passes the original source code location of the string to the pattern, which enables highlighting active events.
Other convenient features like pseudo variables, operator overloading and top level await are possible with transpilation.
After the transpilation, the code is ready to be evaluated into a `Pattern`.
Behind the scenes, the user code string is parsed with `acorn`, turning it into an Abstract Syntax Tree (AST). The AST allows changing the structure of the code before generating the transpiled version using `escodegen`.
### Mini-notation
While the transpilation allows JavaScript to express Patterns in a less verbose way, it is still preferable to use the mini-notation as a more compact way to express rhythm. Strudel aims to provide the same mini-notation features and syntax as used in Tidal.
The mini-notation parser is implemented using `peggy`, which allows generating performant parsers for Domain Specific Languages (DSLs) using a concise grammar notation. The generated parser turns the mini-notation string into an AST which is used to call the respective Strudel functions with the given structure. For example, `"c3 [e3 g3]*2"` will result in the following calls:
```js
seq(
reify('c3').withLocation([1,1,1], [1,4,4]),
seq(
reify('e3').withLocation([1,5,5], [1,8,8]),
reify('g3').withLocation([1,8,8], [1,10,10]),
).fast(2)
)
```
### Highlighting Locations
As seen in the examples above, both the JS and the mini-notation parser add source code locations using `withMiniLocation` and `withLocation` methods. While the JS parser adds locations relative to the user code as a whole, the mini-notation adds locations relative to the position of the mini-notation string. The absolute location of elements within mini-notation can be calculated by simply adding both locations together. This absolute location can be used to highlight active events in real time.
## Scheduling Events
After an instance of `Pattern` is obtained from the user code,
it is used by the scheduler to get queried for events. Once started, the scheduler runs at a fixed interval to query the active pattern for events within the current interval's time span. A simplified implementation looks like this:
```js
let pattern = seq('c3', ['e3', 'g3']); // pattern from user
let interval = 0.5; // query interval in seconds
let time = 0; // beginning of current time span
let minLatency = .1; // min time before a hap should trigger
setInterval(() => {
const haps = pattern.queryArc(time, time + interval);
time += interval; // increment time
haps.forEach((hap) => {
const deadline = hap.whole.begin - time + minLatency;
onTrigger(hap, deadline, duration);
});
}, interval * 1000); // query each "interval" seconds
```
Note that the above code is simplified for illustrative purposes. The actual implementation has to work around imprecise callbacks of `setInterval`. More about the implementation details can be read in [this blog post](https://loophole-letters.vercel.app/web-audio-scheduling).
The fact that `Pattern.queryArc` is a pure function that maps a time span to a set of events allows us to choose any interval we like without changing the resulting output. It also means that when the pattern is changed from outside, the next scheduling callback will work with the new pattern, keeping its clock running.
The latency between the time the pattern is evaluated and the change is heard is between `minLatency` and `interval + minLatency`, in our example between 100ms and 600ms. In Strudel, the current query interval is 50ms with a minLatency of 100ms, meaning the latency is between 50ms and 150ms.
## Output
The last step is to trigger each event in the chosen output.
This is where the given time and value of each event is used to generate audio or any other form of time based output. The default output of the Strudel REPL is the WebAudio output. To understand what an output does, we first have to understand what control parameters are.
### Control Parameters
To be able to manipulate multiple aspects of sound in parallel, so called control parameters are used to shape the value of each event. Example:
```js
note("c3 e3").cutoff(1000).s('sawtooth')
.queryArc(0, 1).map(hap => hap.value)
/* [
{ note: 'c3', cutoff: 1000, s: 'sawtooth' }
{ note: 'e3', cutoff: 1000, s: 'sawtooth' }
] */
```
Here, the control parameter functions `note`, `cutoff` and `s` are used, where each controls a different property in the value object. Each control parameter function accepts a primitive value, a list of values to be sequenced into a `Pattern`, or a `Pattern`. In the example, `note` gets a `Pattern` from a mini-notation expression (double quoted), while `cutoff` and `s` are given a `Number` and a (single quoted) `String` respectively.
Strudel comes with a large default set of control parameter functions that are based on the ones used by Tidal and SuperDirt, focusing on music and audio terminology. It is however possible to create custom control parameters for any purpose:
```js
const { x, y } = createParams('x', 'y')
x(sine.range(0, 200)).y(cosine.range(0,200))
```
This example creates the custom control parameters `x` and `y` which are then used to form a pattern that descibes the coordinates of a circle.
### Outputs
Now that we know how the value of an event is manipulated using control parameters, we can look at how outputs can use that value to generate anything. The scheduler above was calling the `onTrigger` function which is used to implement the output. A very simple version of the web audio output could look like this:
```js
function onTrigger(hap, deadline, duration) {
const { note } = hap.value;
const time = getAudioContext().currentTime + deadline;
const o = getAudioContext().createOscillator();
o.frequency.value = getFreq(note);
o.start(time);
o.stop(time + event.duration);
o.connect(getAudioContext().destination);
}
```
The above example will create an `OscillatorNode` for each event, where the frequency is controlled by the `note` param. In essence, this is how the WebAudio API output of Strudel works, only with many more parameters to control synths, samples and effects.
# Pattern alignment and combination
One core aspect of Strudel, inherited from Tidal, is the flexible way that patterns can be combined, irrespective of their structure. Its declarative approach means a live coder does not have to think about the details of *how* this is done, only *what* is to be done.
As a simple example, consider two number patterns `"0 [1 2] 3"`, and `"10 20"`. The first has three contiguous steps of equal lengths, with the second step broken down into two substeps, giving four events in total. There are a very large number of ways in which the structure of these two patterns could be combined, but the default method in both Strudel and Tidal is to line up the cycles of the two patterns, and then take events from the first pattern and match them with those in the second pattern. Therefore, the following two lines are equivalent:
```js
"0 [1 2] 3".add("10 20")
"10 [11 22] 23"
```
Where the events only partially overlap, they are treated as fragments
of the event in the first pattern. This is a little difficult to
conceptualise, but lets start by comparing the two patterns in the
following example:
```js
"0 1 2".add("10 20")
"10 [11 21] 20"
```
They are similar to the previous example in that the number `1` is split in two, with its two halves added to `10` and `20` respectively. However, the `11` 'remembers' that it is a fragment of that original `1` event, and so is treated as having a duration of a third of a cycle, despite only being active for a sixth of a cycle. Likewise, the `21` is also a fragment of that original `1` event, but a fragment of its second half. Because the start of its event is missing, it wouldn't actually trigger a sound (unless it underwent further pattern transformations/combinations).
In practice, the effect of this default, implicit method for combining two patterns is that the second pattern is added *in* to the first one, and indeed this can be made explicit:
```js
"0 1 2".add.in("10 20")
```
This makes way for other ways to align the pattern, and several are already defined, in particular:
* `in` - as explained above, aligns cycles, and applies values from the pattern on the right *in* to the pattern on the left.
* `out` - as with `in`, but values are applied *out* of the pattern on the left (i.e. *in* to the one on the right).
* `mix` - structures from both patterns are combined, so that the new events are not fragments but are created at intersections of events from both sides.
* `squeeze` - cycles from the pattern on the right are squeezed into events on the left. So that e.g. `"0 1 2".add.squeeze("10 20")` is equivalent to `"[10 20] [11 21] [12 22]"`.
* `squeezeout` - as with `squeeze`, but cycles from the left are squeezed into events on the right. So, `"0 1 2".add.squeezeout("10 20")` is equivalent to `[10 11 12] [20 21 22]`.
* `trig` is similar to `squeezeout` in that cycles from the right are aligned with events on the left. However those cycles are not 'squeezed', rather they are truncated to fit the event. So `"0 1 2 3 4 5 6 7".add.trig("10 [20 30]")` would be equivalent to `10 11 12 13 20 21 30 31`. In effect, events on the right 'trigger' cycles on the left.
* `trigzero` is similar to `trig`, but the pattern is 'triggered' from its very first cycle, rather than from the current cycle. `trig` and `trigzero` therefore only give different results where the leftmost pattern differs from one cycle to the next.
We will save going deeper into the background, design and practicalities of these alignment functions for future publications. However in the next section, we take them as a case study for looking at the different design affordances offered by Haskell to Tidal, and JavaScript to Strudel.
# Comparing Strudel and Haskell in use
Unlike Haskell, JavaScript lacks the ability to define custom infix
operators, or change the meaning of existing ones. So the above
Strudel example of `"0 1 2".add.out("10 20")` is equivalent to the
Tidal expression `"0 1 2" +| "10 20"`, where the vertical bar in the
operator `+|` stands for `out` (where `a |+ b` would be equivalent of
`a.add.in(b)`).
From this we can already see that Tidal tends towards brevity through
mixing infix operators with functions, and Strudel tends towards
spelling out operations which are joined together with the `.`
operator. This then is the design trade-off of Tidal's tersity,
versus Strudel's simplicity.
To demonstrate this, consider the following Tidal pattern:
```haskell
iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4
```
This can be directly translated to the Strudel equivalent:
```js
iter(4, every(3, add.squeeze("10 20"), n("0 1 3").s("triangle").crush(4)))
```
Although for a more canonical Strudel expression, we would reorder it
as:
```js
n("0 1 3").every(3, add.squeeze("10 20")).iter(4).s("triangle").crush(4)
```
The Strudel example uses the `.` method call operator for all
operations and combinations, whereas the Tidal example has `#` for the
default method for combining patterns and uses infix operators for
other methods. The relative lack of parenthesis in the Tidal example is partly
due to the way that arguments are applied to Haskell's functions, and
partly due to the use of the `$` operator as an alternative way to
establish precedence and control the order of evaluation.
Considering the above, we hypothesise that the Haskell syntax is a little
cleaner, but that the Strudel syntax is easier to learn. Our informal
observation is that while Haskell's dollar `$` operator is very useful
in making code easier to work with, it is one of the most difficult
aspects of Tidal use for beginners to learn. On the other hand, the
deeper levels of parenthesis in Strudel code can be difficult to keep
track of, especially while coding under pressure of live musical
performance. However this difficulty can largely be mitigated by
reordering expressions, and further mitigated by supporting editor
features.
With Strudel, we have little choice but to embrace the affordances and
constraints offered by JavaScript, and while designing a
domain-specific language based on method calls is a
challenge, through creative adoption of functional programming
techniques like partial application, we are so far very happy with the
results. Tidal's functional reactive approach to pattern-making has in
general translated well to JavaScript, and opportunities and
constraints have overall traded off to create a very approachable and
useable live coding environment.
## The trade-off of flexible typing
We have identified one problem with porting Tidal to JavaScript where we have missed Haskell's strict typing and type inference. In both Tidal and Strudel, time is rational, where any point in time is represented as the ratio of two integers. This allows representation of musical ratios such that are impossible to represent accurately using the more common floating point numbers. However while libraries are available that support rational numbers in JavaScript, the lack of strict typing means that it is easy to implement pattern methods where computationally expensive conversion from floating point to rational numbers are performed late, and therefore often enough to overload the CPUs, due to the large number of iterative calculations required to estimate a ratio for a given floating point number. To mitigate this problem, we might consider moving to TypeScript in the future.
# Future Outlook
The project is still young, with many features on the horizon. As general guiding principles, Strudel aims to be
1. accessible
2. consistent with Tidal's approach to pattern
3. modular and extensible
While Haskell's type system makes it a great language for the ongoing development of Tidal's inner representation of pattern, JavaScript's vibrant ecosystem, flexibility and accessibility makes it a great host for more ad-hoc experiments, including interface design. For the future, it is planned to integrate additional alternative sound engines such as Glicol [@lanChaosprintGlicol2022] and Faust [@FaustProgrammingLanguage2022]. Strudel is already approaching feature parity with Tidal, but there are more Tidal functions to be ported, and work to be done to improve compatibility with Tidal's mini-notation. Tidal version 2.0 is under development, which brings a new representation for sequences to its patterns, which will then be brought to Strudel. Besides sound, other ways to render events are being explored, such as graphical, and choreographic output. We are also looking into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax to escape the constraints of JavaScript, and developing hardware/e-textile interfaces. In summary, there is a lot of fun ahead.
# Links
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the AGPL-3.0 License.
# Acknowledgments
Thanks to the Strudel and wider Tidal, live coding, WebAudio and free/open source software communities for inspiration and support. Alex McLean's work on this project is supported by a UKRI Future Leaders Fellowship [grant number MR/V025260/1].
# References

Binary file not shown.

Binary file not shown.

BIN
docs/iclc2023-paper/images/cc.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -0,0 +1,92 @@
% Copyright 2014 Michael Sharpe
% Based initially on Karl Berry's inconsolata.sty.
% You may freely use, modify and/or distribute this file.
\def\fileversion{1.05}
\def\filedate{2014/06/22}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{inconsolata}[\filedate\space v\fileversion]
\message{`inconsolata-zi4' v\fileversion, \filedate\space Text macros for Inconsolata (msharpe)}
\RequirePackage{textcomp}
\RequirePackage{keyval}
\newcount\zifour@ocount
\newif\ifzifour@altzero
\newif\ifzifour@noupq
\define@key{zifour}{scaled}[1.0]{\def\zifour@scaled{s*[#1]}}
\DeclareOption*{%
\begingroup
\edef\x{\endgroup
\noexpand\setkeys{zifour}{\CurrentOption}}%
\x}
% by default, change \tt to mean zi4.
\newcommand*{\zifour@default}{%
\renewcommand*{\ttdefault}{zi4}%
}
% option [nott] to avoid changing tt.
\DeclareOption{nott}{%
\renewcommand*{\zifour@default}{}%
}
% option [noupquote] to prevent loading upquote.
\DeclareOption{noupquote}{%
\zifour@noupqtrue}%
% option var0---use unslashed zero (slashed is default)
\DeclareOption{var0}{%
\zifour@altzerotrue\advance\zifour@ocount \tw@ %
}
\DeclareOption{varl}{%
\advance\zifour@ocount \@ne %
}
\DeclareOption{varqu}{%
\advance\zifour@ocount 4\relax %
}
\ProcessOptions*
\zifour@default
\edef\zifour@opt{\the\zifour@ocount}
\ifzifour@altzero
\advance\zifour@ocount -\tw@
\else
\advance\zifour@ocount \tw@
\fi
\edef\zifour@altopt{\the\zifour@ocount}
% define an \altzero macro which flips to slashed, unslashed
\def\altzero{{\fontfamily{zi4}%
\fontshape{scit}%
\selectfont 0}}
\def\zifour@T@ne@nc{T1}
\def\zifour@OT@ne@nc{OT1}
\def\zifour@LY@ne@nc{LY1}
\def\zifour@QX@nc{QX}
\def\zifour@TQS{%
\UndeclareTextCommand{\textquotesingle}{\encodingdefault}
\DeclareTextSymbol{\textquotesingle}{TS1}{39}}
\ifzifour@noupq% do nothing
% Try to correct for wrong slots for QX
\ifx\encodingdefault\zifour@QX@nc
\zifour@TQS
\else
\ifx\encodingdefault\zifour@LY@ne@nc
\zifour@TQS
\fi
\fi
\else
\AtBeginDocument{%
\ifx\encodingdefault\zifour@T@ne@nc % do nothing
\else
\ifx\encodingdefault\zifour@OT@ne@nc % do nothing
\else
\zifour@TQS
\fi
\fi
\usepackage{upquote}}
\fi
\endinput

17
docs/iclc2023-paper/make.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
if [ -d "$HOME/.cabal/bin" ] ; then
PATH="$HOME/.cabal/bin:$PATH"
fi
# --template=templates/template.latex \
pandoc -s demo.md \
--from markdown+auto_identifiers --pdf-engine=xelatex --template tex/latex-template.tex -V colorlinks --number-sections \
--citeproc --pdf-engine=xelatex \
--dpi=300 -o demo.pdf
pandoc -s demo.md --filter bin/code-filter.py \
--citeproc \
-t markdown-citations -t markdown-fenced_divs \
-o demo-preprocessed.md

View file

@ -0,0 +1,80 @@
$if(false)$
This is a pandoc template and should not be edited.
$endif$
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
<meta name="date" content="$date-meta$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
<style type="text/css">code{white-space: pre;}</style>
$if(quotes)$
<style type="text/css">q { quotes: "“" "”" "" ""; }</style>
$endif$
$if(highlighting-css)$
<style type="text/css">
$highlighting-css$
</style>
$endif$
<link rel="stylesheet" href="css/iclc.css" $if(html5)$$else$type="text/css" $endif$/>
$for(css)$
<link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>
$endfor$
$if(math)$
$math$
$endif$
$for(header-includes)$
$header-includes$
$endfor$
</head>
<body>
$for(include-before)$
$include-before$
$endfor$
$if(title)$
<div id="$idprefix$header">
<h1 class="title">$title$</h1>
$if(subtitle)$
<h1 class="subtitle">$subtitle$</h1>
$endif$
<ul id="authorlist">
$for(author)$
<li>$author$</li>
$endfor$
</ul>
$if(date)$
<h3 class="date">$date$</h3>
$endif$
</div>
$endif$
$if(toc)$
<div id="$idprefix$TOC">
$toc$
</div>
$endif$
<h2 class="abstract">Abstract</h2>
<div id="abstract">
$if(abstract)$
$abstract$
$else$
Please provide an abstract in the metadata block at the top of your
markdown document. Refer to template.txt for details.
$endif$
</div>
$body$
$for(include-after)$
$include-after$
$endfor$
</body>
</html>

View file

@ -0,0 +1,224 @@
$if(false)$
This is a pandoc template and should not be edited.
$endif$
\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$lang$,$endif$$if(papersize)$$papersize$,$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$}
\usepackage{pandoc/iclc}
$if(linestretch)$
\usepackage{setspace}
\setstretch{$linestretch$}
$endif$
\usepackage{amssymb,amsmath}
\usepackage{ifxetex,ifluatex}
\usepackage{fixltx2e} % provides \textsubscript
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
$if(fontfamily)$
\usepackage{$fontfamily$}
\usepackage{inconsolata}
$else$
\usepackage{lmodern}
$endif$
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
$if(euro)$
\usepackage{eurosym}
$endif$
\else % if luatex or xelatex
\ifxetex
\usepackage{mathspec}
\usepackage{xltxtra,xunicode}
\else
\usepackage{fontspec}
\fi
\defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase}
\newcommand{\euro}{€}
$if(mainfont)$
\setmainfont{$mainfont$}
$endif$
$if(sansfont)$
\setsansfont{$sansfont$}
$endif$
$if(monofont)$
\setmonofont[Mapping=tex-ansi]{$monofont$}
$endif$
$if(mathfont)$
\setmathfont(Digits,Latin,Greek){$mathfont$}
$endif$
\fi
% use upquote if available, for straight quotes in verbatim environments
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
% use microtype if available
\IfFileExists{microtype.sty}{%
\usepackage{microtype}
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
}{}
$if(geometry)$
\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
$endif$
$if(lang)$
\ifxetex
\usepackage{polyglossia}
\setmainlanguage{$mainlang$}
\else
\usepackage[shorthands=off,$lang$]{babel}
\fi
$endif$
$if(natbib)$
\usepackage{natbib}
\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$}
$endif$
$if(biblatex)$
\usepackage{biblatex}
$if(biblio-files)$
\bibliography{$biblio-files$}
$endif$
$endif$
$if(listings)$
\usepackage{listings}
$endif$
$if(lhs)$
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
$endif$
$if(highlighting-macros)$
$highlighting-macros$
$endif$
$if(verbatim-in-note)$
\usepackage{fancyvrb}
\VerbatimFootnotes
$endif$
$if(tables)$
\usepackage{longtable,booktabs}
$endif$
$if(csl-refs)$
\newlength{\cslhangindent}
\setlength{\cslhangindent}{1.5em}
\newenvironment{CSLReferences}%
{$if(csl-hanging-indent)$\setlength{\parindent}{0pt}%
\everypar{\setlength{\hangindent}{\cslhangindent}}\ignorespaces$endif$}%
{\par}
$endif$
$if(graphics)$
\usepackage{graphicx}
\makeatletter
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\makeatother
% Scale images if necessary, so that they will not overflow the page
% margins by default, and it is still possible to overwrite the defaults
% using explicit options in \includegraphics[width, height, ...]{}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
$endif$
\ifxetex
\usepackage[setpagesize=false, % page size defined by xetex
unicode=false, % unicode breaks when used with xetex
xetex]{hyperref}
\else
\usepackage[unicode=true]{hyperref}
\fi
\hypersetup{breaklinks=true,
bookmarks=true,
pdfauthor={$author-meta$},
pdftitle={$title-meta$},
colorlinks=true,
citecolor=$if(citecolor)$$citecolor$$else$blue$endif$,
urlcolor=$if(urlcolor)$$urlcolor$$else$blue$endif$,
linkcolor=$if(linkcolor)$$linkcolor$$else$magenta$endif$,
pdfborder={0 0 0}}
\urlstyle{same} % don't use monospace font for urls
$if(links-as-notes)$
% Make links footnotes instead of hotlinks:
\renewcommand{\href}[2]{#2\footnote{\url{#1}}}
$endif$
$if(strikeout)$
\usepackage[normalem]{ulem}
% avoid problems with \sout in headers with hyperref:
\pdfstringdefDisableCommands{\renewcommand{\sout}{}}
$endif$
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}
\setlength{\emergencystretch}{3em} % prevent overfull lines
\providecommand{\tightlist}{%/
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
$if(numbersections)$
\setcounter{secnumdepth}{5}
$else$
\setcounter{secnumdepth}{0}
$endif$
$if(verbatim-in-note)$
\VerbatimFootnotes % allows verbatim text in footnotes
$endif$
$if(title)$
\title{$title$$if(subtitle)$\\\vspace{0.5em}{\large $subtitle$}$endif$}
$endif$
$if(author)$
\author{
$for(author)$
$author.name$ \\
$author.affiliation$\\
\href{mailto:$author.email$}{$author.email$}
$sep$ \and
$endfor$
}
$endif$
\date{$date$}
$for(header-includes)$
$header-includes$
$endfor$
\begin{document}
$if(title)$
\maketitle
$endif$
\begin{abstract}
$if(abstract)$
$abstract$
$else$
Please provide an abstract in the metadata block at the top of the
markdown document. Refer to template.txt for details. $endif$
\end{abstract}
$for(include-before)$
$include-before$
$endfor$
$if(toc)$
{
\hypersetup{linkcolor=black}
\setcounter{tocdepth}{$toc-depth$}
\tableofcontents
}
$endif$
$if(lot)$
\listoftables
$endif$
$if(lof)$
\listoffigures
$endif$
$body$
$if(natbib)$
$if(biblio-files)$
$if(biblio-title)$
$if(book-class)$
\renewcommand\bibname{$biblio-title$}
$else$
\renewcommand\refname{$biblio-title$}
$endif$
$endif$
\bibliography{$biblio-files$}
$endif$
$endif$
$if(biblatex)$
\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$
$endif$
$for(include-after)$
$include-after$
$endfor$
\end{document}

View file

@ -0,0 +1,54 @@
\def\Hline{\noalign{\hrule height 0.4mm}}
%\newcommand{\bm}[1]{\mbox{\boldmath{$#1$}}}
\newcommand{\figbox}[1]{\fbox{\parbox{\columnwidth}{\centering{ #1 }}}}
\newcommand{\range}[2]{{#1,\cdots,#2\;}}
\newcommand{\secref}[1]{\mbox{Section~\ref{#1}}}
\newcommand{\tabref}[1]{\mbox{Table~\ref{#1}}}
\newcommand{\figref}[1]{\mbox{Figure~\ref{#1}}}
\newcommand{\eqnref}[1]{\mbox{Eq.~(\ref{#1})}}
\renewcommand{\sfdefault}{phv}
\renewcommand{\rmdefault}{ptm}
\renewcommand{\ttdefault}{pcr}
\setlength{\paperheight}{297mm}
\setlength{\paperwidth}{210mm}
\setlength{\textheight}{252mm}
\setlength{\textwidth}{172mm}
\setlength{\columnsep}{8mm}
\setlength{\headheight}{0mm}
\setlength{\voffset}{-12mm}
\setlength{\hoffset}{0mm}
\setlength{\marginparwidth}{0mm}
\setlength{\parindent}{2mm} %1pc
\setlength{\topmargin}{-5mm}
\setlength{\oddsidemargin}{-6mm}
\setlength{\evensidemargin}{-6mm}
\setlength\normallineskip{1\p@}
\setlength\parskip{0\p@ \@plus \p@}
%\def\baselinestretch{0.98}
\def\normalsize{\@setsize\normalsize{12pt}\xpt\@xpt}
\def\small{\@setsize\small{10pt}\ixpt\@ixpt}
\def\footnotesize{\@setsize\footnotesize{8pt}\viiipt\@viiipt}
\def\scriptsize{\@setsize\scriptsize{8pt}\viipt\@viipt}
\def\tiny{\@setsize\tiny{7pt}\vipt\@vipt}
\def\large{\@setsize\large{14pt}\xiipt\@xiipt}
\def\Large{\@setsize\Large{16pt}\xivpt\@xivpt}
\def\LARGE{\@setsize\LARGE{20pt}\xviipt\@xviipt}
\def\huge{\@setsize\huge{23pt}\xxpt\@xxpt}
\def\Huge{\@setsize\Huge{28pt}\xxvpt\@xxvpt}
\pagestyle{empty}
\def\abstract{
\begin{center}{
\bf ABSTRACT
}
\end{center}
}
\def\endabstract{\par}
\flushbottom

View file

@ -0,0 +1,349 @@
---
date: 2022-03-22
references:
- abstract: In this artist statement, I will discuss the tension between
source code as an interactive system for performers and source code
as information and entertainment for audiences in live-coding
performances. I then describe augmentations I developed for the
presentation of source code in the live-coding environment Gibber,
including animations and annotations that visually reveal aspects of
system state during performances. I briefly describe audience
responses to these techniques and, more importantly, how they are
critical to my own artistic practice.
accessed:
date-parts:
- - 2022
- 3
- 24
author:
- family: Roberts
given: Charles
container-title: International Journal of Performance Arts and Digital
Media
DOI: 10.1080/14794713.2016.1227602
id: "https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode_x61_rpdm20"
ISSN: 1479-4713
issue: 2
issued:
date-parts:
- - 2016
- 7
keyword: Live coding, psychology of programming, notation, audiences,
algorithms
page: 201-206
title: Code as information and code as spectacle
type: article-journal
URL: "https://doi.org/10.1080/14794713.2016.1227602"
volume: 12
- abstract: The TidalCycles (or Tidal for short) live coding environment
has been developed since around 2009, via several rewrites of its
core representation. Rather than having fixed goals, this
development has been guided by use, motivated by the open aim to
make music. This development process can be seen as a long-form
improvisation, with insights into the nature of Tidal gained through
the process of writing it, feeding back to guide the next steps of
development. This brings the worrying thought that key insights will
have been missed along this development journey, that would
otherwise have lead to very different software. Indeed participants
at beginners' workshops that I have lead or co-lead have often asked
questions without good answers, because they made deficiencies or
missing features in the software clear. It is well known that a
beginner's mind is able to see much that an expert has become blind
to. Running workshops are an excellent way to find new development
ideas, but the present paper explores a different technique -- the
rewrite.
accessed:
date-parts:
- - 2022
- 3
- 24
id: "https://zenodo.org/record/5788732"
issued:
date-parts:
- - 2021
- 12
keyword: live coding, algorithmic pattern, tidalcycles, haskell,
python
publisher-place: Valdivia, Chile
title: Alternate Timelines for TidalCycles
URL: "https://zenodo.org/record/5788732"
- abstract: A JavaScript dialect of its mini-notation for pattern is
created, enabling easy integration with creative coding tools and an
accompanying technique for visually annotating the playback of
TidalCycles patterns over time. TidalCycles has rapidly become the
most popular system for many styles of live coding performance, in
particular Algoraves. We created a JavaScript dialect of its
mini-notation for pattern, enabling easy integration with creative
coding tools. Our research pairs a formalism describing the
mini-notation with a small JavaScript library for generating events
over time; this library is suitable for generating events inside of
an AudioWorkletProcessor thread and for assisting with scheduling in
JavaScript environments more generally. We describe integrating the
library into the two live coding systems, Gibber and Hydra, and
discuss an accompanying technique for visually annotating the
playback of TidalCycles patterns over time.
accessed:
date-parts:
- - 2022
- 4
- 12
author:
- family: Roberts
given: Charles
container-title: www.semanticscholar.org
id: "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42"
issued:
date-parts:
- - 2019
title: Bringing the TidalCycles Mini-Notation to the Browser
URL: "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42"
title: Strudel
url2cite: all-links
---
# Introduction
This paper introduces Strudel, an alternative implementation of the
TidalCycles live coding system, using the JavaScript programming
language.
# Background
General motivations / related work. Reference vortex paper and summarise
its background.
The reimplementation of TidalCycles in Python (cite TidalVortex) showed
that it is possible to translate pure functional reactive programming
ideas to a multi paradigm language. It proved to be a stepping stone to
move to other multi-paradigm languages, like JavaScript. A significant
part of of the Python codebase could be ported to JavaScript by
syntactical adjustments.
# Introducing TidalStrudel
(do we want to call it TidalStrudel once, and Strudel for short from
then on as with vortex? Or just stick with Strudel? Should we start
calling TidalCycles just Cycles?? froos: I think TidalStrudel sounds a
bit weird, but we can stick to the TidalX naming scheme if that's
important. For me, StrudelCycles sounds better, because it has 3/4
phonems in common with TidalCycles)
- Motivating musical example
# Tidal patterns
(should we explain shortly what tidal patterns do in general here?)
The essence of TidalCycles are Patterns. Patterns are abstract entities
that represent flows of time. Taking a time span as its input, a Pattern
can output a set of events that happen within that time span. It depends
on the structure of the Pattern where the events are placed. From now
on, this process of generating events from a time span will be called
**querying**. Example:
<MiniRepl tune={`const pattern = sequence(c3, [e3, g3]);
const events = pattern.query(0, 1);
console.log(events.map(e => e.show()))`} />
In this example, we create a pattern using the `sequence` function and
**query** it for the timespan from `0` to `1`. Those numbers represent
units of time called **cycles**. The length of one cycle defaults to one
second, but could be any number of seconds. The console output looks
like this:
<MiniRepl tune={`(0 -> 1/2 c3)
(1/2 -> 3/4 e3)
(3/2 -> 1 g3)`} />
In this output, each line represents one event. The two fractions
represent the begin and end time of the event, followed by its value. In
this case, the events are placed in sequential order, where c3 takes the
first half, and e3 and g3 together take the second half. This temporal
placement is the result of the `sequence` function, which divides its
arguments equally over one cycle. If an argument is an array, the same
rule applies to that part of the sequence. In our example e3 and g3 are
divided equally over the second half of the whole sequence.
# Mini Notation
In this example, the Pattern is created using the `mini` function, which
parses Tidal's Mini Notation. The Mini Notation is a Domain Specific
Language (DSL) that allows expressing rhythms in a short mannger.
- Some comparisons of -Strudel with -Vortex and -Cycles code?
(the following examples are from vortex paper, with added js versions)
## 1
<MiniRepl tune={`sound "bd ~ [sd cp]"`} />
<MiniRepl tune={`sound("bd", silence, ["sd", "cp"])`} />
<MiniRepl tune={`sound("bd ~ [sd cp]")`} />
without mini notation:
<MiniRepl tune={`sound $ cat
[pure "bd", silence,
cat(pure "sd", pure "cp")]`} />
<MiniRepl tune={`sound('bd', silence, cat('sd', 'cp'))`} />
## 2
<MiniRepl tune={`sound "bd ~ <sd cp>"`} />
<MiniRepl tune={`sound("bd", silence, slowcat("sd", "cp"))`} />
<MiniRepl tune={`sound("bd ~ <sd cp>")
// sound('bd', silence, slowcat('sd', 'cp'))`} />
## 3
<MiniRepl tune={`sound "bd {cp sd, lt mt ht}"`} />
<MiniRepl tune={`sound("bd", pm(["cp", "sd"], ["lt", "mt", "ht"]))`} />
<MiniRepl tune={`?`} />
## 4
<MiniRepl tune={`sound "bd {cp sd, [lt mt,bd bd bd] ht}"`} />
<MiniRepl tune={` sound("bd", pm(["cp", "sd"],
[pr(["lt", "mt"],
["bd", "bd", "bd"]
),
"ht" ]))`} />
<MiniRepl tune={`??`} />
## 5
<MiniRepl tune={`sound "bd sd cp" # speed "1 2"`} />
<MiniRepl tune={`sound("bd", "sd", "cp") >> speed (1, 2)`} />
<MiniRepl tune={`sound("bd sd cp").speed("1 2")`} />
(operator overloading like in vortex?)
## 6
<MiniRepl tune={`rev $ sound "bd sd"`} />
<MiniRepl tune={`rev(sound("bd", "sd"))
sound("bd", "sd").rev()`} />
<MiniRepl tune={`rev(sound("bd sd"))
sound("bd sd").rev()`} />
## 7
<MiniRepl tune={`jux rev $ every 3 (fast 2) $ sound "bd sd"`} />
<MiniRepl tune={`jux(rev, every(3, fast(2), sound("bd", "sd")))
sound("bd","sd").every(3, fast(2)).jux(rev)`} />
<MiniRepl tune={`jux(rev, every(3, fast(2), sound("bd sd")))
sound("bd sd").every(3, fast(2)).jux(rev)`} />
(partial application)
## 8
<MiniRepl tune={`n ("1 2 3" + "4 5") # sound "drum"`} />
<MiniRepl tune={`n (sequence(1,2,3) + sequence(4,5)) >> sound "drum"`} />
<MiniRepl tune={`n("1 2 3".add("4 5")).sound("drum")
n("5 [6 7] 8").sound("drum")`} />
(operator overloading?)
## 9
<MiniRepl tune={`speed("1 2 3" + sine)`} />
<MiniRepl tune={`speed(sequence(1,2,3) + sine)`} />
<MiniRepl tune={`speed("1 2 3".add(sine))
"c3*4".add(sine.mul(12).slow(8)).pianoroll()`} />
## 10
- Mininotation
# Strudel/web specifics
Some discussion about whether strudel is really a port of TidalCycles,
or whether javascript affordances mean it's going its own way..
- Recursive Scheduling: "calling itself in the future"
- Optimizing Syntax for minimal keystrokes / readability: "AST
Hacking" via shift-ast pseudo variables
- Handling mininotation - double quoted and template strings to
mini calls
- Operator overloading
- Fixing inconsistencies (e.g. with stut/echo) adding source locations
- Dynamic HUD: Highlighting + drawing
- Translation of Tidal concepts to Javascript - different constraints,
affordances, aesthetics
- Dynamic Harmonic Programming?
- emulating musician thought patterns
- microtonal features? webserial
## User Code Transpilation
(compare user input vs shifted output)
### double quotes -\> mini calls
<MiniRepl tune={`"c3 e3" // or `c3 e3``} />
<MiniRepl tune={`mini("c3 e3")`} />
### operator overloading
<MiniRepl tune={`cat(c3, e3) * 4`} />
<MiniRepl tune={`reify(cat("c3","e3")).fast(4)`} />
(reify is redundant here, the shapeshifter could have an additional
check...)
(TBD: ability to multiply mini notation strings)
### pseudo variables
<MiniRepl tune={`cat(c3, r, e3)`} />
<MiniRepl tune={`cat("c3",silence,"e3")`} />
### locations
<MiniRepl tune={`cat(c3, e3)`} />
<MiniRepl tune={`cat(
reify("c3").withLocation([1,4,4],[1,6,6]),
reify("e3").withLocation([1,8,8],[1,10,10])
)`} />
<MiniRepl tune={`mini("c3 e3")`} />
with locations:
<MiniRepl tune={`// "c3 e3"
mini("c3 e3").withMiniLocation([1,0,0],[1,7,7])`} />
(talk about mini adding locations of mini notation parser)
### top level await
<MiniRepl tune={`const p = (await piano()).toDestination()
cat(c3).tone(p)`} />
<MiniRepl tune={`(async()=>{
const p = (await piano()).toDestination();
return cat("c3").tone(p);
})()`} />
# Musical examples
...
# Ongoing work/future aims
- WASM Sound Backend
- OSC -\> Supercollider
- mininotation as the 'regex' of metre
That
@https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode_x61_rpdm20
are excellent, I reference their work at least twice per sentence
[@https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode_x61_rpdm20,
p. 3]. Another reference [@https://zenodo.org/record/5788732].
<MiniRepl tune={`"1 2 3"`} />
# References
- gibber
- krill
- glicol

View file

@ -0,0 +1,354 @@
---
title: 'StrudelCycles: live coding algorithmic patterns on the web'
date: '2022-03-22'
url2cite: all-links
---
# Introduction
This paper introduces Strudel, an alternative implementation of the TidalCycles live coding system, using the JavaScript programming language.
# Background
TidalCycles (or *Tidal* for short) has been developed since around 2009, as a system for live coding algorithmic patterns, particularly in music [@tidalcycles]. Tidal is embedded in the pure functional *Haskell* programming language, taking advantage of its terse syntax and advanced type system. Over the past decade, Tidal has undergone a number of re-writes, developing a functional reactive representation of pattern, where patterns may be combined and transformed in a wide variety of ways [@alternate-timelines]. Over this time is has gained diverse ideas from other patterned forms, including from computer music [@spiegel], Indian classical music [@bel], textiles [@fabricating], improvised percussion [@hession], and Ancient Greek lyric [@cyclic-patterns].
Most recently, attention has turned to transferring Tidal's ideas to other, less 'pure' languages; firstly, to the Python programming language as *TidalVortex* [@tidalvortex] (*Vortex* for short), and now to JavaScript as StrudelCycles (*Strudel* for short), the topic of the present paper. For general background on the motivations for porting Tidal to a multi-paradigm programming language, please see the TidalVortex paper [@tidalvortex]. The motivations for porting it to JavaScript are similar, with a particular slanting on accessibility - of course, a web browser based application does not require any installation. As with Vortex though, it is important to point out that this is a creative, free/open source project, and as such, an primary motivation will always be developer's curiosity, and market-driven perspectives on development choices may even be demotivational.
General motivations / related work.
Reference vortex paper and summarise its background.
The reimplementation of TidalCycles in Python (cite TidalVortex) showed that it is possible to translate pure functional reactive programming ideas to a multi paradigm language. It proved to be a stepping stone to move to other multi-paradigm languages, like JavaScript. A significant part of of the Python codebase could be quickly ported to JavaScript by syntactical adjustments.
# Introducing Strudel
* Motivating musical example
# Tidal patterns
(should we explain shortly what tidal patterns do in general here?)
The essence of TidalCycles are Patterns. Patterns are abstract entities that represent flows of time, supporting both continuous changes (like signals) and discrete events (like notes).
Taking a time span as its input, a Pattern can output a set of events that happen within that time span.
It depends on the structure of the Pattern where the events are placed.
From now on, this process of generating events from a time span will be called **querying**.
Example:
```js
const pattern = sequence(c3, [e3, g3]);
const events = pattern.query(0, 1);
console.log(events.map(e => e.show()))
```
In this example, we create a pattern using the `sequence` function and **query** it for the timespan from `0` to `1`.
Those numbers represent units of time called **cycles**. The length of one cycle defaults to one second, but could be any number of seconds.
The console output looks like this:
```js
(0 -> 1/2 c3)
(1/2 -> 3/4 e3)
(3/2 -> 1 g3)
```
In this output, each line represents one event. The two fractions represent the begin and end time of the event, followed by its value.
In this case, the events are placed in sequential order, where c3 takes the first half, and e3 and g3 together take the second half.
This temporal placement is the result of the `sequence` function, which divides its arguments equally over one cycle.
If an argument is an array, the same rule applies to that part of the sequence. In our example e3 and g3 are divided equally over the second half of the whole sequence.
# Mini Notation
In this example, the Pattern is created using the `mini` function, which parses Tidal's Mini Notation.
The Mini Notation is a Domain Specific Language (DSL) that allows expressing rhythms in a short mannger.
* Some comparisons of -Strudel with -Vortex and -Cycles code?
(the following examples are from vortex paper, with added js versions)
## 1
```haskell
sound "bd ~ [sd cp]"
```
```python
sound("bd", silence, ["sd", "cp"])
```
```javascript
sound("bd ~ [sd cp]")
```
without mini notation:
```haskell
sound $ cat
[pure "bd", silence,
cat(pure "sd", pure "cp")]
```
```javascript
sound('bd', silence, cat('sd', 'cp'))
```
## 2
```haskell
sound "bd ~ <sd cp>"
```
```python
sound("bd", silence, slowcat("sd", "cp"))
```
```javascript
sound("bd ~ <sd cp>")
// sound('bd', silence, slowcat('sd', 'cp'))
```
## 3
```haskell
sound "bd {cp sd, lt mt ht}"
```
```python
sound("bd", pm(["cp", "sd"], ["lt", "mt", "ht"]))
```
```js
?
```
## 4
```haskell
sound "bd {cp sd, [lt mt,bd bd bd] ht}"
```
```python
sound("bd", pm(["cp", "sd"],
[pr(["lt", "mt"],
["bd", "bd", "bd"]
),
"ht" ]))
```
```js
??
```
## 5
```haskell
sound "bd sd cp" # speed "1 2"
```
```python
sound("bd", "sd", "cp") >> speed (1, 2)
```
```javascript
sound("bd sd cp").speed("1 2")
```
(operator overloading like in vortex?)
## 6
```haskell
rev $ sound "bd sd"
```
```python
rev(sound("bd", "sd"))
sound("bd", "sd").rev()
```
```javascript
rev(sound("bd sd"))
sound("bd sd").rev()
```
## 7
```haskell
jux rev $ every 3 (fast 2) $ sound "bd sd"
```
```python
jux(rev, every(3, fast(2), sound("bd", "sd")))
sound("bd","sd").every(3, fast(2)).jux(rev)
```
```js
jux(rev, every(3, fast(2), sound("bd sd")))
sound("bd sd").every(3, fast(2)).jux(rev)
```
(partial application)
## 8
```haskell
n ("1 2 3" + "4 5") # sound "drum"
```
```python
n (sequence(1,2,3) + sequence(4,5)) >> sound "drum"
```
```js
n("1 2 3".add("4 5")).sound("drum")
n("5 [6 7] 8").sound("drum")
```
(operator overloading?)
## 9
```haskell
speed("1 2 3" + sine)
```
```python
speed(sequence(1,2,3) + sine)
```
```js
speed("1 2 3".add(sine))
"c3*4".add(sine.mul(12).slow(8)).pianoroll()
```
## 10
* Mininotation
# Strudel/web specifics
Some discussion about whether strudel is really a port of TidalCycles, or whether javascript affordances mean it's going its own way..
* Recursive Scheduling: "calling itself in the future"
* Optimizing Syntax for minimal keystrokes / readability: "AST Hacking" via shift-ast
pseudo variables
* Handling mininotation - double quoted and template strings to mini calls
* Operator overloading
* Fixing inconsistencies (e.g. with stut/echo)
adding source locations
* Dynamic HUD: Highlighting + drawing
* Translation of Tidal concepts to Javascript - different constraints, affordances, aesthetics
* Dynamic Harmonic Programming?
* emulating musician thought patterns
* microtonal features?
webserial
## User Code Transpilation
(compare user input vs shifted output)
### double quotes -> mini calls
```javascript
"c3 e3" // or `c3 e3`
```
```javascript
mini("c3 e3")
```
### operator overloading
```javascript
cat(c3, e3) * 4
```
```javascript
reify(cat("c3","e3")).fast(4)
```
(reify is redundant here, the shapeshifter could have an additional check...)
(TBD: ability to multiply mini notation strings)
### pseudo variables
```javascript
cat(c3, r, e3)
```
```javascript
cat("c3",silence,"e3")
```
### locations
```javascript
cat(c3, e3)
```
```javascript
cat(
reify("c3").withLocation([1,4,4],[1,6,6]),
reify("e3").withLocation([1,8,8],[1,10,10])
)
```
```javascript
mini("c3 e3")
```
with locations:
```javascript
// "c3 e3"
mini("c3 e3").withMiniLocation([1,0,0],[1,7,7])
```
(talk about mini adding locations of mini notation parser)
### top level await
```javascript
const p = (await piano()).toDestination()
cat(c3).tone(p)
```
```javascript
(async()=>{
const p = (await piano()).toDestination();
return cat("c3").tone(p);
})()
```
# Musical examples
...
# Ongoing work/future aims
* WASM Sound Backend
* OSC -> Supercollider
* mininotation as the 'regex' of metre
That @roberts2016 are excellent, I reference their work at least twice per sentence [@roberts2016, p. 3].
```javascript
"1 2 3"
```
# References
[@roberts2016]: https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode=rpdm20
[@alternate-timelines]: https://zenodo.org/record/5788732
[@tidal.pegjs]: https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42
[@tidalvortex]: https://zenodo.org/record/6456380
[@ogborn17]: https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b
[@tidalcycles]: https://dl.acm.org/doi/10.1145/2633638.2633647
[@hession]: https://www.scopus.com/record/display.uri?eid=2-s2.0-84907386880&origin=inward&txGid=03307e26fba02a27bdc68bda462016f6266316467_Extending_Instruments_with_Live_Algorithms_in_a_Percussion_Code_Duo
[@spiegel]: https://www.academia.edu/664807/Manipulations_of_musical_patterns
[@bel]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.517.7129
[@algorithmicpattern]: https://zenodo.org/record/4299661
[@fabricating]: https://zenodo.org/record/2155745
[@cyclic-patterns]: https://zenodo.org/record/1548969
- gibber
- krill
- glicol

Binary file not shown.

View file

@ -0,0 +1,125 @@
\documentclass{tex/sig-alternate}
\usepackage[colorlinks = true,
linkcolor = blue,
urlcolor = blue,
citecolor = blue,
anchorcolor = blue]{hyperref}
\usepackage{fancyvrb}
\usepackage{xcolor}
\usepackage[utf8]{inputenc}
\newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}}
\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\BuiltInTok}[1]{#1}
\newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}}
\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}}
\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}}
\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}}
\newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
\newcommand{\ExtensionTok}[1]{#1}
\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}}
\newcommand{\ImportTok}[1]{#1}
\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
\newcommand{\NormalTok}[1]{#1}
\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}}
\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}}
\newcommand{\RegionMarkerTok}[1]{#1}
\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}}
\newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}}
\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\setlength{\emergencystretch}{3em} % prevent overfull lines
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\setcounter{secnumdepth}{5}
\newlength{\cslhangindent}
\setlength{\cslhangindent}{1.5em}
\newlength{\csllabelwidth}
\setlength{\csllabelwidth}{3em}
\newlength{\cslentryspacingunit} % times entry-spacing
\setlength{\cslentryspacingunit}{\parskip}
\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing
{% don't indent paragraphs
\setlength{\parindent}{0pt}
% turn on hanging indent if param 1 is 1
\ifodd #1
\let\oldpar\par
\def\par{\hangindent=\cslhangindent\oldpar}
\fi
% set entry spacing
\setlength{\parskip}{#2\cslentryspacingunit}
}%
{}
\usepackage{calc}
\newcommand{\CSLBlock}[1]{#1\hfill\break}
\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}}
\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break}
\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1}
\setlength{\paperheight}{11in}
\setlength{\paperwidth}{8.5in}
\usepackage[
pass,% keep layout unchanged
% showframe,% show the layout
]{geometry}
\newenvironment{Shaded}{}{}
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
% Add ',fontsize=\small' for more characters per line
\makeatletter
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\makeatother
% Scale images if necessary, so that they will not overflow the page
% margins by default, and it is still possible to overwrite the defaults
% using explicit options in \includegraphics[width, height, ...]{}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
\begin{document}
\setcopyright{waclicense}
\conferenceinfo{Web Audio Conference WAC-2022,}{July 6--8, 2022, Cannes, France.}
\CopyrightYear{2022}
\title{Strudel: Algorithmic Patterns for the Web}
\numberofauthors{2}
\author{
\alignauthor
\name{Felix Roos}
\affaddr{Lembach, France}
\email{flix91@gmail.com}
% 2nd. author
\alignauthor
\name{Alex McLean}
\affaddr{Then Try This\\ Sheffield/Penryn, UK}
\email{alex@slab.org}
}
\maketitle
\begin{sloppypar}
%\begin{abstract}
%Abstract goes here (find me in the latex template)
%\end{abstract}
\end{sloppypar}
$body$
\end{document}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,229 @@
%%
%% This is file `acmcopyright.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% acmcopyright.dtx (with options: `style')
%%
%% IMPORTANT NOTICE:
%%
%% For the copyright see the source file.
%%
%% Any modified versions of this file must be renamed
%% with new filenames distinct from acmcopyright.sty.
%%
%% For distribution of the original source see the terms
%% for copying and modification in the file acmcopyright.dtx.
%%
%% This generated file may be distributed as long as the
%% original source files, as listed above, are part of the
%% same distribution. (The sources need not necessarily be
%% in the same archive or directory.)
%% \CharacterTable
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%% Digits \0\1\2\3\4\5\6\7\8\9
%% Exclamation \! Double quote \" Hash (number) \#
%% Dollar \$ Percent \% Ampersand \&
%% Acute accent \' Left paren \( Right paren \)
%% Asterisk \* Plus \+ Comma \,
%% Minus \- Point \. Solidus \/
%% Colon \: Semicolon \; Less than \<
%% Equals \= Greater than \> Question mark \?
%% Commercial at \@ Left bracket \[ Backslash \\
%% Right bracket \] Circumflex \^ Underscore \_
%% Grave accent \` Left brace \{ Vertical bar \|
%% Right brace \} Tilde \~}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{waccopyright}
[2014/06/29 v1.2 Copyright statements for ACM classes]
\newif\if@printcopyright
\@printcopyrighttrue
\newif\if@printpermission
\@printpermissiontrue
\newif\if@acmowned
\@acmownedtrue
\RequirePackage{xkeyval}
\define@choicekey*{ACM@}{acmcopyrightmode}[%
\acm@copyrightinput\acm@copyrightmode]{none,acmcopyright,acmlicensed,%
rightsretained,usgov,usgovmixed,cagov,cagovmixed,%
licensedusgovmixed,licensedcagovmixed,othergov,licensedothergov,waclicense}{%
\@printpermissiontrue
\@printcopyrighttrue
\@acmownedtrue
\ifnum\acm@copyrightmode=0\relax % none
\@printpermissionfalse
\@printcopyrightfalse
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=2\relax % acmlicensed
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=3\relax % rightsretained
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=4\relax % usgov
\@printpermissiontrue
\@printcopyrightfalse
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=6\relax % cagov
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=8\relax % licensedusgovmixed
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=9\relax % licensedcagovmixed
\@acmownedfalse
\fi
\ifnum\acm@copyrightmode=10\relax % othergov
\@acmownedtrue
\fi
\ifnum\acm@copyrightmode=11\relax % licensedothergov
\@acmownedfalse
\@printcopyrightfalse
\fi
\ifnum\acm@copyrightmode=12\relax % waclicense
\@acmownedfalse
\fi}
\def\setcopyright#1{\setkeys{ACM@}{acmcopyrightmode=#1}}
\setcopyright{acmcopyright}
\def\@copyrightowner{%
\ifcase\acm@copyrightmode\relax % none
\or % acmcopyright
ACM.
\or % acmlicensed
Copyright held by the owner/author(s). Publication rights not licensed to
ACM.
\or % rightsretained
Felix Roos and Alex McLean.
\or % usgov
\or % usgovmixed
ACM.
\or % cagov
Crown in Right of Canada.
\or %cagovmixed
ACM.
\or %licensedusgovmixed
Copyright held by the owner/author(s). Publication rights licensed to
ACM.
\or %licensedcagovmixed
Copyright held by the owner/author(s). Publication rights licensed to
ACM.
\or % othergov
ACM.
\or % licensedothergov
\or % waclicense
Felix Roos and Alex McLean.
\fi}
\def\@copyrightpermission{%
\ifcase\acm@copyrightmode\relax % none
\or % acmcopyright
Permission to make digital or hard copies of all or part of this
work for personal or classroom use is granted without fee provided
that copies are not made or distributed for profit or commercial
advantage and that copies bear this notice and the full citation on
the first page. Copyrights for components of this work owned by
others than ACM must be honored. Abstracting with credit is
permitted. To copy otherwise, or republish, to post on servers or to
redistribute to lists, requires prior specific permission
and\hspace*{.5pt}/or a fee. Request permissions from
permissions@acm.org.
\or % acmlicensed
Permission to make digital or hard copies of all or part of this
work for personal or classroom use is granted without fee provided
that copies are not made or distributed for profit or commercial
advantage and that copies bear this notice and the full citation on
the first page. Copyrights for components of this work owned by
others than the author(s) must be honored. Abstracting with credit
is permitted. To copy otherwise, or republish, to post on servers
or to redistribute to lists, requires prior specific permission
and\hspace*{.5pt}/or a fee. Request permissions from
permissions@acm.org.
\or % rightsretained
Permission to make digital or hard copies of part or all of this work
for personal or classroom use is granted without fee provided that
copies are not made or distributed for profit or commercial advantage
and that copies bear this notice and the full citation on the first
page. Copyrights for third-party components of this work must be
honored. For all other uses, contact the
owner\hspace*{.5pt}/author(s).
\or % usgov
This paper is authored by an employee(s) of the United States
Government and is in the public domain. Non-exclusive copying or
redistribution is allowed, provided that the article citation is
given and the authors and agency are clearly identified as its
source.
\or % usgovmixed
ACM acknowledges that this contribution was authored or co-authored
by an employee, or contractor of the national government. As such,
the Government retains a nonexclusive, royalty-free right to
publish or reproduce this article, or to allow others to do so, for
Government purposes only. Permission to make digital or hard copies
for personal or classroom use is granted. Copies must bear this
notice and the full citation on the first page. Copyrights for
components of this work owned by others than ACM must be
honored. To copy otherwise, distribute, republish, or post,
requires prior specific permission and\hspace*{.5pt}/or a
fee. Request permissions from permissions@acm.org.
\or % cagov
This article was authored by employees of the Government of Canada.
As such, the Canadian government retains all interest in the
copyright to this work and grants to ACM a nonexclusive,
royalty-free right to publish or reproduce this article, or to allow
others to do so, provided that clear attribution is given both to
the authors and the Canadian government agency employing them.
Permission to make digital or hard copies for personal or classroom
use is granted. Copies must bear this notice and the full citation
on the first page. Copyrights for components of this work owned by
others than the Canadain Government must be honored. To copy
otherwise, distribute, republish, or post, requires prior specific
permission and\hspace*{.5pt}/or a fee. Request permissions from
permissions@acm.org.
\or % cagovmixed
ACM acknowledges that this contribution was co-authored by an
affiliate of the national government of Canada. As such, the Crown
in Right of Canada retains an equal interest in the copyright.
Reprints must include clear attribution to ACM and the author's
government agency affiliation. Permission to make digital or hard
copies for personal or classroom use is granted. Copies must bear
this notice and the full citation on the first page. Copyrights for
components of this work owned by others than ACM must be honored.
To copy otherwise, distribute, republish, or post, requires prior
specific permission and\hspace*{.5pt}/or a fee. Request permissions
from permissions@acm.org.
\or % licensedusgovmixed
Publication rights licensed to ACM. ACM acknowledges that this
contribution was authored or co-authored by an employee, contractor
or affiliate of the United States government. As such, the
Government retains a nonexclusive, royalty-free right to publish or
reproduce this article, or to allow others to do so, for Government
purposes only.
\or % licensedcagovmixed
Publication rights licensed to ACM. ACM acknowledges that this
contribution was authored or co-authored by an employee, contractor
or affiliate of the national government of Canada. As such, the
Government retains a nonexclusive, royalty-free right to publish or
reproduce this article, or to allow others to do so, for Government
purposes only.
\or % othergov
ACM acknowledges that this contribution was authored or co-authored
by an employee, contractor or affiliate of a national government. As
such, the Government retains a nonexclusive, royalty-free right to
publish or reproduce this article, or to allow others to do so, for
Government purposes only.
\or % licensedothergov
Publication rights licensed to ACM. ACM acknowledges that this
contribution was authored or co-authored by an employee, contractor
or affiliate of a national government. As such, the Government
retains a nonexclusive, royalty-free right to publish or reproduce
this article, or to allow others to do so, for Government purposes
only.
\or % waclicense
\frame{\includegraphics[scale=.54]{images/cc}}\vspace{1mm}\vfill
Licensed under a Creative Commons Attribution 4.0 International License (CC BY 4.0). \textbf{Attribution}: Felix Roos and Alex McLean.
\fi}
\endinput
%%
%% End of file `acmcopyright.sty'.