My Favorite Object: bucket


    Several years ago, Darwin Grosse and I worked on a project that used optical flow to track people running around in circles to simulate the jog wheel on old analog video tape decks. As is often the case with many sensor projects, the raw data was really jumpy and jittery and required some serious sweetening and massaging. Darwin’s solution, much to my surprise, was the bucket object. It’s been a valuable tool in my Max swiss army knife ever since.

    Running Averages

    The bucket patch I use the most allows you to get a running average over some finite set of events (if you want a running average for an stream or unspecified set of values, you'd use the mean object). Here's an example that calculates the average of the last 8 floating point input values:
    This technique is really useful, but can be a lot to patch when averaging a larger value set. The included bucket.avg abstraction included with the downloadable patches lets you type in the number of values you want to average and scripts the connections for you. Keep in mind that the zl sum object will default to a maximum list length of 256, so if you want a larger value, you’ll have to alter the zl object.
    Here's the patch in action (make sure you have the bucket.avg abstraction in your search path):

    Velocity and Acceleration

    The bucket object is also computationally useful for calculating first and second derivatives for streams of data. The terms "first and second derivative" are math-speak for keeping track of:
    • the rate that things are changing (that's what velocity is - the rate of change in a value over time.)
    • the rate at which the rate that things is changing - that's what acceleration (the second derivative) is.
    Here's how the patch looks:

    A bucket for Symbols

    The bucket object is a little unusual in that you don't need to worry about using arguments to the object in order to have it work with floating point values. In fact, bucket is so useful that people often ask whether or not there's a Max object that is similar but will work with symbols or lists. While there isn't a specific object that works with symbols the way bucket does with numbers, here's a Max patch that does exactly that (and it helpfully outputs stuff in standard right-to-left order, too):
    As with the first example, this can mean a lot of patching when you start making one for larger lists. The bucket.sym abstraction makes easy work of this - just type in the number of outlets you want as an argument (i.e., bucket.sym 4).
    I hope you can see how I’ve come to rely on the bucket object, and how it has influenced me to make similar tools for other message types. If you have other uses for the bucket object, please jump onto the forum and share!
    Check out the entire "My Favorite Object" series: bucket, stutter~, jstrigger, multislider, plot~, trigger, rate~, jit.slide, curve~, jit.gl.mesh, button, jit.gl.pix.

    • Nov 16 2016 | 10:21 am
      Thank you, Cory, especially for the "Velocity and Acceleration" and "A bucket for symbols" parts.
      For the "Running Averages" I find it much easier to do this:
      zl.stream X zl.sum / X.
      No need for scripting and you can even change the number of samples to average dynamically by sending it to the stream and divider object's right inputs.
    • Nov 16 2016 | 5:18 pm
      [deleted for stupidity]
    • Nov 16 2016 | 5:51 pm
      So simple! I think I'm going to go through all my sensor input patches and try out changing the smoothing method to this. And add in some acceleration based triggers. Brilliant. Thanks Cory.
    • Nov 16 2016 | 10:07 pm
      bucket 8 expr ($f1+$f2+$f3+$f4+$f5+$f6+$f7+$f8)/8.
    • Nov 16 2016 | 10:47 pm
      Question, as I don't have max available right now to school myself: as far as I understand from the bucket reference, it doesn't output the last entry to the object: it outputs what is stored from before, then stores and shifts in the new value and doesn't output this until the next value is received.
      Will this not make the average and the derivatives unprecise? The former when it comes to averages over few numbers or small datasets (as it will lose one factor) and the latter, as the first derivative will be of the penultimate and third last value (and the second derivative will be of the third and the fourth last value) from the drunk object?
    • Nov 17 2016 | 7:51 am
      [bucket 4 1] "non-zero output flag sends input to left outlet when it is received" if you need it now, flag it.
    • Nov 17 2016 | 10:41 am
      Aha. I was wondering about that since it was not mentioned in the tutorial.
    • Nov 17 2016 | 4:26 pm
      "Will this not make the average and the derivatives unprecise?"
      no, it just means that all input data is timeshifted for one event. and since controller input is usually very fast and dense...
    • Nov 17 2016 | 10:34 pm
      @roman: Which was kind of what I was aiming to clarify in the sentence following up my question. "It doesn't matter due to data input being fast and dense" doesn't mean that one shouldn't be aware of the timeshift when using this technique of handling data, specifically if you are using the treated data in direct relation to the input. I felt this was missing from the tutorial and thus worth a question, as I didn't have a laptop with Max in front of me at the time. As Rick replied: There is the flag to counteract this.
    • Nov 18 2016 | 10:23 am
      I see everyone discussing the bucket object for calculating running averages and got curious: is there some advantage I'm missing in calculating it using bucket instead of my approach (zl.stream)? Or is it just out of curiosity? Anyway, I revised the patch I posted above. Since I can dynamically change the "smoothing factor" (number of samples to average), I've noticed a problem: when you change it, the buffer needs to be refilled, which causes a temporary pause in the output. This can be avoided with the inclusion of another zl.stream. This way you would be able to smoothly tweak the smoothing factor, an essential requirement in a live performance context.
      Just two quick notes: - the patch is aimed at sensors with continuous output. In other types of non-continuous streams of data, I would just continuously sample them. - there's an initial delay before this approach starts its output, related to the buffer being filled, but that does not mean that the output values are delayed by the buffer size value.
    • Nov 18 2016 | 2:00 pm
      the bucket (or funnel or unpack or trigger or zl nth) solutions are just more oldschool and people still have these as their custom abstractions installed.
    • Dec 02 2016 | 2:57 pm
      Yes, for all these examples [zl stream] works great and definitely involves less patching. And it also works with symbols etc.