Tutorial: Using the JavaScript Live API in Max for Live

    Max For Live

    Adam Murray's icon
    Adam Murray's icon
    Adam Murray
    Jun 22 2014 | 1:43 am
    I've been writing a series of articles that explain how to hack on Ableton Live with JavaScript through Max for Live and the Live API.
    After some initial groundwork, it takes you through building a Live device to "humanize" (randomize) the timing and velocity of notes in a MIDI clip.
    I hope this is useful to someone!
    http://compusition.com/writings/js-live-api
    I'm still writing the last article which is just some miscellaneous advanced topics. Not sure when that will be done. Let me know if you have any suggestions or would like to see other topics covered in the future.
    Cheers, Adam

    • mvf's icon
      mvf's icon
      mvf
      Jun 22 2014 | 10:27 am
      Thank you Adam! A very helpful tutorial.
      Share
    • endless's icon
      endless's icon
      endless
      Jun 22 2014 | 1:08 pm
      this is awesome, thanks!!
    • do.while's icon
      do.while's icon
      do.while
      Jun 22 2014 | 5:36 pm
      thats great Adam , fantastic work .
      considering one of the covered topics (Note I/O). Is it safe to manipulate note placement within triplet time grid ? Im asking because i came to the point where i had to deal with the float precision issue due to decimal fractions required for triplets inside bar/time grid .
      Lets say that im in need to grab and duplicate a note that has length of a triplet duration , or even duplicate a note of fixed/whole duration but from triplet position . or move .
      In such cases im dealing with decimal offests that doesnt make me feel safe enough . Also reading positions of triplets from piano-roll doesnt give the same fraction results on every triplet (tried toFixed(100)) which is quite logic as we are dealing with just the "computer" anyway .
      I gave up on this because i had to introduce some sort of inaccuracy/error threshold which was actually working rather as a psychological aspect than any sort of cure . Do u have some experiences with such scenarios ?
    • Adam Murray's icon
      Adam Murray's icon
      Adam Murray
      Jun 23 2014 | 3:25 am
      @DO...WHILE: No, I haven't spent much time generating notes in a triplet grid. And you are right, you can run into issues with floating point numbers not being accurate enough.
      The accuracy of floating point is better for low values. So 1/3 is going to be a lot more accurate than, say, 100000000000000/3. So I imagine that things are good enough to work with the triplet grid in Max for Live and JavaScript. Probably any inaccuracies are going to be so small you wouldn't even be able to tell when you are listening to the music.
      One area where I think you could run into trouble is if you are generating repeated legato notes. For example, if a note with the same pitch starts every 1/3 of a beat and is also 1/3 of a beat in duration. It seems possible the inaccuracies in floating point could cause the end of one note to overlap the beginning of another, causing a note to be lost. If this were to happen, I would just make the duration slightly less than 1/3 of a beat, so it still seems workable to me.
      Another thing to consider is you can set the clip quantization value and quantize the clip via the Live API, so you could generate some notes in JavaScript and then quantize them.
    • do.while's icon
      do.while's icon
      do.while
      Jun 23 2014 | 8:47 am
      Hi Adam ,thanks for the response !
      Probably any inaccuracies are going to be so small you wouldn’t even be able to tell when you are listening to the music.
      well , i thought that too , but ive been in scenarios where ive been able to notice while listening or been able to spot sync problems (long edits) . for example while manipulating BAR loop range forth and back which i tried to make possible . I lost interest :)
      As for Notes ,overlapping lengths did play its game here ,and as u said shorting it up helped a lot in those days I can think of note quantization as a part solution (not quite sure right now , wondering if i can quantize selected note through the API ? ) , but sometimes i dont want to quantize them , so perhaps i could quantize a note and add its computed offset again . yeah maybe ... maybe adding computed offset at the last stage of sending data would work for all of the issues . like counting everything from most accurate grid division first . dont know .. just brainstorming
      how people builds their daw's then :) i will give up on this
    • Rob Gordon's icon
      Rob Gordon's icon
      Rob Gordon
      Nov 18 2014 | 9:58 am
      This is amazing coming from a web dev background. Thanks Adam!
    • Emerah's icon
      Emerah's icon
      Emerah
      Nov 30 2014 | 6:40 pm
      I have always wanted to learn how to manipulate Live from javascript. Thank you Adam ... you got me motivated to finally start :)
    • Rob Gordon's icon
      Rob Gordon's icon
      Rob Gordon
      Dec 10 2014 | 2:00 pm
      Couldn't have gotten started without this tutorial. Thanks Adam!
    • Tom Swirly's icon
      Tom Swirly's icon
      Tom Swirly
      Jul 25 2015 | 10:11 pm
      Just saw this - great stuff! I'm going to steal some parts of your log function particularly.
      Have you thought of checking some of this in to github or some other open source system?
      I have a pretty huge library of Javascript for Max rooted here - let me know if you want a tour...
    • Chalisque's icon
      Chalisque's icon
      Chalisque
      Jul 06 2016 | 9:20 pm
      With regards to the float precision thing, if using simple floats is a problem, and all you have are floats to work with, consider some 'integer plus fraction' solution.
      In javascript, have an object of the form: var obj = { i: 47, f: 0.3 }; to represent 47.3. The i member is the integer part, the f member is the fraction part. The 32bit float type can represent integers _exactly_ between -2^24 and 2^24 (that is, between -16m and +16m). Splitting like this means all of the bits of the f member can be used to represent the fractional part (not as efficient as a double, but if you're stuck with floats, you're stuck with floats). Then function addf(x,y) { var z = {i: x.i + y.i, f:x.f + y.f}; if( z.f = 1 ) { z.f -= 1; z.i += 1; } } and similar for other arithmetic operations. A bit of a chore to write, but if you need additional precision and all you have are floats, this is the kind of thing you need to do.
    • Chalisque's icon
      Chalisque's icon
      Chalisque
      Jul 06 2016 | 9:23 pm
      If someone has a max for live patch using javascript and float precision is causing a problem, and they don't mind me having a copy to play with, I have a background in pure maths (that is, way back when I did a PhD in it) and an interest in all those nitty gritty detaily things (though these days I only explore them for a hobby).
    • M.J.W.'s icon
      M.J.W.'s icon
      M.J.W.
      Feb 17 2017 | 5:35 am
      It is already a couple of years ago that you wrote it, but I still want to thank you for these articles! I am starting with Max and Max for Live and working on my first project. It is not working correctly yet and that is why I am going to rewrite my first project using Javascript and your tutorials are very helpful for that. Thanks!
    • Kirkwood West's icon
      Kirkwood West's icon
      Kirkwood West
      Feb 12 2019 | 5:32 am
      Just so you know this is the best article i've read on the matter. thanks adam.
    • Udo Matthias's icon
      Udo Matthias's icon
      Udo Matthias
      Aug 05 2019 | 5:32 am
      Wow, wow Adam, great work. Love it!! wr udo
    • cupcake's icon
      cupcake's icon
      cupcake
      Sep 13 2019 | 1:31 pm
      Just to note, Chalisque's post above (Jul 06 2016) has an error in the code in which there is an assignment in a conditional expression, instead of a comparison: function addf(x,y) { var z = {i: x.i + y.i, f:x.f + y.f}; if( z.f = 1 ) { z.f -= 1; z.i += 1; } }
      "(z.f = 1)"
      Should probably be: function addf(x,y) { var z = {i: x.i + y.i, f:x.f + y.f}; if( z.f >= 1 ) { z.f -= 1; z.i += 1; } }
      Don't know if JS would raise a fuss over the syntax. Java does, but it's valid code in C/C++ and will always evaluate to true.
    • jrs1's icon
      jrs1's icon
      jrs1
      Jan 10 2020 | 8:55 pm
      Adam, thanks for the tutorial. I'd been wanting to get JS and better ways to control the liveapi, nearly completed my first device using JS.
    • Sam Tarakajian's icon
      Sam Tarakajian's icon
      Sam Tarakajian
      Dec 09 2020 | 8:17 pm
      Hey! Thank you so much for this post, it was very helpful to me, even in 2020. Thanks especially for pointing out that Live will complain if you use floating point numbers with too many decimals—I never would have figured out to use toFixed. One small thing I'll point out is that 4 decimal positions of accuracy doesn't seem to be quite enough. I had to go up to 8 to get my triplets to line up with Live's.
      Hope that's useful to someone in 2020 :)
    • Thibault Lebrun's icon
      Thibault Lebrun's icon
      Thibault Lebrun
      Dec 14 2020 | 12:53 pm
      Hi everyone, this is kinda of a noobie post :p. I followed part of the tutorial and I was wondering if someone could help me understand why I cannot get access to the LiveAPI object (via : new LiveAPI("live_set")) which is always an empty object with no information on the set whatsoever.
      I've copied code as is and also tried alternative ways like sending a bang (with the bang method defined in js). As well as adding or not a callback to the "new LiveAPI()".. Also tried different paths like "live_set tracks 0" and so on. And used defer or deferlow objects.
      Here's my js code :
      function log() {
        for(var i=0,len=arguments.length; i<len; i++) {
          var message = arguments[i];
          if(message && message.toString) {
            var s = message.toString();
            if(s.indexOf("[object ") >= 0) {
              s = JSON.stringify(message);
            }
            post(s);
          }
          else if(message === null) {
            post("<null>");
          }
          else {
            post(message);
          }
        }
        post("\n");
      }
       
      log("___________________________________________________");
      log("Reload:", new Date);
      
      function bang() {
      	var api = new LiveAPI(null, "live_set")
      	log(api.info);
      }
      I connected a live.thisdevice to the (first) inlet and when I click it it always logs "undefined". This is a midi max patch.
      Am I missing something ? I'm using Live 10.1.30 with Max 8.1.7 64 bit on windows 10.
      Really baffled about this one ..
      Thanks !
    • Thibault Lebrun's icon
      Thibault Lebrun's icon
      Thibault Lebrun
      Dec 14 2020 | 2:32 pm
      Well false alert, I reinstalled all the stock midi remote scripts and everything seems to work now ! Guess I must have removed some core ableton python files by mistake when doing remote script dev :)