strudel/paper/paper.md
2022-04-14 20:55:07 +01:00

9.3 KiB

title, date, url2cite
title date url2cite
StrudelCycles: live coding algorithmic patterns on the web 2022-03-22 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:

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:

(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

sound "bd ~ [sd cp]"
sound("bd", silence, ["sd", "cp"])
sound("bd ~ [sd cp]")

without mini notation:

sound $ cat
   [pure "bd", silence,
    cat(pure "sd", pure "cp")]
sound('bd', silence, cat('sd', 'cp'))

2

sound "bd ~ <sd cp>"
sound("bd", silence, slowcat("sd", "cp"))
sound("bd ~ <sd cp>")
// sound('bd', silence, slowcat('sd', 'cp'))

3

sound "bd {cp sd, lt mt ht}"
sound("bd", pm(["cp", "sd"], ["lt", "mt", "ht"]))
?

4

sound "bd {cp sd, [lt mt,bd bd bd] ht}"
 sound("bd", pm(["cp", "sd"],
 [pr(["lt", "mt"],
 ["bd", "bd", "bd"]
 ),
 "ht" ]))
??

5

sound "bd sd cp" # speed "1 2"
sound("bd", "sd", "cp") >> speed (1, 2)
sound("bd sd cp").speed("1 2")

(operator overloading like in vortex?)

6

rev $ sound "bd sd"
rev(sound("bd", "sd"))
sound("bd", "sd").rev()
rev(sound("bd sd"))
sound("bd sd").rev()

7

jux rev $ every 3 (fast 2) $ sound "bd sd"
jux(rev, every(3, fast(2), sound("bd", "sd")))
sound("bd","sd").every(3, fast(2)).jux(rev)
jux(rev, every(3, fast(2), sound("bd sd")))
sound("bd sd").every(3, fast(2)).jux(rev)

(partial application)

8

n ("1 2 3" + "4 5") # sound "drum"
n (sequence(1,2,3) + sequence(4,5)) >> sound "drum"
n("1 2 3".add("4 5")).sound("drum")
n("5 [6 7] 8").sound("drum")

(operator overloading?)

9

speed("1 2 3" + sine)
speed(sequence(1,2,3) + sine)
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

"c3 e3" // or `c3 e3`
mini("c3 e3")

operator overloading

cat(c3, e3) * 4
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

cat(c3, r, e3)
cat("c3",silence,"e3")

locations

cat(c3, e3)
cat(
  reify("c3").withLocation([1,4,4],[1,6,6]),
  reify("e3").withLocation([1,8,8],[1,10,10])
)
mini("c3 e3")

with locations:

// "c3 e3"
mini("c3 e3").withMiniLocation([1,0,0],[1,7,7])

(talk about mini adding locations of mini notation parser)

top level await

const p = (await piano()).toDestination()
cat(c3).tone(p)
(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].

"1 2 3"

References

  • gibber
  • krill
  • glicol