[HN Gopher] Tonal.js: Functional music theory lib
       ___________________________________________________________________
        
       Tonal.js: Functional music theory lib
        
       Author : brianzelip
       Score  : 252 points
       Date   : 2024-04-02 10:35 UTC (2 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | jarmitage wrote:
       | Used in https://strudel.cc (TidalCycles' JavaScript version)
       | 
       | Specifically https://strudel.cc/learn/tonal/
        
       | hasty_pudding wrote:
       | "all functions are pure, there is no data mutation, and entities
       | are represented by data structures instead of objects."
       | 
       | God i love to see this
        
         | recursive wrote:
         | In javascript, what data structure can you implement without
         | objects? Pretty much nothing. Even arrays are objects.
        
           | metaxy2 wrote:
           | They're using objects, including arrays [1], so I think they
           | would have been more precise to say that they're using
           | objects and arrays as simple data structures rather than
           | using OO features like inheritance and mutation.
           | 
           | [1] https://github.com/tonaljs/tonal/blob/main/packages/chord
           | /in...
        
             | pcthrowaway wrote:
             | This is just semantics then. If you're implementing a
             | linked list or a tree without a class (or at the very least
             | a function that behaves like a class), it's going to be
             | awkward and clumsy.
             | 
             | Data-structures beyond what the standard lib gives you
             | (string and array) are the main reason the addition of
             | classes to JS was so helpful.
        
               | eitacaraleo wrote:
               | Uh... what? It's quite the opposite; why would you need a
               | class for linked lists and trees?
        
               | hasty_pudding wrote:
               | You definitely need generic node objects to define the
               | nodes.
               | 
               | You could have a completely stateless class/object that
               | would manipulate those nodes and add them and remove them
               | from the data structure.
        
               | metaxy2 wrote:
               | Well, in principle you could have the node objects be
               | created with an object literal in an outside builder
               | function, and the manipulation also be done by outside
               | functions. That's how it's done in languages without OOP.
               | The question is how far are you willing to go to avoid
               | doing anything that looks like OOP.
        
               | hasty_pudding wrote:
               | Very far
        
               | recursive wrote:
               | To provide an easy place to find the behaviors that are
               | included with those data structures.
        
               | kamov wrote:
               | This isn't quite right, linked lists and trees in
               | functional languages can be represented extremely
               | succintly, i.e.                 data List a = Nil | Node
               | a (List a)       data Tree a = Leaf a | Node (Tree a)
               | (Tree a)
               | 
               | there is nothing stopping you from writing something like
               | this in JS                 const list = { kind: 'node',
               | val: 3, next: { kind: 'nil' } };
               | 
               | And then using a switch case to navigate the list/tree
               | matching on the kind field.
        
               | recursive wrote:
               | Absolutely. The navigation behaviors will have to be in
               | functions though. And from an application code
               | developer's perspective, the connection between these
               | objects and functions may not be obvious. If only there
               | were some way to associate behavior with state.
        
               | lioeters wrote:
               | The whole point of the top comment is that they
               | appreciate how this library chose _not_ to associate
               | behavior with state, and instead organized things as
               | functions and data structures.
               | 
               | With the latter approach, the user is not limited to the
               | bahaviors that the library author wrote. The data (not
               | state) can be used freely. Similarly, the functions are
               | not tied to any particular state - the data can come from
               | anywhere, as long as it has the right shape.
        
           | hasty_pudding wrote:
           | What they mean is functions and data are separate, emulating
           | a more functional paradigm and less mutable state.
           | 
           | The functions are kept pure and the data is kept pure.
           | 
           | As opposed to an object oriented programming style in which
           | data and functions share the same object and create bug
           | ridden state machines thats not as easy to reason about.
           | 
           | Some further explanation about this:
           | 
           | https://www.goodreads.com/quotes/702062-immutable-objects-
           | ar...
           | 
           | a semi-nuanced discussion:
           | 
           | https://www.reddit.com/r/scala/comments/ejwsp1/is_mutability.
           | ..
           | 
           | you can just Google mutable state to find more info.
           | 
           | separating your data structures and functions in many cases
           | will make your code cleaner and more bug free.
        
             | recursive wrote:
             | I'm well aware of the pros and cons of mutability. I object
             | (hah!) to this novel new use of the word "object". The
             | concept is well defined by the ECMAScript spec.
             | 
             | Your first link's first sentence's first two words are
             | "Immutable objects". No problem there. It doesn't conflate
             | objects with mutability.
        
               | hasty_pudding wrote:
               | you know how unsufferable it is when people bog a
               | conversation down with semantics instead of ideas.
               | 
               | the point is obvious.
               | 
               | Being that every single language on Earth uses objects
               | for non-primitive data structures that should be
               | inferrable to a reasonable person.
               | 
               | a data structure in that context is clearly an ordered
               | grouping of objects versus an unordered/loose grouping of
               | objects.
        
               | recursive wrote:
               | I mean some languages don't.
               | 
               | I have ideas but I don't think I'm getting them through.
               | Most of the problems functional proselytizers have with
               | objects come from inheritance and mutability. Instance
               | methods from classes don't seem to conflict with any of
               | the functional tenets.
               | 
               | As for mutability, I think it's good sometimes. Dates
               | should have been immutable, but Maps are a good fit for
               | mutation. Immutable maps might make sense too sometimes.
               | 
               | But I find it difficult to communicate about any of this
               | when fundamental terminology is used in novel ways.
        
               | hasty_pudding wrote:
               | what languages don't use objects to implement non-
               | primitive data structures?
               | 
               | like a binary tree for example
        
               | recursive wrote:
               | C# allows you to implement some data structures using
               | `struct`s instead of `object`s. Probably not binary tree
               | though.
               | 
               | I honestly still don't understand what you're getting at.
               | 
               | > a data structure in that context is clearly an ordered
               | grouping of objects versus an unordered/loose grouping of
               | objects.
               | 
               | What's an "ordered grouping"? What does "loose" mean?
               | These are not gotchas. I'm trying my best here.
               | 
               | For me, if I were implementing a data structure, I would
               | probably use objects. In fact, I'd probably use a class.
               | I'm not trying to argue that data structures shouldn't be
               | implemented in terms of objects or anything. My point is
               | just that the tonal.js description is kind of
               | nonsensical. Or at least hard to understand.
        
           | hasty_pudding wrote:
           | What languages on Earth implement non-primitive data
           | structures without objects?
           | 
           | I guess maybe low level c and assembly where you implement
           | everything as bits in physical memory?
        
             | recursive wrote:
             | It's mostly a question of what the language considers to be
             | an object.
             | 
             | There's often a stack-/heap-allocated dichotomy. Some
             | languages consider stack-allocated structs not to be
             | objects. They have no reference identity. Assignment has
             | copy semantics. C# is one such language.
        
       | smrtinsert wrote:
       | Wonderful. Wanted something like this forever. Glad it exists
        
         | helpfulContrib wrote:
         | I wonder if there is a similar library in C++?
        
           | diggan wrote:
           | I'm not aware of any, but you could probably compile it to
           | WebAssembly with Emscripten and use it that way, the library
           | seems fairly simple for something like that.
        
       | duranduran wrote:
       | Tonal is great! I use it extensively here: https://app.bars.ai
       | 
       | There are a few small areas where the library hiccups, like with
       | bigger chords. I've been able to work around some of those things
       | by reframing the chord as an inversion of N relative to the root
       | of whatever scale I'm in. I haven't bothered to debug why it does
       | that, but my little work around has been sufficient.
        
         | CodeIsTheEnd wrote:
         | Unsolicited feedback because I clicked: - Don't require picking
         | a composition name before getting started - Whatever scroll
         | settings you have broke swipe to go back, and then when I
         | tapped the back button I got stuck in an infinite loop between
         | the home page and the create composition page
        
           | duranduran wrote:
           | Thanks for testing it out! You're the third person to give me
           | that feedback on the composition name on launch. I'll delete
           | that step.
           | 
           | On back navigation, that's really bad bug! I'll repro it and
           | fix. Thanks for finding that!
        
       | unikeko wrote:
       | this initially confused me; you might not be aware, but
       | "functional" is a bit overloaded term with regard to music
       | theory, see: functional harmony
        
         | eyelidlessness wrote:
         | I mean, if the README is representative, "functional" is _also_
         | appropriate in terms of the programming paradigm.
        
           | systoll wrote:
           | It's mostly about the programming paradigm.
           | 
           | @tonaljs/key gives you some specifically functional info, but
           | the vast bulk of it as useful for non-functional analysis -
           | though it does assume 12-tone equal temperament.
        
         | seanhunter wrote:
         | For people who are not aware, "functional harmony" means the
         | extended system of tonal harmony that you hear in basically
         | almost all music apart from a very few exceptions (eg free
         | atonality, serialism etc but there aren't many compared to the
         | very large amount of functional harmony out there).
         | 
         | It's "functional" because the chords in the harmony have the
         | function of establishing key centres, moving towards and away
         | from them, establishing transitions to remote key areas etc.
         | 
         | One of the best technical books about functional harmony is
         | "Structural Functions of Harmony" by Arnold Schoenberg[1],
         | which is much shorter than his "Harmonielehre" but really
         | packed with interesting detail about key and chord
         | relationships.
         | 
         | [1] Yes that Schoenberg but this isn't about serialism, it's
         | about trying to explain all the harmony up to and including
         | Mahler. Here is an online pdf of an early edition
         | https://is.muni.cz/el/1421/podzim2007/VH_53/Schoenberg_Struc...
        
           | mrweiner wrote:
           | Small correction -- almost all *western* music.
        
             | seanhunter wrote:
             | True. Almost all non-western music also has functional
             | harmony though. The notable exceptions are things like
             | Indian classical music and things like gamelan (which have
             | static modal harmony).
        
       | hannibalekta wrote:
       | Im working on a music learning app to study jazz licks and this
       | is exactly what I needed. Thanks!
        
       | grimmdude wrote:
       | I love Tonal.js, have used it for many personal projects
       | including https://github.com/grimmdude/MidiWriterJS
        
       | demondemidi wrote:
       | Surprised it doesn't account for tempering given it is so
       | comprehensive.
        
         | giraffe_lady wrote:
         | I don't think it matters at this abstraction layer? You reason
         | about intervals, scales, chords etc the same regardless of the
         | tuning system. That only matters for sound production, which
         | this doesn't do.
         | 
         | It definitely assumes you're on a 12-tone system so is really
         | only useful within western european music theory. But that is
         | usually what people mean when they say "music theory" anyway.
        
           | hunter2_ wrote:
           | It matters for just one tiny aspect of the library:
           | Note.freq(), Note.fromFreq(), etc.
        
         | zengid wrote:
         | you might be interested in something like tune.js
         | https://github.com/instrumentbible/tune.js
        
       | cushpush wrote:
       | Can I set the tuning to A=432Hz ?
        
         | ysofunny wrote:
         | yes, it doesn't really matter at all for this kind of system
         | 
         | you can set it to zero, to negative one... possibly to the
         | imaginary constant
         | 
         | because this library does not actually make any sound. it's (to
         | me) an implementation of 12tet, the standard western scale
         | system which includes a bunch of scales and approximates the
         | rest 'well' enough
        
           | hunter2_ wrote:
           | I don't mean to lose my 'temper' here, but I think gp is
           | hoping Note.freq(), Note.fromFreq(), etc. aren't locked into
           | A440 as the examples imply. While >99% of the library doesn't
           | deal with this at all, it would be handy nonetheless.
           | Unfortunately, it's currently hard coded:
           | 
           | https://github.com/tonaljs/tonal/blob/main/packages%2Fpitch-.
           | ..
           | 
           | https://github.com/tonaljs/tonal/blob/main/packages%2Fmidi%2.
           | ..
        
           | altruios wrote:
           | Imaginary constant... complex tuning here we come!
           | 
           | The ideal tuning isn't 440hz, it's (440+42i)hz /s
        
             | ysofunny wrote:
             | up there with negative probability frequencies
        
             | selenography wrote:
             | Isn't that just 442Hz, except about 34.3ms early?
        
               | altruios wrote:
               | Would the delay vary by pitch? (or would i also have to
               | increase for that to be the case)
        
       | Freedom2 wrote:
       | Thanks for this! I've recently been exploring how to write a SATB
       | four part harmony for a given melody / bass line, this takes a
       | decent chunk of the boilerplate out for me.
        
       | BHSPitMonkey wrote:
       | Since we're all sharing, I've been using this recently to replace
       | Teoria in an old project of mine (a simple PWA for Duolingo-style
       | sight reading and ear training practice, with MIDI support):
       | 
       | https://www.getprelude.net/
        
       | kerneloops wrote:
       | See also: Well-typed music does not sound wrong (experience
       | report), Haskell 2017
       | 
       | https://dl.acm.org/doi/10.1145/3122955.3122964
        
       | justAnotherHero wrote:
       | I am currently working on an app with React Native that helps
       | users play along to songs and one of the features needed was
       | displaying the various scales/modes on the fretboard so I
       | basically built a mini library of my own to construct the note
       | arrays.
       | 
       | This will be a great example to see how the library has handled
       | the same problem.
        
       | lodovic wrote:
       | This is really nice! I am writing a guitar neck explorer tool to
       | learn the patterns on the guitar neck. As I progress, I need to
       | implement a lot more music theory than I really want to, so I was
       | already looking around for something to integrate. This library
       | will come in very handy. Nice work!
        
       | iamsaitam wrote:
       | (shameless plug) If you're interested in something more focused
       | on creating music with tools for generating melodies, chord
       | progressions, drum beats, etc., check out PLAYA at
       | https://github.com/ricardomatias/playa
        
         | matteason wrote:
         | Thanks for posting this! I've been experimenting with adding
         | generative ambient music to my white noise app Ambiphone [0]
         | using tone.js, but I've just been using manually written
         | progressions and motifs, so I'm really excited to try this out.
         | 
         | Just a heads up that your example at
         | https://observablehq.com/@ricardomatias/playa doesn't seem to
         | be working at the moment because of browser autoplay
         | restrictions - the console errors that look relevant are:
         | 
         | > An AudioContext was prevented from starting automatically. It
         | must be created or resumed after a user gesture on the page.
         | 
         | > The AudioContext is "suspended". Invoke Tone.start() from a
         | user action to start the audio.
         | 
         | [0] https://ambiph.one
        
       | cess11 wrote:
       | Looks nice. For silly exploratory projects I've previously used
       | p5.sound: https://p5js.org/reference/#/libraries/p5.sound
        
       | dr_dshiv wrote:
       | How is it different from tone.js?
        
         | matteason wrote:
         | Tone.js is a framework to actually play audio/music. Tonal.js
         | deals with music theory but not actually turning that into
         | audio
        
       | jjarvis wrote:
       | Max/Msp supports node.js. This would be very useful in a max
       | patch.
        
       | beaugunderson wrote:
       | i love this library; i used it to write a pedal steel guitar
       | visualizer:
       | 
       | https://beaugunderson.com/pedal-steel/
       | 
       | try highlight - scale degree colors or highlight - chord formula
       | and put "1 3 5 7" in the chord formula box... then play with the
       | pedal checkboxes to see how you can play that chord vertically,
       | or more stylistically by using the pedal slides
        
       ___________________________________________________________________
       (page generated 2024-04-04 23:02 UTC)