[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)