Myo.js is pretty cool. We talked about setting it up a few weeks ago, but basically it's a simple JavaScript library to let you Myo-enable any webpage, giving you the ability to add gesture and motion control with ease. Today I want to talk about a fun little myo.js project the RAD team put together at #thalmichackd: Myosaurus.

This is a neat little web app that simulates a sock puppet. The idea is that you open and close your hand to make the puppet's mouth move and make noise. You can grab the source code (and all the other Rapid Application Development projects) in GitHub.

If you take a look at the project, you can see that it's pretty simple. index.html has some instructions and a few JavaScript inclusions. The puppet is a single JavaScript file called puppetGL.js. It's based on this sweet demo here.

sockpuppet.js is what we are interested in:

var myo = Myo.create();

myo.on('connected', function(){  
    myo.streamEMG(true);
})

var setupSound = function(){  
    var context = new window.AudioContext();

    osc = context.createOscillator();
    osc.frequency.value = 0;
    osc.connect(context.destination);
    osc.start(0);
}()

var sawtoothWave = _.times(15, function(){ return 0});

myo.on('emg', function(emg){

    //Normalize the EMG data and cut out some noise
    var emgAverage = _.reduce(emg, function(r, pod){
        return r + pod;
    },0)/emg.length;
    if(emgAverage < 0) emgAverage *= -1
    if(emgAverage < 5) emgAverage = 0;

    //Produces a fast rise, slow fall, sawtooth-like dataset from the EMG data
    var lastVal = _.last(sawtoothWave);
    if(emgAverage > lastVal){
        lastVal = emgAverage;
    }else{
        lastVal -= 1;
    }
    if(lastVal<0) lastVal=0;
    sawtoothWave.shift();
    sawtoothWave.push(lastVal);

    //Smooths the spikey-ness into something we can use
    var smoothedAverage = _.reduce(sawtoothWave, function(r, val){
        return r + val;
    }, 0)/sawtoothWave.length;

    this.trigger('puppet_mouth', smoothedAverage);

})

myo.on('puppet_mouth', function(val){  
    //Using the puppet mouth with some tweak variables to get the desired effect
    osc.frequency.value = 500/30 * val;

    if(val/50 < 0.2){
        nMouthOpen = -0.08
    }else{
        nMouthOpen = val/50;
    }
})

So, first we create a myo object, and then turn EMG streaming on. Then we set up an Oscillator to play audio (Not familiar with HTML5 Audio? Try this post to get you started, and this to learn about oscillators).

The myo.on('emg', function(emg) { ... }) function is where the real action happens. Now, the Myo armband gives you 8 separate channels of EMG data to play with. For our sock puppet, we don't really care about individual channels. We are just using a simple model where high activation means the hand open. That's definitely not always true, but it's close enough for our purposes. To figure out if we have high activation, we start by averaging all the channels together (with help from underscore.js's reduce):

    //Normalize the EMG data and cut out some noise
    var emgAverage = _.reduce(emg, function(r, pod){
        return r + pod;
    },0)/emg.length;
    if(emgAverage < 0) emgAverage *= -1
    if(emgAverage < 5) emgAverage = 0;

There is always going to be a bit of noise on the EMG sensors, so if the average reading is less than some floor value (5 seemed to work), we clamp it down to 0. We also flip it so the value is always positive (since it bounces back and forth).

The tricky bit is coming up with a good sound. Through a lot of experimentation, we determined that you needed the relation of the mouth height to the sound pitch to be really tight while opening your hand, but it could slowly taper off as your hand was closing. This is a (reverse) sawtooth wave:

    //Produces a fast rise, slow fall, sawtooth-like dataset from the EMG data
    var lastVal = _.last(sawtoothWave);
    if(emgAverage > lastVal){
        lastVal = emgAverage;
    }else{
        lastVal -= 1;
    }
    if(lastVal<0) lastVal=0;
    sawtoothWave.shift();
    sawtoothWave.push(lastVal);

    //Smooths the spikey-ness into something we can use
    var smoothedAverage = _.reduce(sawtoothWave, function(r, val){
        return r + val;
    }, 0)/sawtoothWave.length;

So, every time we get EMG data, we check if the average is bigger than the last value. If so, then the average becomes the new value. If not, we reduce the last value by one (making sure it doesn't go below 0) and use that instead.

We then calculate a smoothed average based off the last 15 samples we saved. Then, we use myo.js's built in event system to pass the result to our mouth and sound code:

    this.trigger('puppet_mouth', smoothedAverage);

myo.on('puppet_mouth', function(val){  
    //Using the puppet mouth with some tweak variables to get the desired effect
    osc.frequency.value = 500/30 * val;

    if(val/50 < 0.2){
        nMouthOpen = -0.08
    }else{
        nMouthOpen = val/50;
    }
}

nMouthOpen is something we exposed in puppetGL.js, it just controls how open the mouth is, and osc.frequency.value determines the frequency of the sound that get's played.

That's it! This was a quick hack that turned out to be a lot of fun, but it just goes to show you how easy it is to make something with myo.js. Sound off in the comments if you're working on something cool! Don't forget you can submit web apps to the Myo Market and (unless you are reading this FROM THE FUTURE), that can earn you a sweet Myo Developer T-Shirt, free.

See you next time!

Newsletter

Enter your email address and get all latest content delivered to your inbox every now and then.