From 7a1f7bd41888a47411420e45428e89319a728296 Mon Sep 17 00:00:00 2001
From: DaveDubUK <DaveDubUK@gmail.com>
Date: Wed, 5 Nov 2014 20:11:11 +0000
Subject: [PATCH 01/37] Update to walk.js v 1.1

Revised code structure (see /libraries for new files), walk animation
completely overhauled (now using geometric wave generators, Fourier
synthesis and Butterworth LP filtering for selected motion curve
construction), redesigned sidestepping animations, tweaked flying
animations, decreased maximum walk speed in Interface to more natural
human value, improved Hydra support, added support for Leap motion,
animation file sizes optimised, cleared out code used to work around now
fixed bugs, lots of minor tweaks and improvements. walk.js is now under
100,000 characters, but I had to use tabs instead of 4 spaces to get it
below the threshold.
---
 examples/libraries/walkApi.js       |  409 ++
 examples/libraries/walkFilters.js   |  230 +
 examples/libraries/walkInterface.js | 2713 ++++++++++++
 examples/walk.js                    | 6105 +++++++++++----------------
 interface/src/avatar/MyAvatar.cpp   |    2 +-
 5 files changed, 5761 insertions(+), 3698 deletions(-)
 create mode 100644 examples/libraries/walkApi.js
 create mode 100644 examples/libraries/walkFilters.js
 create mode 100644 examples/libraries/walkInterface.js

diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js
new file mode 100644
index 0000000000..90b9dad623
--- /dev/null
+++ b/examples/libraries/walkApi.js
@@ -0,0 +1,409 @@
+//
+//  walkObjects.js
+//
+//  version 1.000
+//
+//  Created by David Wooldridge, Autumn 2014
+//
+//  Motion, state and Transition objects for use by the walk.js script v1.1
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+// constructor for the Motion object
+Motion = function() {
+
+    this.setGender = function(gender) {
+
+        this.avatarGender = gender;
+
+        switch(this.avatarGender) {
+
+            case MALE:
+
+                this.selWalk = walkAssets.maleStandardWalk;
+                this.selStand = walkAssets.maleStandOne;
+                this.selFlyUp = walkAssets.maleFlyingUp;
+                this.selFly = walkAssets.maleFlying;
+                this.selFlyDown = walkAssets.maleFlyingDown;
+                this.selSideStepLeft = walkAssets.maleSideStepLeft;
+                this.selSideStepRight = walkAssets.maleSideStepRight;
+                this.curAnim = this.selStand;
+                return;
+
+            case FEMALE:
+
+                this.selWalk = walkAssets.femaleStandardWalk;
+                this.selStand = walkAssets.femaleStandOne;
+                this.selFlyUp = walkAssets.femaleFlyingUp;
+                this.selFly = walkAssets.femaleFlying;
+                this.selFlyDown = walkAssets.femaleFlyingDown;
+                this.selSideStepLeft = walkAssets.femaleSideStepLeft;
+                this.selSideStepRight = walkAssets.femaleSideStepRight;
+                this.curAnim = this.selStand;
+                return;
+        }
+    }
+
+    this.hydraCheck = function() {
+
+        // function courtesy of Thijs Wenker, frisbee.js
+        var numberOfButtons = Controller.getNumberOfButtons();
+        var numberOfTriggers = Controller.getNumberOfTriggers();
+        var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
+        var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
+        hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
+        return hydrasConnected;
+    }
+
+    // settings
+    this.armsFree = this.hydraCheck(); // automatically sets true for Hydra support - temporary fix
+    this.makesFootStepSounds = true;
+    this.avatarGender = MALE;
+    this.motionPitchMax = 60;
+    this.motionRollMax = 40;
+
+    // timing
+    this.frameStartTime = 0; // used for measuring frame execution times
+    this.frameExecutionTimeMax = 0; // keep track of the longest frame execution time
+    this.cumulativeTime = 0.0;
+    this.lastWalkStartTime = 0;
+
+    // selected animations
+    this.selWalk = walkAssets.maleStandardWalk;
+    this.selStand = walkAssets.maleStandOne;
+    this.selFlyUp = walkAssets.maleFlyingUp;
+    this.selFly = walkAssets.maleFlying;
+    this.selFlyDown = walkAssets.maleFlyingDown;
+    this.selSideStepLeft = walkAssets.maleSideStepLeft;
+    this.selSideStepRight = walkAssets.maleSideStepRight;
+
+    // the currently selected animation, joint and transition
+    this.curAnim = this.selStand;
+    this.curJointIndex = 0;
+    this.curTransition = null;
+
+    // zero out avi's joints, curl the fingers nicely then take some measurements
+    this.avatarJointNames = MyAvatar.getJointNames();
+    if (!this.armsFree) {
+
+        for (var i = 0; i < this.avatarJointNames.length; i++) {
+
+            if (i > 17 || i < 34)
+                // left hand fingers
+                MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
+
+            else if (i > 33 || i < 38)
+                // left hand thumb
+                MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
+
+            else if (i > 41 || i < 58)
+                // right hand fingers
+                MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
+
+            else if (i > 57 || i < 62)
+                // right hand thumb
+                MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
+
+            else
+                // zero out the remaining joints
+                MyAvatar.clearJointData(this.avatarJointNames[i]);
+        }
+    }
+
+    this.footRPos = MyAvatar.getJointPosition("RightFoot");
+    this.hipsToFeet = MyAvatar.getJointPosition("Hips").y - this.footRPos.y;
+
+    // walkwheel (foot / ground speed matching)
+    this.direction = FORWARDS;
+    this.nextStep = RIGHT;
+    this.nFrames = 0;
+    this.strideLength = this.selWalk.calibration.strideLengthForwards;
+    this.walkWheelPos = 0;
+
+    this.advanceWalkWheel = function(angle){
+
+        this.walkWheelPos += angle;
+        if (motion.walkWheelPos >= 360)
+            this.walkWheelPos = this.walkWheelPos % 360;
+    }
+
+    // last frame history
+    this.lastDirection = 0;
+    this.lastVelocity = 0;
+
+};  // end Motion constructor
+
+// finite state machine
+state = (function () {
+
+    return {
+
+        // the finite list of states
+        STANDING: 1,
+        WALKING: 2,
+        SIDE_STEP: 3,
+        FLYING: 4,
+        EDIT_WALK_STYLES: 5,
+        EDIT_WALK_TWEAKS: 6,
+        EDIT_WALK_JOINTS: 7,
+        EDIT_STANDING: 8,
+        EDIT_FLYING: 9,
+        EDIT_FLYING_UP: 10,
+        EDIT_FLYING_DOWN: 11,
+        EDIT_SIDESTEP_LEFT: 12,
+        EDIT_SIDESTEP_RIGHT: 14,
+        currentState: this.STANDING,
+
+        // status vars
+        powerOn: true,
+        minimised: true,
+        editing:false,
+        editingTranslation: false,
+
+        setInternalState: function(newInternalState) {
+
+            switch (newInternalState) {
+
+                case this.WALKING:
+
+                    this.currentState = this.WALKING;
+                    this.editing = false;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.FLYING:
+
+                    this.currentState = this.FLYING;
+                    this.editing = false;
+                    motion.lastWalkStartTime = 0;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.SIDE_STEP:
+
+                    this.currentState = this.SIDE_STEP;
+                    this.editing = false;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_WALK_STYLES:
+
+                    this.currentState = this.EDIT_WALK_STYLES;
+                    this.editing = true;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    motion.curAnim = motion.selWalk;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_WALK_TWEAKS:
+
+                    this.currentState = this.EDIT_WALK_TWEAKS;
+                    this.editing = true;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    motion.curAnim = motion.selWalk;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_WALK_JOINTS:
+
+                    this.currentState = this.EDIT_WALK_JOINTS;
+                    this.editing = true;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    motion.curAnim = motion.selWalk;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_STANDING:
+
+                    this.currentState = this.EDIT_STANDING;
+                    this.editing = true;
+                    motion.lastWalkStartTime = 0;
+                    motion.curAnim = motion.selStand;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_SIDESTEP_LEFT:
+
+                    this.currentState = this.EDIT_SIDESTEP_LEFT;
+                    this.editing = true;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    motion.curAnim = motion.selSideStepLeft;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_SIDESTEP_RIGHT:
+
+                    this.currentState = this.EDIT_SIDESTEP_RIGHT;
+                    this.editing = true;
+                    motion.lastWalkStartTime = new Date().getTime();
+                    motion.curAnim = motion.selSideStepRight;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_FLYING:
+
+                    this.currentState = this.EDIT_FLYING;
+                    this.editing = true;
+                    motion.lastWalkStartTime = 0;
+                    motion.curAnim = motion.selFly;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_FLYING_UP:
+
+                    this.currentState = this.EDIT_FLYING_UP;
+                    this.editing = true;
+                    motion.lastWalkStartTime = 0;
+                    motion.curAnim = motion.selFlyUp;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.EDIT_FLYING_DOWN:
+
+                    this.currentState = this.EDIT_FLYING_DOWN;
+                    this.editing = true;
+                    motion.lastWalkStartTime = 0;
+                    motion.curAnim = motion.selFlyDown;
+                    walkInterface.updateMenu();
+                    return;
+
+                case this.STANDING:
+                default:
+
+                    this.currentState = this.STANDING;
+                    this.editing = false;
+                    motion.lastWalkStartTime = 0;
+                    motion.curAnim = motion.selStand;
+                    walkInterface.updateMenu();
+
+                    // initialisation - runs at script startup only
+                    if (motion.strideLength === 0) {
+
+                        motion.setGender(MALE);
+                        if (motion.direction === BACKWARDS)
+                            motion.strideLength = motion.selWalk.calibration.strideLengthBackwards;
+                        else
+                            motion.strideLength = motion.selWalk.calibration.strideLengthForwards;
+                    }
+                    return;
+            }
+        }
+    }
+})(); // end state object literal
+
+// constructor for animation Transition
+Transition = function(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) {
+
+    this.lastAnim = lastAnimation; // name of last animation
+    if (lastAnimation === motion.selWalk ||
+        nextAnimation === motion.selSideStepLeft ||
+        nextAnimation === motion.selSideStepRight)
+        this.walkingAtStart = true; // boolean - is the last animation a walking animation?
+    else
+        this.walkingAtStart = false; // boolean - is the last animation a walking animation?
+    this.nextAnimation = nextAnimation; // name of next animation
+    if (nextAnimation === motion.selWalk ||
+        nextAnimation === motion.selSideStepLeft ||
+        nextAnimation === motion.selSideStepRight)
+        this.walkingAtEnd = true; // boolean - is the next animation a walking animation?
+    else
+        this.walkingAtEnd = false; // boolean - is the next animation a walking animation?
+    this.reachPoses = reachPoses; // placeholder / stub: array of reach poses for squash and stretch techniques
+    this.transitionDuration = transitionDuration; // length of transition (seconds)
+    this.easingLower = easingLower; // Bezier curve handle (normalised)
+    this.easingUpper = easingUpper; // Bezier curve handle (normalised)
+    this.startTime = new Date().getTime(); // Starting timestamp (seconds)
+    this.progress = 0; // how far are we through the transition?
+    this.walkWheelIncrement = 6; // how much to turn the walkwheel each frame when transitioning to / from walking
+    this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition
+    this.walkStopAngle = 0; // what angle should we stop the walk cycle?
+
+}; // end Transition constructor
+
+
+walkAssets = (function () {
+
+    // path to the sounds used for the footsteps
+    var _pathToSounds = 'https://s3.amazonaws.com/hifi-public/sounds/Footsteps/';
+
+    // read in the sounds
+    var _footsteps = [];
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW2Left-12db.wav"));
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW2Right-12db.wav"));
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW3Left-12db.wav"));
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW3Right-12db.wav"));
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW5Left-12db.wav"));
+    _footsteps.push(new Sound(_pathToSounds+"FootstepW5Right-12db.wav"));
+
+    // load the animation datafiles
+    Script.include(pathToAssets+"animations/dd-female-standard-walk-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-flying-up-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-flying-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-flying-down-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-standing-one-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-sidestep-left-animation.js");
+    Script.include(pathToAssets+"animations/dd-female-sidestep-right-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-standard-walk-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-flying-up-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-flying-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-flying-down-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-standing-one-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-sidestep-left-animation.js");
+    Script.include(pathToAssets+"animations/dd-male-sidestep-right-animation.js");
+
+    // read in the animation files
+    var _FemaleStandardWalkFile = new FemaleStandardWalk();
+    var _femaleStandardWalk = _FemaleStandardWalkFile.loadAnimation();
+    var _FemaleFlyingUpFile = new FemaleFlyingUp();
+    var _femaleFlyingUp = _FemaleFlyingUpFile.loadAnimation();
+    var _FemaleFlyingFile = new FemaleFlying();
+    var _femaleFlying = _FemaleFlyingFile.loadAnimation();
+    var _FemaleFlyingDownFile = new FemaleFlyingDown();
+    var _femaleFlyingDown = _FemaleFlyingDownFile.loadAnimation();
+    var _FemaleStandOneFile = new FemaleStandingOne();
+    var _femaleStandOne = _FemaleStandOneFile.loadAnimation();
+    var _FemaleSideStepLeftFile = new FemaleSideStepLeft();
+    var _femaleSideStepLeft = _FemaleSideStepLeftFile.loadAnimation();
+    var _FemaleSideStepRightFile = new FemaleSideStepRight();
+    var _femaleSideStepRight = _FemaleSideStepRightFile.loadAnimation();
+    var _MaleStandardWalkFile = new MaleStandardWalk(filter);
+    var _maleStandardWalk = _MaleStandardWalkFile.loadAnimation();
+    var _MaleFlyingUpFile = new MaleFlyingUp();
+    var _maleFlyingUp = _MaleFlyingUpFile.loadAnimation();
+    var _MaleFlyingFile = new MaleFlying();
+    var _maleFlying = _MaleFlyingFile.loadAnimation();
+    var _MaleFlyingDownFile = new MaleFlyingDown();
+    var _maleFlyingDown = _MaleFlyingDownFile.loadAnimation();
+    var _MaleStandOneFile = new MaleStandingOne();
+    var _maleStandOne = _MaleStandOneFile.loadAnimation();
+    var _MaleSideStepLeftFile = new MaleSideStepLeft();
+    var _maleSideStepLeft = _MaleSideStepLeftFile.loadAnimation();
+    var _MaleSideStepRightFile = new MaleSideStepRight();
+    var _maleSideStepRight = _MaleSideStepRightFile.loadAnimation();
+
+    return {
+
+        // expose the sound assets
+        footsteps: _footsteps,
+
+        // expose the animation assets
+        femaleStandardWalk: _femaleStandardWalk,
+        femaleFlyingUp: _femaleFlyingUp,
+        femaleFlying: _femaleFlying,
+        femaleFlyingDown: _femaleFlyingDown,
+        femaleStandOne: _femaleStandOne,
+        femaleSideStepLeft: _femaleSideStepLeft,
+        femaleSideStepRight: _femaleSideStepRight,
+        maleStandardWalk: _maleStandardWalk,
+        maleFlyingUp: _maleFlyingUp,
+        maleFlying: _maleFlying,
+        maleFlyingDown: _maleFlyingDown,
+        maleStandOne: _maleStandOne,
+        maleSideStepLeft: _maleSideStepLeft,
+        maleSideStepRight: _maleSideStepRight,
+    }
+})();
\ No newline at end of file
diff --git a/examples/libraries/walkFilters.js b/examples/libraries/walkFilters.js
new file mode 100644
index 0000000000..608618e580
--- /dev/null
+++ b/examples/libraries/walkFilters.js
@@ -0,0 +1,230 @@
+//
+//  walkFilters.js
+//
+//  version 1.000
+//
+//  Created by David Wooldridge, Autumn 2014
+//
+//  Provides a variety of filters for use by the walk.js script v1.1
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+AveragingFilter = function(length) {
+
+    //this.name = name;
+    this.pastValues = [];
+
+    for(var i = 0; i < length; i++) {
+
+        this.pastValues.push(0);
+    }
+
+    // single arg is the nextInputValue
+    this.process = function() {
+
+        if (this.pastValues.length === 0 && arguments[0]) {
+            return arguments[0];
+        }
+        else if (arguments[0]) {
+
+            // apply quick and simple LP filtering
+            this.pastValues.push(arguments[0]);
+            this.pastValues.shift();
+            var nextOutputValue = 0;
+            for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea];
+            return nextOutputValue / this.pastValues.length;
+        }
+        else return 0;
+    };
+};
+
+// 2nd order Butterworth LP filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
+// provides LP filtering with a more stable frequency / phase response
+ButterworthFilter = function(cutOff) {
+
+    switch(cutOff) {
+
+        case 5:
+
+            this.gain = 20.20612010;
+            this.coeffOne = -0.4775922501;
+            this.coeffTwo = 1.2796324250;
+            break;
+    }
+
+    // initialise the arrays
+    this.xv = [];
+    this.yv = [];
+    for(var i = 0; i < 3; i++) {
+        this.xv.push(0);
+        this.yv.push(0);
+    }
+
+    // process values
+    this.process = function(nextInputValue) {
+
+        this.xv[0] = this.xv[1];
+        this.xv[1] = this.xv[2];
+        this.xv[2] = nextInputValue / this.gain;
+
+        this.yv[0] = this.yv[1];
+        this.yv[1] = this.yv[2];
+        this.yv[2] = (this.xv[0] + this.xv[2]) +
+                      2 * this.xv[1] +
+                     (this.coeffOne * this.yv[0]) +
+                     (this.coeffTwo * this.yv[1]);
+
+        return this.yv[2];
+    };
+}; // end Butterworth filter contructor
+
+// Add harmonics to a given sine wave to form square, sawtooth or triangle waves
+// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
+WaveSynth = function(waveShape, numHarmonics, smoothing) {
+
+    this.numHarmonics = numHarmonics;
+    this.waveShape = waveShape;
+    this.averagingFilter = new AveragingFilter(smoothing);
+
+    // NB: frequency in radians
+    this.shapeWave = function(frequency) {
+
+        // make some shapes
+        var harmonics = 0;
+        var multiplier = 0;
+        var iterations = this.numHarmonics * 2 + 2;
+        if (this.waveShape === TRIANGLE) iterations++;
+
+        for(var n = 2; n < iterations; n++) {
+
+            switch(this.waveShape) {
+
+                case SAWTOOTH:
+
+                    multiplier = 1 / n;
+                    harmonics += multiplier * Math.sin(n * frequency);
+                    break;
+
+                case TRIANGLE:
+
+                    if (n % 2 === 1) {
+
+                        var mulitplier = 1 / (n * n);
+
+                        // multiply every (4n-1)th harmonic by -1
+                        if (n === 3 || n === 7 || n === 11 || n === 15)
+                            mulitplier *= -1;
+                        harmonics += mulitplier * Math.sin(n * frequency);
+                    }
+                    break;
+
+                case SQUARE:
+
+                    if (n % 2 === 1) {
+
+                        multiplier = 1 / n;
+                        harmonics += multiplier * Math.sin(n * frequency);
+                    }
+                    break;
+            }
+        }
+
+        // smooth the result and return
+        return this.averagingFilter.process(harmonics);
+    };
+};
+
+// Create a wave shape by summing pre-calcualted sinusoidal harmonics
+HarmonicsFilter = function(magnitudes, phaseAngles) {
+
+    this.magnitudes = magnitudes;
+    this.phaseAngles = phaseAngles;
+
+    this.calculate = function(twoPiFT) {
+
+        var harmonics = 0;
+        var numHarmonics = magnitudes.length;
+
+        for(var n = 0; n < numHarmonics; n++)
+
+            harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]);
+
+        return harmonics;
+    };
+};
+
+// the main filter object literal
+filter = (function() {
+
+    // Bezier private functions
+    function _B1(t) { return t * t * t };
+    function _B2(t) { return 3 * t * t * (1 - t) };
+    function _B3(t) { return 3 * t * (1 - t) * (1 - t) };
+    function _B4(t) { return (1 - t) * (1 - t) * (1 - t) };
+
+    return {
+
+        // helper methods
+        degToRad: function(degrees) {
+
+            var convertedValue = degrees * Math.PI / 180;
+            return convertedValue;
+        },
+
+        radToDeg: function(radians) {
+
+            var convertedValue = radians * 180 / Math.PI;
+            return convertedValue;
+        },
+
+        // these filters need instantiating, as they hold arrays of previous values
+        createAveragingFilter: function(length) {
+
+            var newAveragingFilter = new AveragingFilter(length);
+            return newAveragingFilter;
+        },
+
+        createButterworthFilter: function(cutoff) {
+
+            var newButterworthFilter = new ButterworthFilter(cutoff);
+            return newButterworthFilter;
+        },
+
+        createWaveSynth: function(waveShape, numHarmonics, smoothing) {
+
+            var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing);
+            return newWaveSynth;
+        },
+
+        createHarmonicsFilter: function(magnitudes, phaseAngles) {
+
+            var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles);
+            return newHarmonicsFilter;
+        },
+
+
+        // the following filters do not need separate instances, as they hold no previous values
+        bezier: function(percent, C1, C2, C3, C4) {
+
+            // Bezier functions for more natural transitions
+            // based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
+            var pos = {x: 0, y: 0};
+            pos.x = C1.x * _B1(percent) + C2.x * _B2(percent) + C3.x * _B3(percent) + C4.x * _B4(percent);
+            pos.y = C1.y * _B1(percent) + C2.y * _B2(percent) + C3.y * _B3(percent) + C4.y * _B4(percent);
+            return pos;
+        },
+
+        // simple clipping filter (faster way to make square waveforms)
+        clipTrough: function(inputValue, peak, strength) {
+
+            var outputValue = inputValue * strength;
+            if (outputValue < -peak)
+                outputValue = -peak;
+
+            return outputValue;
+        }
+    }
+
+})();
\ No newline at end of file
diff --git a/examples/libraries/walkInterface.js b/examples/libraries/walkInterface.js
new file mode 100644
index 0000000000..71e4462933
--- /dev/null
+++ b/examples/libraries/walkInterface.js
@@ -0,0 +1,2713 @@
+//
+//  walkInterface.js
+//
+//  version 1.000
+//
+//  Created by David Wooldridge, Autumn 2014
+//
+//  Presents the UI for the walk.js script v1.1
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+walkInterface = (function() {
+
+    //  controller element positions and dimensions
+    var _backgroundWidth = 350;
+    var _backgroundHeight = 700;
+    var _backgroundX = Window.innerWidth - _backgroundWidth - 58;
+    var _backgroundY = Window.innerHeight / 2 - _backgroundHeight / 2;
+    var _minSliderX = _backgroundX + 30;
+    var _sliderRangeX = 295 - 30;
+    var _jointsControlWidth = 200;
+    var _jointsControlHeight = 300;
+    var _jointsControlX = _backgroundX + _backgroundWidth / 2 - _jointsControlWidth / 2;
+    var _jointsControlY = _backgroundY + 242 - _jointsControlHeight / 2;
+    var _buttonsY = 20; // distance from top of panel to menu buttons
+    var _bigButtonsY = 408; //  distance from top of panel to top of first big button
+
+    // arrays of overlay names
+    var _sliderThumbOverlays = [];
+    var _backgroundOverlays = [];
+    var _buttonOverlays = [];
+    var _jointsControlOverlays = [];
+    var _bigbuttonOverlays = [];
+
+    // reference to the internal state
+    var _state = {
+        editingTranslation: false
+    };
+
+    // reference to the Motion object
+    var _motion = null;
+
+    var _walkAssets = null;
+
+    // constants
+    var MAX_WALK_SPEED = 1257;
+
+    // look and feel
+    var momentaryButtonTimer = null;
+
+    // all slider controls have a range (with the exception of phase controls that are always +-180)
+    var _sliderRanges = {
+        "joints": [{
+            "name": "hips",
+            "pitchRange": 12,
+            "yawRange": 18,
+            "rollRange": 12,
+            "pitchOffsetRange": 25,
+            "yawOffsetRange": 25,
+            "rollOffsetRange": 25,
+            "swayRange": 0.12,
+            "bobRange": 0.05,
+            "thrustRange": 0.05,
+            "swayOffsetRange": 0.25,
+            "bobOffsetRange": 0.25,
+            "thrustOffsetRange": 0.25
+        }, {
+            "name": "upperLegs",
+            "pitchRange": 30,
+            "yawRange": 35,
+            "rollRange": 35,
+            "pitchOffsetRange": 20,
+            "yawOffsetRange": 20,
+            "rollOffsetRange": 20
+        }, {
+            "name": "lowerLegs",
+            "pitchRange": 10,
+            "yawRange": 20,
+            "rollRange": 20,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 20,
+            "rollOffsetRange": 20
+        }, {
+            "name": "feet",
+            "pitchRange": 10,
+            "yawRange": 20,
+            "rollRange": 20,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 50,
+            "rollOffsetRange": 50
+        }, {
+            "name": "toes",
+            "pitchRange": 90,
+            "yawRange": 20,
+            "rollRange": 20,
+            "pitchOffsetRange": 90,
+            "yawOffsetRange": 20,
+            "rollOffsetRange": 20
+        }, {
+            "name": "spine",
+            "pitchRange": 40,
+            "yawRange": 40,
+            "rollRange": 40,
+            "pitchOffsetRange": 90,
+            "yawOffsetRange": 50,
+            "rollOffsetRange": 50
+        }, {
+            "name": "spine1",
+            "pitchRange": 20,
+            "yawRange": 40,
+            "rollRange": 20,
+            "pitchOffsetRange": 90,
+            "yawOffsetRange": 50,
+            "rollOffsetRange": 50
+        }, {
+            "name": "spine2",
+            "pitchRange": 20,
+            "yawRange": 40,
+            "rollRange": 20,
+            "pitchOffsetRange": 90,
+            "yawOffsetRange": 50,
+            "rollOffsetRange": 50
+        }, {
+            "name": "shoulders",
+            "pitchRange": 35,
+            "yawRange": 40,
+            "rollRange": 20,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 180,
+            "rollOffsetRange": 180
+        }, {
+            "name": "upperArms",
+            "pitchRange": 90,
+            "yawRange": 90,
+            "rollRange": 90,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 180,
+            "rollOffsetRange": 180
+        }, {
+            "name": "lowerArms",
+            "pitchRange": 90,
+            "yawRange": 90,
+            "rollRange": 120,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 180,
+            "rollOffsetRange": 180
+        }, {
+            "name": "hands",
+            "pitchRange": 90,
+            "yawRange": 180,
+            "rollRange": 90,
+            "pitchOffsetRange": 180,
+            "yawOffsetRange": 180,
+            "rollOffsetRange": 180
+        }, {
+            "name": "head",
+            "pitchRange": 20,
+            "yawRange": 20,
+            "rollRange": 20,
+            "pitchOffsetRange": 90,
+            "yawOffsetRange": 90,
+            "rollOffsetRange": 90
+        }]
+    };
+
+    // load overlay images
+    var _controlsMinimisedTab = Overlays.addOverlay("image", {
+        x: Window.innerWidth - 58,
+        y: Window.innerHeight - 145,
+        width: 50,
+        height: 50,
+        imageURL: pathToAssets + 'overlay-images/ddao-minimise-tab.png',
+        visible: true,
+        alpha: 0.9
+    });
+
+    var _controlsBackground = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX,
+            y: _backgroundY,
+            width: _backgroundWidth,
+            height: _backgroundHeight
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _backgroundOverlays.push(_controlsBackground);
+
+    var _controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX,
+            y: _backgroundY,
+            width: _backgroundWidth,
+            height: _backgroundHeight
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-styles.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _backgroundOverlays.push(_controlsBackgroundWalkEditStyles);
+
+    var _controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX,
+            y: _backgroundY,
+            width: _backgroundWidth,
+            height: _backgroundHeight
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-tweaks.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _backgroundOverlays.push(_controlsBackgroundWalkEditTweaks);
+
+    var _controlsBackgroundWalkEditHipTrans = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX,
+            y: _backgroundY,
+            width: _backgroundWidth,
+            height: _backgroundHeight
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-translation.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _backgroundOverlays.push(_controlsBackgroundWalkEditHipTrans);
+
+    var _controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX,
+            y: _backgroundY,
+            width: _backgroundWidth,
+            height: _backgroundHeight
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-joints.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _backgroundOverlays.push(_controlsBackgroundWalkEditJoints);
+
+    // load character joint selection control images
+    var _hipsJointsTranslation = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-hips-translation.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_hipsJointsTranslation);
+
+    var _hipsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-hips.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_hipsJointControl);
+
+    var _upperLegsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-upper-legs.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_upperLegsJointControl);
+
+    var _lowerLegsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-lower-legs.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_lowerLegsJointControl);
+
+    var _feetJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-feet.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_feetJointControl);
+
+    var _toesJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-toes.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_toesJointControl);
+
+    var _spineJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_spineJointControl);
+
+    var _spine1JointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine1.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_spine1JointControl);
+
+    var _spine2JointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine2.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_spine2JointControl);
+
+    var _shouldersJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-shoulders.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_shouldersJointControl);
+
+    var _upperArmsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-upper-arms.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_upperArmsJointControl);
+
+    var _forearmsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-forearms.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_forearmsJointControl);
+
+    var _handsJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-hands.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_handsJointControl);
+
+    var _headJointControl = Overlays.addOverlay("image", {
+        bounds: {
+            x: _jointsControlX,
+            y: _jointsControlY,
+            width: 200,
+            height: 300
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-background-edit-head.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _jointsControlOverlays.push(_headJointControl);
+
+
+    // slider thumb overlays
+    var _sliderOne = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderOne);
+
+    var _sliderTwo = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderTwo);
+
+    var _sliderThree = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderThree);
+
+    var _sliderFour = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderFour);
+
+    var _sliderFive = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderFive);
+
+    var _sliderSix = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderSix);
+
+    var _sliderSeven = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderSeven);
+
+    var _sliderEight = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderEight);
+
+    var _sliderNine = Overlays.addOverlay("image", {
+        bounds: {
+            x: 0,
+            y: 0,
+            width: 25,
+            height: 25
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _sliderThumbOverlays.push(_sliderNine);
+
+
+    // button overlays
+    var _onButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 20,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-on-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_onButton);
+
+    var _offButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 20,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-off-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_offButton);
+
+    var _configWalkButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-walk-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkButton);
+
+    var _configWalkButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-walk-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkButtonSelected);
+
+    var _configStandButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-stand-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configStandButton);
+
+    var _configStandButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-stand-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configStandButtonSelected);
+
+    var _configFlyingButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingButton);
+
+    var _configFlyingButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingButtonSelected);
+
+    var _configFlyingUpButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-up-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingUpButton);
+
+    var _configFlyingUpButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-up-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingUpButtonSelected);
+
+    var _configFlyingDownButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-down-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingDownButton);
+
+    var _configFlyingDownButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-fly-down-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configFlyingDownButtonSelected);
+
+    var _hideButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 272,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-hide-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_hideButton);
+
+    var _hideButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 272,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-hide-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_hideButtonSelected);
+
+    var _configWalkStylesButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-styles-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkStylesButton);
+
+    var _configWalkStylesButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-styles-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkStylesButtonSelected);
+
+    var _configWalkTweaksButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-tweaks-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkTweaksButton);
+
+    var _configWalkTweaksButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 146,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-tweaks-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkTweaksButtonSelected);
+
+    var _configSideStepLeftButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-left-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configSideStepLeftButton);
+
+    var _configSideStepLeftButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 83,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-left-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configSideStepLeftButtonSelected);
+
+    var _configSideStepRightButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-right-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configSideStepRightButton);
+
+    var _configSideStepRightButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-right-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configSideStepRightButtonSelected);
+
+    var _configWalkJointsButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-joints-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkJointsButton);
+
+    var _configWalkJointsButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 209,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-joints-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_configWalkJointsButtonSelected);
+
+    var _backButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 272,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-back-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_backButton);
+
+    var _backButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + 272,
+            y: _backgroundY + _buttonsY,
+            width: 60,
+            height: 47
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-back-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _buttonOverlays.push(_backButtonSelected);
+
+    // big button overlays - front panel
+    var _femaleBigButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-female-big-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_femaleBigButton);
+
+    var _femaleBigButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-female-big-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_femaleBigButtonSelected);
+
+    var _maleBigButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 60,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-male-big-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_maleBigButton);
+
+    var _maleBigButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 60,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-male-big-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_maleBigButtonSelected);
+
+    var _armsFreeBigButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 120,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-arms-free-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_armsFreeBigButton);
+
+    var _armsFreeBigButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 120,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-arms-free-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_armsFreeBigButtonSelected);
+
+    var _footstepsBigButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 180,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-footsteps-big-button.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_footstepsBigButton);
+
+    var _footstepsBigButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY + 180,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-footsteps-big-button-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_footstepsBigButtonSelected);
+
+
+    // walk styles
+    _bigButtonsY = 121;
+    var _standardWalkBigButton = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-select-button-standard.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_standardWalkBigButton);
+
+    var _standardWalkBigButtonSelected = Overlays.addOverlay("image", {
+        bounds: {
+            x: _backgroundX + _backgroundWidth / 2 - 115,
+            y: _backgroundY + _bigButtonsY,
+            width: 230,
+            height: 36
+        },
+        imageURL: pathToAssets + "overlay-images/ddao-walk-select-button-standard-selected.png",
+        color: {
+            red: 255,
+            green: 255,
+            blue: 255
+        },
+        alpha: 1,
+        visible: false
+    });
+    _bigbuttonOverlays.push(_standardWalkBigButtonSelected);
+
+    // various show / hide GUI element functions
+    function minimiseDialog(minimise) {
+
+        if (momentaryButtonTimer) {
+            Script.clearInterval(momentaryButtonTimer);
+            momentaryButtonTimer = null;
+        }
+
+        if (minimise) {
+            setBackground();
+            hideMenuButtons();
+            setSliderThumbsVisible(false);
+            hideJointSelectors();
+            initialiseFrontPanel(false);
+            Overlays.editOverlay(_controlsMinimisedTab, {
+                visible: true
+            });
+        } else {
+            Overlays.editOverlay(_controlsMinimisedTab, {
+                visible: false
+            });
+        }
+    };
+
+    function setBackground(backgroundID) {
+        for (var i in _backgroundOverlays) {
+            if (_backgroundOverlays[i] === backgroundID)
+                Overlays.editOverlay(_backgroundOverlays[i], {
+                    visible: true
+                });
+            else Overlays.editOverlay(_backgroundOverlays[i], {
+                visible: false
+            });
+        }
+    };
+
+    // top row menu type buttons (on | walk | stand | fly | hide)
+    function hideMenuButtons() {
+        for (var i in _buttonOverlays) {
+            Overlays.editOverlay(_buttonOverlays[i], {
+                visible: false
+            });
+        }
+    };
+
+    function hideJointSelectors() {
+        for (var i in _jointsControlOverlays) {
+            Overlays.editOverlay(_jointsControlOverlays[i], {
+                visible: false
+            });
+        }
+    };
+
+    function setSliderThumbsVisible(thumbsVisible) {
+        for (var i = 0; i < _sliderThumbOverlays.length; i++) {
+            Overlays.editOverlay(_sliderThumbOverlays[i], {
+                visible: thumbsVisible
+            });
+        }
+    };
+
+    function setButtonOverlayVisible(buttonOverlayName) {
+        for (var i in _buttonOverlays) {
+            if (_buttonOverlays[i] === buttonOverlayName) {
+                Overlays.editOverlay(buttonOverlayName, {
+                    visible: true
+                });
+            }
+        }
+    };
+
+    function initialiseFrontPanel(showButtons) {
+
+        if (_motion.avatarGender === FEMALE) {
+            Overlays.editOverlay(_femaleBigButtonSelected, {
+                visible: showButtons
+            });
+            Overlays.editOverlay(_femaleBigButton, {
+                visible: false
+            });
+            Overlays.editOverlay(_maleBigButtonSelected, {
+                visible: false
+            });
+            Overlays.editOverlay(_maleBigButton, {
+                visible: showButtons
+            });
+
+        } else {
+
+            Overlays.editOverlay(_femaleBigButtonSelected, {
+                visible: false
+            });
+            Overlays.editOverlay(_femaleBigButton, {
+                visible: showButtons
+            });
+            Overlays.editOverlay(_maleBigButtonSelected, {
+                visible: showButtons
+            });
+            Overlays.editOverlay(_maleBigButton, {
+                visible: false
+            });
+        }
+        if (_motion.armsFree) {
+            Overlays.editOverlay(_armsFreeBigButtonSelected, {
+                visible: showButtons
+            });
+            Overlays.editOverlay(_armsFreeBigButton, {
+                visible: false
+            });
+
+        } else {
+
+            Overlays.editOverlay(_armsFreeBigButtonSelected, {
+                visible: false
+            });
+            Overlays.editOverlay(_armsFreeBigButton, {
+                visible: showButtons
+            });
+        }
+        if (_motion.makesFootStepSounds) {
+            Overlays.editOverlay(_footstepsBigButtonSelected, {
+                visible: showButtons
+            });
+            Overlays.editOverlay(_footstepsBigButton, {
+                visible: false
+            });
+
+        } else {
+
+            Overlays.editOverlay(_footstepsBigButtonSelected, {
+                visible: false
+            });
+            Overlays.editOverlay(_footstepsBigButton, {
+                visible: showButtons
+            });
+        }
+    };
+
+    function initialiseWalkStylesPanel(showButtons) {
+
+        // set all big buttons to hidden, but skip the first 8, as are used by the front panel
+        for (var i = 8; i < _bigbuttonOverlays.length; i++) {
+            Overlays.editOverlay(_bigbuttonOverlays[i], {
+                visible: false
+            });
+        }
+
+        if (!showButtons) return;
+
+        // set all the non-selected ones to showing
+        for (var i = 8; i < _bigbuttonOverlays.length; i += 2) {
+            Overlays.editOverlay(_bigbuttonOverlays[i], {
+                visible: true
+            });
+        }
+
+        // set the currently selected one
+        if (_motion.selWalk === _walkAssets.femaleStandardWalk ||
+            _motion.selWalk === _walkAssets.maleStandardWalk) {
+
+            Overlays.editOverlay(_standardWalkBigButtonSelected, {
+                visible: true
+            });
+            Overlays.editOverlay(_standardWalkBigButton, {
+                visible: false
+            });
+        }
+    };
+
+    function initialiseWalkTweaksPanel() {
+
+        // sliders for commonly required walk adjustments
+        var i = 0;
+        var yLocation = _backgroundY + 71;
+
+        // walk speed
+        var sliderXPos = _motion.curAnim.calibration.frequency / MAX_WALK_SPEED * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // lean (hips pitch offset)
+        sliderXPos = (((_sliderRanges.joints[0].pitchOffsetRange + _motion.curAnim.joints[0].pitchOffset) / 2) /
+            _sliderRanges.joints[0].pitchOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // stride (upper legs pitch)
+        sliderXPos = _motion.curAnim.joints[1].pitch / _sliderRanges.joints[1].pitchRange * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Legs separation (upper legs roll offset)
+        sliderXPos = (((_sliderRanges.joints[1].rollOffsetRange + _motion.curAnim.joints[1].rollOffset) / 2) /
+            _sliderRanges.joints[1].rollOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Legs forward (upper legs pitch offset)
+        sliderXPos = (((_sliderRanges.joints[1].pitchOffsetRange + _motion.curAnim.joints[1].pitchOffset) / 2) /
+            _sliderRanges.joints[1].pitchOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Lower legs splay (lower legs roll offset)
+        sliderXPos = (((_sliderRanges.joints[2].rollOffsetRange + _motion.curAnim.joints[2].rollOffset) / 2) /
+            _sliderRanges.joints[2].rollOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Arms forward (upper arms yaw offset)
+        sliderXPos = (((_sliderRanges.joints[9].yawOffsetRange + _motion.curAnim.joints[9].yawOffset) / 2) /
+            _sliderRanges.joints[9].yawOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Arms out (upper arm pitch offset)
+        sliderXPos = (((_sliderRanges.joints[9].pitchOffsetRange - _motion.curAnim.joints[9].pitchOffset) / 2) /
+            _sliderRanges.joints[9].pitchOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+
+        // Lower arms splay (lower arm pitch offset)
+        sliderXPos = (((_sliderRanges.joints[10].pitchOffsetRange - _motion.curAnim.joints[10].pitchOffset) / 2) /
+            _sliderRanges.joints[10].pitchOffsetRange) * _sliderRangeX;
+        Overlays.editOverlay(_sliderThumbOverlays[++i], {
+            x: _minSliderX + sliderXPos,
+            y: yLocation += 60,
+            visible: true
+        });
+    };
+
+    function initialiseJointsEditingPanel() {
+
+        var i = 0;
+        var yLocation = _backgroundY + 359;
+        hideJointSelectors();
+
+        if (_state.editingTranslation) {
+
+            // display the joint control selector for hips translations
+            Overlays.editOverlay(_hipsJointsTranslation, {visible: true});
+
+            // Hips sway
+            var sliderXPos = _motion.curAnim.joints[0].sway / _sliderRanges.joints[0].swayRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // Hips bob
+            sliderXPos = _motion.curAnim.joints[0].bob / _sliderRanges.joints[0].bobRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // Hips thrust
+            sliderXPos = _motion.curAnim.joints[0].thrust / _sliderRanges.joints[0].thrustRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // Sway Phase
+            sliderXPos = (90 + _motion.curAnim.joints[0].swayPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // Bob Phase
+            sliderXPos = (90 + _motion.curAnim.joints[0].bobPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // Thrust Phase
+            sliderXPos = (90 + _motion.curAnim.joints[0].thrustPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // offset ranges are also -ve thr' zero to +ve, so we centre them
+            sliderXPos = (((_sliderRanges.joints[0].swayOffsetRange + _motion.curAnim.joints[0]
+                .swayOffset) / 2) / _sliderRanges.joints[0].swayOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (((_sliderRanges.joints[0].bobOffsetRange + _motion.curAnim.joints[0]
+                .bobOffset) / 2) / _sliderRanges.joints[0].bobOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (((_sliderRanges.joints[0].thrustOffsetRange + _motion.curAnim.joints[0]
+                .thrustOffset) / 2) / _sliderRanges.joints[0].thrustOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+        } else {
+
+            switch (_motion.curJointIndex) {
+
+                case 0:
+                    Overlays.editOverlay(_hipsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 1:
+                    Overlays.editOverlay(_upperLegsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 2:
+                    Overlays.editOverlay(_lowerLegsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 3:
+                    Overlays.editOverlay(_feetJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 4:
+                    Overlays.editOverlay(_toesJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 5:
+                    Overlays.editOverlay(_spineJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 6:
+                    Overlays.editOverlay(_spine1JointControl, {
+                        visible: true
+                    });
+                    break;
+                case 7:
+                    Overlays.editOverlay(_spine2JointControl, {
+                        visible: true
+                    });
+                    break;
+                case 8:
+                    Overlays.editOverlay(_shouldersJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 9:
+                    Overlays.editOverlay(_upperArmsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 10:
+                    Overlays.editOverlay(_forearmsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 11:
+                    Overlays.editOverlay(_handsJointControl, {
+                        visible: true
+                    });
+                    break;
+                case 12:
+                    Overlays.editOverlay(_headJointControl, {
+                        visible: true
+                    });
+                    break;
+            }
+
+            var sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].pitch /
+                _sliderRanges.joints[_motion.curJointIndex].pitchRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].yaw /
+                _sliderRanges.joints[_motion.curJointIndex].yawRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].roll /
+                _sliderRanges.joints[_motion.curJointIndex].rollRange * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // set phases (full range, -180 to 180)
+            sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].pitchPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].yawPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].rollPhase / 2) / 180 * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            // offset ranges are also -ve thr' zero to +ve, so we offset
+            sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange +
+                _motion.curAnim.joints[_motion.curJointIndex].pitchOffset) / 2) /
+                _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].yawOffsetRange +
+                    _motion.curAnim.joints[_motion.curJointIndex].yawOffset) / 2) /
+                    _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+
+            sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].rollOffsetRange +
+                    _motion.curAnim.joints[_motion.curJointIndex].rollOffset) / 2) /
+                    _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange) * _sliderRangeX;
+            Overlays.editOverlay(_sliderThumbOverlays[++i], {
+                x: _minSliderX + sliderXPos,
+                y: yLocation += 30,
+                visible: true
+            });
+        }
+    };
+
+    // mouse event handlers
+    var _movingSliderOne = false;
+    var _movingSliderTwo = false;
+    var _movingSliderThree = false;
+    var _movingSliderFour = false;
+    var _movingSliderFive = false;
+    var _movingSliderSix = false;
+    var _movingSliderSeven = false;
+    var _movingSliderEight = false;
+    var _movingSliderNine = false;
+
+    function mousePressEvent(event) {
+
+        var clickedOverlay = Overlays.getOverlayAtPoint({
+            x: event.x,
+            y: event.y
+        });
+
+        if (_state.currentState === _state.EDIT_WALK_JOINTS ||
+            _state.currentState === _state.EDIT_STANDING ||
+            _state.currentState === _state.EDIT_FLYING ||
+            _state.currentState === _state.EDIT_FLYING_UP ||
+            _state.currentState === _state.EDIT_FLYING_DOWN ||
+            _state.currentState === _state.EDIT_SIDESTEP_LEFT ||
+            _state.currentState === _state.EDIT_SIDESTEP_RIGHT) {
+
+            // check for new joint selection and update display accordingly
+            var clickX = event.x - _backgroundX - 75;
+            var clickY = event.y - _backgroundY - 92;
+
+            if (clickX > 60 && clickX < 120 && clickY > 123 && clickY < 155) {
+                _motion.curJointIndex = 0;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 63 && clickX < 132 && clickY > 156 && clickY < 202) {
+                _motion.curJointIndex = 1;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 58 && clickX < 137 && clickY > 203 && clickY < 250) {
+                _motion.curJointIndex = 2;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 58 && clickX < 137 && clickY > 250 && clickY < 265) {
+                _motion.curJointIndex = 3;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 58 && clickX < 137 && clickY > 265 && clickY < 280) {
+                _motion.curJointIndex = 4;
+                initialiseJointsEditingPanel();
+                return;
+            } else if (clickX > 78 && clickX < 121 && clickY > 111 && clickY < 128) {
+                _motion.curJointIndex = 5;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 78 && clickX < 128 && clickY > 89 && clickY < 111) {
+                _motion.curJointIndex = 6;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 85 && clickX < 118 && clickY > 77 && clickY < 94) {
+                _motion.curJointIndex = 7;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 64 && clickX < 125 && clickY > 55 && clickY < 77) {
+                _motion.curJointIndex = 8;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if ((clickX > 44 && clickX < 73 && clickY > 71 && clickY < 94) ||
+                       (clickX > 125 && clickX < 144 && clickY > 71 && clickY < 94)) {
+                _motion.curJointIndex = 9;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if ((clickX > 28 && clickX < 57 && clickY > 94 && clickY < 119) ||
+                       (clickX > 137 && clickX < 170 && clickY > 97 && clickY < 114)) {
+                _motion.curJointIndex = 10;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if ((clickX > 18 && clickX < 37 && clickY > 115 && clickY < 136) ||
+                       (clickX > 157 && clickX < 182 && clickY > 115 && clickY < 136)) {
+                _motion.curJointIndex = 11;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 81 && clickX < 116 && clickY > 12 && clickY < 53) {
+                _motion.curJointIndex = 12;
+                initialiseJointsEditingPanel();
+                return;
+
+            } else if (clickX > 188 && clickX < 233 && clickY > 6 && clickY < 34) {
+
+                // translation editing radio selection
+                if (_state.editingTranslation) {
+
+                    hideJointSelectors();
+                    setBackground(_controlsBackgroundWalkEditJoints);
+                    _state.editingTranslation = false;
+
+                } else {
+
+                    hideJointSelectors();
+                    setBackground(_controlsBackgroundWalkEditHipTrans);
+                    _state.editingTranslation = true;
+                }
+                initialiseJointsEditingPanel();
+                return;
+            }
+        }
+
+        switch (clickedOverlay) {
+
+            case _offButton:
+
+                _state.powerOn = true;
+                Overlays.editOverlay(_offButton, {
+                    visible: false
+                });
+                Overlays.editOverlay(_onButton, {
+                    visible: true
+                });
+                _state.setInternalState(state.STANDING);
+                return;
+
+            case _controlsMinimisedTab:
+
+                _state.minimised = false;
+                minimiseDialog(_state.minimised);
+                _state.setInternalState(_state.STANDING);
+                return;
+
+            case _hideButton:
+                _hideButtonSelected:
+
+                    Overlays.editOverlay(_hideButton, {
+                        visible: false
+                    });
+                Overlays.editOverlay(_hideButtonSelected, {
+                    visible: true
+                });
+                _state.minimised = true;
+                momentaryButtonTimer = Script.setInterval(function() {
+                    minimiseDialog(_state.minimised);
+                }, 80);
+                return;
+
+            case _backButton:
+
+                Overlays.editOverlay(_backButton, {
+                    visible: false
+                });
+                Overlays.editOverlay(_backButtonSelected, {
+                    visible: true
+                });
+                momentaryButtonTimer = Script.setInterval(function() {
+
+                    _state.setInternalState(_state.STANDING);
+                    Overlays.editOverlay(_backButton, {
+                        visible: false
+                    });
+                    Overlays.editOverlay(_backButtonSelected, {
+                        visible: false
+                    });
+                    Script.clearInterval(momentaryButtonTimer);
+                    momentaryButtonTimer = null;
+                }, 80);
+                return;
+
+            case _footstepsBigButton:
+
+                _motion.makesFootStepSounds = true;
+                Overlays.editOverlay(_footstepsBigButtonSelected, {
+                    visible: true
+                });
+                Overlays.editOverlay(_footstepsBigButton, {
+                    visible: false
+                });
+                return;
+
+            case _footstepsBigButtonSelected:
+
+                _motion.makesFootStepSounds = false;
+                Overlays.editOverlay(_footstepsBigButton, {
+                    visible: true
+                });
+                Overlays.editOverlay(_footstepsBigButtonSelected, {
+                    visible: false
+                });
+                return;
+
+            case _femaleBigButton:
+            case _maleBigButtonSelected:
+
+                _motion.setGender(FEMALE);
+
+                Overlays.editOverlay(_femaleBigButtonSelected, {
+                    visible: true
+                });
+                Overlays.editOverlay(_femaleBigButton, {
+                    visible: false
+                });
+                Overlays.editOverlay(_maleBigButton, {
+                    visible: true
+                });
+                Overlays.editOverlay(_maleBigButtonSelected, {
+                    visible: false
+                });
+                return;
+
+            case _maleBigButton:
+            case _femaleBigButtonSelected:
+
+                _motion.setGender(MALE);
+
+                Overlays.editOverlay(_femaleBigButton, {
+                    visible: true
+                });
+                Overlays.editOverlay(_femaleBigButtonSelected, {
+                    visible: false
+                });
+                Overlays.editOverlay(_maleBigButtonSelected, {
+                    visible: true
+                });
+                Overlays.editOverlay(_maleBigButton, {
+                    visible: false
+                });
+                return;
+
+            case _armsFreeBigButton:
+
+                _motion.armsFree = true;
+
+                Overlays.editOverlay(_armsFreeBigButtonSelected, {
+                    visible: true
+                });
+                Overlays.editOverlay(_armsFreeBigButton, {
+                    visible: false
+                });
+                return;
+
+            case _armsFreeBigButtonSelected:
+
+                _motion.armsFree = false;
+
+                Overlays.editOverlay(_armsFreeBigButtonSelected, {
+                    visible: false
+                });
+                Overlays.editOverlay(_armsFreeBigButton, {
+                    visible: true
+                });
+                return;
+
+            case _standardWalkBigButton:
+
+                if (_motion.avatarGender === FEMALE) _motion.selWalk = _motion.femaleStandardWalk;
+                else _motion.selWalk = _motion.maleStandardWalk;
+                _motion.curAnim = _motion.selWalk;
+                initialiseWalkStylesPanel(true);
+                return;
+
+            case _standardWalkBigButtonSelected:
+
+                // toggle forwards / backwards walk display
+                if (_motion.direction === FORWARDS) {
+                    _motion.direction = BACKWARDS;
+                } else _motion.direction = FORWARDS;
+                return;
+
+            case _sliderOne:
+
+                _movingSliderOne = true;
+                return;
+
+            case _sliderTwo:
+
+                _movingSliderTwo = true;
+                return;
+
+            case _sliderThree:
+
+                _movingSliderThree = true;
+                return;
+
+            case _sliderFour:
+
+                _movingSliderFour = true;
+                return;
+
+            case _sliderFive:
+
+                _movingSliderFive = true;
+                return;
+
+            case _sliderSix:
+
+                _movingSliderSix = true;
+                return;
+
+            case _sliderSeven:
+
+                _movingSliderSeven = true;
+                return;
+
+            case _sliderEight:
+
+                _movingSliderEight = true;
+                return;
+
+            case _sliderNine:
+
+                _movingSliderNine = true;
+                return;
+
+            case _configWalkButtonSelected:
+            case _configStandButtonSelected:
+            case _configSideStepLeftButtonSelected:
+            case _configSideStepRightButtonSelected:
+            case _configFlyingButtonSelected:
+            case _configFlyingUpButtonSelected:
+            case _configFlyingDownButtonSelected:
+            case _configWalkStylesButtonSelected:
+            case _configWalkTweaksButtonSelected:
+            case _configWalkJointsButtonSelected:
+
+                // exit edit modes
+                _motion.curAnim = _motion.selStand;
+                _state.setInternalState(_state.STANDING);
+                break;
+
+            case _onButton:
+
+                _state.powerOn = false;
+                _state.setInternalState(state.STANDING);
+                Overlays.editOverlay(_offButton, {
+                    visible: true
+                });
+                Overlays.editOverlay(_onButton, {
+                    visible: false
+                });
+                //resetJoints();
+                break;
+
+            case _backButton:
+            case _backButtonSelected:
+
+                Overlays.editOverlay(_backButton, {
+                    visible: false
+                });
+                Overlays.editOverlay(_backButtonSelected, {
+                    visible: false
+                });
+                _state.setInternalState(_state.STANDING);
+                break;
+
+            case _configWalkStylesButton:
+
+                _state.setInternalState(_state.EDIT_WALK_STYLES);
+                break;
+
+            case _configWalkTweaksButton:
+
+                _state.setInternalState(_state.EDIT_WALK_TWEAKS);
+                break;
+
+            case _configWalkJointsButton:
+
+                _state.setInternalState(_state.EDIT_WALK_JOINTS);
+                break;
+
+            case _configWalkButton:
+
+                _state.setInternalState(_state.EDIT_WALK_STYLES);
+                break;
+
+            case _configStandButton:
+
+                _state.setInternalState(_state.EDIT_STANDING);
+                break;
+
+            case _configSideStepLeftButton:
+                _state.setInternalState(_state.EDIT_SIDESTEP_LEFT);
+
+                break;
+
+            case _configSideStepRightButton:
+
+                _state.setInternalState(_state.EDIT_SIDESTEP_RIGHT);
+                break;
+
+            case _configFlyingButton:
+
+                _state.setInternalState(_state.EDIT_FLYING);
+                break;
+
+            case _configFlyingUpButton:
+
+                _state.setInternalState(_state.EDIT_FLYING_UP);
+                break;
+
+            case _configFlyingDownButton:
+
+                _state.setInternalState(_state.EDIT_FLYING_DOWN);
+                break;
+        }
+    };
+
+    function mouseMoveEvent(event) {
+
+        // workaround for bug (https://worklist.net/20160)
+        if ((event.x > 310 && event.x < 318 && event.y > 1350 && event.y < 1355) ||
+           (event.x > 423 && event.x < 428 && event.y > 1505 && event.y < 1508 )) return;
+
+        if (_state.currentState === _state.EDIT_WALK_JOINTS ||
+            _state.currentState === _state.EDIT_STANDING ||
+            _state.currentState === _state.EDIT_FLYING ||
+            _state.currentState === _state.EDIT_FLYING_UP ||
+            _state.currentState === _state.EDIT_FLYING_DOWN ||
+            _state.currentState === _state.EDIT_SIDESTEP_LEFT ||
+            _state.currentState === _state.EDIT_SIDESTEP_RIGHT) {
+
+            var thumbClickOffsetX = event.x - _minSliderX;
+            var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX;
+            if (thumbPositionNormalised < 0) thumbPositionNormalised = 0;
+            if (thumbPositionNormalised > 1) thumbPositionNormalised = 1;
+            var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range
+
+            if (_movingSliderOne) {
+
+                // currently selected joint pitch or sway
+                Overlays.editOverlay(_sliderOne, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].sway =
+                        thumbPositionNormalised * _sliderRanges.joints[0].swayRange;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].pitch =
+                        thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].pitchRange;
+                }
+
+            } else if (_movingSliderTwo) {
+
+                // currently selected joint yaw or bob
+                Overlays.editOverlay(_sliderTwo, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].bob =
+                        thumbPositionNormalised * _sliderRanges.joints[0].bobRange;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].yaw =
+                        thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].yawRange;
+                }
+
+            } else if (_movingSliderThree) {
+
+                // currently selected joint roll or thrust
+                Overlays.editOverlay(_sliderThree, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].thrust =
+                        thumbPositionNormalised * _sliderRanges.joints[0].thrustRange;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].roll =
+                        thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].rollRange;
+                }
+
+            } else if (_movingSliderFour) {
+
+                // currently selected joint pitch phase
+                Overlays.editOverlay(_sliderFour, {
+                    x: sliderX + _minSliderX
+                });
+
+                var newPhase = 360 * thumbPositionNormalised - 180;
+
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].swayPhase = newPhase;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].pitchPhase = newPhase;
+                }
+
+            } else if (_movingSliderFive) {
+
+                // currently selected joint yaw phase;
+                Overlays.editOverlay(_sliderFive, {
+                    x: sliderX + _minSliderX
+                });
+
+                var newPhase = 360 * thumbPositionNormalised - 180;
+
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].bobPhase = newPhase;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].yawPhase = newPhase;
+                }
+
+            } else if (_movingSliderSix) {
+
+                // currently selected joint roll phase
+                Overlays.editOverlay(_sliderSix, {
+                    x: sliderX + _minSliderX
+                });
+
+                var newPhase = 360 * thumbPositionNormalised - 180;
+
+                if (_state.editingTranslation) {
+
+                    _motion.curAnim.joints[0].thrustPhase = newPhase;
+
+                } else {
+
+                    _motion.curAnim.joints[_motion.curJointIndex].rollPhase = newPhase;
+                }
+
+            } else if (_movingSliderSeven) {
+
+                // currently selected joint pitch offset
+                Overlays.editOverlay(_sliderSeven, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 * _sliderRanges.joints[0].swayOffsetRange;
+                    _motion.curAnim.joints[0].swayOffset = newOffset;
+
+                } else {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 * _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange;
+                    _motion.curAnim.joints[_motion.curJointIndex].pitchOffset = newOffset;
+                }
+
+            } else if (_movingSliderEight) {
+
+                // currently selected joint yaw offset
+                Overlays.editOverlay(_sliderEight, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 *_sliderRanges.joints[0].bobOffsetRange;
+                    _motion.curAnim.joints[0].bobOffset = newOffset;
+
+                } else {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 * _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange;
+                    _motion.curAnim.joints[_motion.curJointIndex].yawOffset = newOffset;
+                }
+
+            } else if (_movingSliderNine) {
+
+                // currently selected joint roll offset
+                Overlays.editOverlay(_sliderNine, {
+                    x: sliderX + _minSliderX
+                });
+                if (_state.editingTranslation) {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 * _sliderRanges.joints[0].thrustOffsetRange;
+                    _motion.curAnim.joints[0].thrustOffset = newOffset;
+
+                } else {
+
+                    var newOffset = (thumbPositionNormalised - 0.5) *
+                                     2 * _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange;
+                    _motion.curAnim.joints[_motion.curJointIndex].rollOffset = newOffset;
+                }
+            }
+
+        } // end if editing joints
+        else if (_state.currentState === _state.EDIT_WALK_TWEAKS) {
+
+            // sliders for commonly required walk adjustments
+            var thumbClickOffsetX = event.x - _minSliderX;
+            var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX;
+            if (thumbPositionNormalised < 0) thumbPositionNormalised = 0;
+            if (thumbPositionNormalised > 1) thumbPositionNormalised = 1;
+            var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range
+
+            if (_movingSliderOne) {
+
+                // walk speed
+                Overlays.editOverlay(_sliderOne, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.calibration.frequency = thumbPositionNormalised * MAX_WALK_SPEED;
+
+            } else if (_movingSliderTwo) {
+
+                // lean (hips pitch offset)
+                Overlays.editOverlay(_sliderTwo, {
+                    x: sliderX + _minSliderX
+                });
+                var newOffset = (thumbPositionNormalised - 0.5) * 2 * _sliderRanges.joints[0].pitchOffsetRange;
+                _motion.curAnim.joints[0].pitchOffset = newOffset;
+
+            } else if (_movingSliderThree) {
+
+                // stride (upper legs pitch)
+                Overlays.editOverlay(_sliderThree, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[1].pitch = thumbPositionNormalised * _sliderRanges.joints[1].pitchRange;
+
+            } else if (_movingSliderFour) {
+
+                // legs separation (upper legs roll)
+                Overlays.editOverlay(_sliderFour, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[1].rollOffset = (thumbPositionNormalised - 0.5) *
+                    2 * _sliderRanges.joints[1].rollOffsetRange;
+
+            } else if (_movingSliderFive) {
+
+                // legs forward (lower legs pitch offset)
+                Overlays.editOverlay(_sliderFive, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[1].pitchOffset = (thumbPositionNormalised - 0.5) *
+                    2 * _sliderRanges.joints[1].pitchOffsetRange;
+
+            } else if (_movingSliderSix) {
+
+                // lower legs splay (lower legs roll offset)
+                Overlays.editOverlay(_sliderSix, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[2].rollOffset = (thumbPositionNormalised - 0.5) *
+                    2 * _sliderRanges.joints[2].rollOffsetRange;
+
+            } else if (_movingSliderSeven) {
+
+                // arms forward (upper arms yaw offset)
+                Overlays.editOverlay(_sliderSeven, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[9].yawOffset = (thumbPositionNormalised - 0.5) *
+                    2 * _sliderRanges.joints[9].yawOffsetRange;
+
+            } else if (_movingSliderEight) {
+
+                // arms out (upper arm pitch offset)
+                Overlays.editOverlay(_sliderEight, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[9].pitchOffset = (thumbPositionNormalised - 0.5) *
+                    -2 * _sliderRanges.joints[9].pitchOffsetRange;
+
+            } else if (_movingSliderNine) {
+
+                // lower arms splay (lower arm pitch offset)
+                Overlays.editOverlay(_sliderNine, {
+                    x: sliderX + _minSliderX
+                });
+                _motion.curAnim.joints[10].pitchOffset = (thumbPositionNormalised - 0.5) *
+                    -2 * _sliderRanges.joints[10].pitchOffsetRange;
+            }
+
+        } // if tweaking
+    };
+
+    function mouseReleaseEvent(event) {
+
+        if (_movingSliderOne) _movingSliderOne = false;
+        else if (_movingSliderTwo) _movingSliderTwo = false;
+        else if (_movingSliderThree) _movingSliderThree = false;
+        else if (_movingSliderFour) _movingSliderFour = false;
+        else if (_movingSliderFive) _movingSliderFive = false;
+        else if (_movingSliderSix) _movingSliderSix = false;
+        else if (_movingSliderSeven) _movingSliderSeven = false;
+        else if (_movingSliderEight) _movingSliderEight = false;
+        else if (_movingSliderNine) _movingSliderNine = false;
+    };
+
+    Controller.mousePressEvent.connect(mousePressEvent);
+    Controller.mouseMoveEvent.connect(mouseMoveEvent);
+    Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
+
+    // Script ending
+    Script.scriptEnding.connect(function() {
+
+        // delete the background overlays
+        for (var i in _backgroundOverlays) {
+            Overlays.deleteOverlay(_backgroundOverlays[i]);
+        }
+        // delete the button overlays
+        for (var i in _buttonOverlays) {
+            Overlays.deleteOverlay(_buttonOverlays[i]);
+        }
+        // delete the slider thumb overlays
+        for (var i in _sliderThumbOverlays) {
+            Overlays.deleteOverlay(_sliderThumbOverlays[i]);
+        }
+        // delete the character joint control overlays
+        for (var i in _jointsControlOverlays) {
+            Overlays.deleteOverlay(_jointsControlOverlays[i]);
+        }
+        // delete the big button overlays
+        for (var i in _bigbuttonOverlays) {
+            Overlays.deleteOverlay(_bigbuttonOverlays[i]);
+        }
+        // delete the mimimised tab
+        Overlays.deleteOverlay(_controlsMinimisedTab);
+    });
+
+    // public methods
+    return {
+
+        // gather references to objects from the walk.js script
+        initialise: function(state, motion, walkAssets) {
+
+            _state = state;
+            _motion = motion;
+            _walkAssets = walkAssets;
+        },
+
+        updateMenu: function() {
+
+            if (!_state.minimised) {
+
+                switch (_state.currentState) {
+
+                    case _state.EDIT_WALK_STYLES:
+                    case _state.EDIT_WALK_TWEAKS:
+                    case _state.EDIT_WALK_JOINTS:
+
+                        hideMenuButtons();
+                        initialiseFrontPanel(false);
+                        hideJointSelectors();
+
+                        if (_state.currentState === _state.EDIT_WALK_STYLES) {
+
+                            setBackground(_controlsBackgroundWalkEditStyles);
+                            initialiseWalkStylesPanel(true);
+                            setSliderThumbsVisible(false);
+                            hideJointSelectors();
+                            setButtonOverlayVisible(_configWalkStylesButtonSelected);
+                            setButtonOverlayVisible(_configWalkTweaksButton);
+                            setButtonOverlayVisible(_configWalkJointsButton);
+
+                        } else if (_state.currentState === _state.EDIT_WALK_TWEAKS) {
+
+                            setBackground(_controlsBackgroundWalkEditTweaks);
+                            initialiseWalkStylesPanel(false);
+                            setSliderThumbsVisible(true);
+                            hideJointSelectors();
+                            initialiseWalkTweaksPanel();
+                            setButtonOverlayVisible(_configWalkStylesButton);
+                            setButtonOverlayVisible(_configWalkTweaksButtonSelected);
+                            setButtonOverlayVisible(_configWalkJointsButton);
+
+                        } else if (_state.currentState === _state.EDIT_WALK_JOINTS) {
+
+                            if (_state.editingTranslation)
+                                setBackground(_controlsBackgroundWalkEditHipTrans);
+                            else
+                                setBackground(_controlsBackgroundWalkEditJoints);
+
+                            initialiseWalkStylesPanel(false);
+                            setSliderThumbsVisible(true);
+                            setButtonOverlayVisible(_configWalkStylesButton);
+                            setButtonOverlayVisible(_configWalkTweaksButton);
+                            setButtonOverlayVisible(_configWalkJointsButtonSelected);
+                            initialiseJointsEditingPanel();
+                        }
+                        setButtonOverlayVisible(_onButton);
+                        setButtonOverlayVisible(_backButton);
+                        return;
+
+                    case _state.EDIT_STANDING:
+                    case _state.EDIT_SIDESTEP_LEFT:
+                    case _state.EDIT_SIDESTEP_RIGHT:
+
+                        if (_state.editingTranslation)
+                            setBackground(_controlsBackgroundWalkEditHipTrans);
+                        else
+                            setBackground(_controlsBackgroundWalkEditJoints);
+                        hideMenuButtons();
+                        initialiseWalkStylesPanel(false);
+                        initialiseFrontPanel(false);
+
+                        if (_state.currentState === _state.EDIT_SIDESTEP_LEFT) {
+
+                            setButtonOverlayVisible(_configSideStepRightButton);
+                            setButtonOverlayVisible(_configSideStepLeftButtonSelected);
+                            setButtonOverlayVisible(_configStandButton);
+
+                        } else if (_state.currentState === _state.EDIT_SIDESTEP_RIGHT) {
+
+                            setButtonOverlayVisible(_configSideStepRightButtonSelected);
+                            setButtonOverlayVisible(_configSideStepLeftButton);
+                            setButtonOverlayVisible(_configStandButton);
+
+                        } else if (_state.currentState === _state.EDIT_STANDING) {
+
+                            setButtonOverlayVisible(_configSideStepRightButton);
+                            setButtonOverlayVisible(_configSideStepLeftButton);
+                            setButtonOverlayVisible(_configStandButtonSelected);
+                        }
+                        initialiseJointsEditingPanel();
+                        setButtonOverlayVisible(_onButton);
+                        setButtonOverlayVisible(_backButton);
+                        return;
+
+                    case _state.EDIT_FLYING:
+                    case _state.EDIT_FLYING_UP:
+                    case _state.EDIT_FLYING_DOWN:
+
+                        setBackground(_controlsBackgroundWalkEditJoints);
+                        hideMenuButtons();
+                        initialiseWalkStylesPanel(false);
+                        initialiseFrontPanel(false);
+                        if (_state.currentState === _state.EDIT_FLYING) {
+
+                            setButtonOverlayVisible(_configFlyingUpButton);
+                            setButtonOverlayVisible(_configFlyingDownButton);
+                            setButtonOverlayVisible(_configFlyingButtonSelected);
+
+                        } else if (_state.currentState === _state.EDIT_FLYING_UP) {
+
+                            setButtonOverlayVisible(_configFlyingUpButtonSelected);
+                            setButtonOverlayVisible(_configFlyingDownButton);
+                            setButtonOverlayVisible(_configFlyingButton);
+
+                        } else if (_state.currentState === _state.EDIT_FLYING_DOWN) {
+
+                            setButtonOverlayVisible(_configFlyingUpButton);
+                            setButtonOverlayVisible(_configFlyingDownButtonSelected);
+                            setButtonOverlayVisible(_configFlyingButton);
+                        }
+                        initialiseJointsEditingPanel();
+                        setButtonOverlayVisible(_onButton);
+                        setButtonOverlayVisible(_backButton);
+                        return;
+
+                    case _state.STANDING:
+                    case _state.WALKING:
+                    case _state.FLYING:
+                    case _state.SIDE_STEP:
+                    default:
+
+                        hideMenuButtons();
+                        hideJointSelectors();
+                        setBackground(_controlsBackground);
+                        if (_state.powerOn) setButtonOverlayVisible(_onButton);
+                        else setButtonOverlayVisible(_offButton);
+                        setButtonOverlayVisible(_configWalkButton);
+                        setButtonOverlayVisible(_configStandButton);
+                        setButtonOverlayVisible(_configFlyingButton);
+                        setButtonOverlayVisible(_hideButton);
+                        setSliderThumbsVisible(false);
+                        initialiseFrontPanel(true);
+                        initialiseWalkStylesPanel(false);
+                        return;
+                }
+            }
+        }
+    }; // end public methods (return)
+
+})();
\ No newline at end of file
diff --git a/examples/walk.js b/examples/walk.js
index a9e8f401d6..1e1cd7748b 100644
--- a/examples/walk.js
+++ b/examples/walk.js
@@ -1,3851 +1,2562 @@
 //
 //  walk.js
 //
-//  version 1.007b
+//  version 1.1
 //
-//  Created by Davedub, August / September 2014
+//  Created by David Wooldridge, Autumn 2014
+//
+//  Animates an avatar using procedural animation techniques
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-// path to the animation files
-var pathToAnimFiles = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/animation-files/';	// working (but only without https)
-
-// path to the images used for the overlays
-var pathToOverlays = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/overlays/';			// working (but only without https)
-
-// path to the sounds used for the footsteps
-var pathToSounds = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/';
-
-// load the animation datafiles
-Script.include(pathToAnimFiles+"dd-female-strut-walk-animation.js");
-Script.include(pathToAnimFiles+"dd-female-flying-up-animation.js");
-Script.include(pathToAnimFiles+"dd-female-flying-animation.js");
-Script.include(pathToAnimFiles+"dd-female-flying-down-animation.js");
-Script.include(pathToAnimFiles+"dd-female-standing-one-animation.js");
-Script.include(pathToAnimFiles+"dd-female-sidestep-left-animation.js");
-Script.include(pathToAnimFiles+"dd-female-sidestep-right-animation.js");
-Script.include(pathToAnimFiles+"dd-male-strut-walk-animation.js");
-Script.include(pathToAnimFiles+"dd-male-flying-up-animation.js");
-Script.include(pathToAnimFiles+"dd-male-flying-animation.js");
-Script.include(pathToAnimFiles+"dd-male-flying-down-animation.js");
-Script.include(pathToAnimFiles+"dd-male-standing-one-animation.js");
-Script.include(pathToAnimFiles+"dd-male-sidestep-left-animation.js");
-Script.include(pathToAnimFiles+"dd-male-sidestep-right-animation.js");
-
-// read in the data from the animation files
-var FemaleStrutWalkFile = new FemaleStrutWalk();
-var femaleStrutWalk = FemaleStrutWalkFile.loadAnimation();
-var FemaleFlyingUpFile = new FemaleFlyingUp();
-var femaleFlyingUp = FemaleFlyingUpFile.loadAnimation();
-var FemaleFlyingFile = new FemaleFlying();
-var femaleFlying = FemaleFlyingFile.loadAnimation();
-var FemaleFlyingDownFile = new FemaleFlyingDown();
-var femaleFlyingDown = FemaleFlyingDownFile.loadAnimation();
-var FemaleStandOneFile = new FemaleStandingOne();
-var femaleStandOne = FemaleStandOneFile.loadAnimation();
-var FemaleSideStepLeftFile = new FemaleSideStepLeft();
-var femaleSideStepLeft = FemaleSideStepLeftFile.loadAnimation();
-var FemaleSideStepRightFile = new FemaleSideStepRight();
-var femaleSideStepRight = FemaleSideStepRightFile.loadAnimation();
-var MaleStrutWalkFile = new MaleStrutWalk();
-var maleStrutWalk = MaleStrutWalkFile.loadAnimation();
-var MaleFlyingUpFile = new MaleFlyingUp();
-var maleFlyingUp = MaleFlyingUpFile.loadAnimation();
-var MaleFlyingFile = new MaleFlying();
-var maleFlying = MaleFlyingFile.loadAnimation();
-var MaleFlyingDownFile = new MaleFlyingDown();
-var maleFlyingDown = MaleFlyingDownFile.loadAnimation();
-var MaleStandOneFile = new MaleStandingOne();
-var maleStandOne = MaleStandOneFile.loadAnimation();
-var MaleSideStepLeftFile = new MaleSideStepLeft();
-var maleSideStepLeft = MaleSideStepLeftFile.loadAnimation();
-var MaleSideStepRightFile = new MaleSideStepRight();
-var maleSideStepRight = MaleSideStepRightFile.loadAnimation();
-
-// read in the sounds
-var footsteps = [];
-footsteps.push(new Sound(pathToSounds+"FootstepW2Left-12db.wav"));
-footsteps.push(new Sound(pathToSounds+"FootstepW2Right-12db.wav"));
-footsteps.push(new Sound(pathToSounds+"FootstepW3Left-12db.wav"));
-footsteps.push(new Sound(pathToSounds+"FootstepW3Right-12db.wav"));
-footsteps.push(new Sound(pathToSounds+"FootstepW5Left-12db.wav"));
-footsteps.push(new Sound(pathToSounds+"FootstepW5Right-12db.wav"));
-
-// all slider controls have a range (with the exception of phase controls that are always +-180) so we store them all here
-var sliderRanges = {"joints":[{"name":"hips","pitchRange":25,"yawRange":25,"rollRange":25,"pitchOffsetRange":25,"yawOffsetRange":25,"rollOffsetRange":25,"thrustRange":0.01,"bobRange":0.02,"swayRange":0.01},{"name":"upperLegs","pitchRange":90,"yawRange":35,"rollRange":35,"pitchOffsetRange":60,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"lowerLegs","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"feet","pitchRange":60,"yawRange":20,"rollRange":20,"pitchOffsetRange":60,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"toes","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"spine","pitchRange":40,"yawRange":40,"rollRange":40,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine1","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine2","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"shoulders","pitchRange":35,"yawRange":40,"rollRange":20,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"upperArms","pitchRange":90,"yawRange":90,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"lowerArms","pitchRange":90,"yawRange":90,"rollRange":120,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"hands","pitchRange":90,"yawRange":180,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"head","pitchRange":20,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":90,"rollOffsetRange":90}]};
-
-//  internal state (FSM based) constants
-var STANDING = 1;
-var WALKING = 2;
-var SIDE_STEPPING = 3;
-var FLYING = 4;
-var CONFIG_WALK_STYLES = 5;
-var CONFIG_WALK_TWEAKS = 6;
-var CONFIG_WALK_JOINTS = 7;
-var CONFIG_STANDING = 8;
-var CONFIG_FLYING = 9;
-var CONFIG_FLYING_UP = 10;
-var CONFIG_FLYING_DOWN = 11;
-var CONFIG_SIDESTEP_LEFT = 12;
-var CONFIG_SIDESTEP_RIGHT = 14;
-var INTERNAL_STATE = STANDING;
-
-// status
-var powerOn = true;
-var paused = false; // pause animation playback whilst adjusting certain parameters
-var minimised = true;
-var armsFree = true; // set true for hydra support - temporary fix for Hydras
-var statsOn = false;
-var playFootStepSounds = true;
-
 // constants
-var MAX_WALK_SPEED = 1257; // max oscillation speed
-var FLYING_SPEED = 6.4;// 12.4;  // m/s - real humans can't run any faster than 12.4 m/s
-var TERMINAL_VELOCITY = 300;  // max speed imposed by Interface
-var DIRECTION_UP = 1;
-var DIRECTION_DOWN = 2;
-var DIRECTION_LEFT = 4;
-var DIRECTION_RIGHT = 8;
-var DIRECTION_FORWARDS = 16;
-var DIRECTION_BACKWARDS = 32;
-var DIRECTION_NONE = 64;
 var MALE = 1;
 var FEMALE = 2;
+var MAX_WALK_SPEED = 2.5;
+var TAKE_FLIGHT_SPEED = 4.55;
+var TOP_SPEED = 300;
+var UP = 1;
+var DOWN = 2;
+var LEFT = 4;
+var RIGHT = 8;
+var FORWARDS = 16;
+var BACKWARDS = 32;
 
-// start of animation control section
-var cumulativeTime = 0.0;
-var lastOrientation;
+// ovelay images location
+var pathToAssets = 'http://s3.amazonaws.com/hifi-public/WalkScript/';
 
-// avi gender and default animations
-var avatarGender = MALE;
-var selectedWalk = maleStrutWalk; // the currently selected animation walk file (to edit any animation, paste it's name here and select walk editing mode)
-var selectedStand = maleStandOne;
-var selectedFlyUp = maleFlyingUp;
-var selectedFly = maleFlying;
-var selectedFlyDown = maleFlyingDown;
-var selectedSideStepLeft = maleSideStepLeft;
-var selectedSideStepRight = maleSideStepRight;
-if(avatarGender===FEMALE) {
+// load the UI
+Script.include("./libraries/walkInterface.js");
 
-	// to make toggling the default quick
-	selectedWalk = femaleStrutWalk;
-	selectedStand = femaleStandOne;
-	selectedFlyUp = femaleFlyingUp;
-	selectedFly = femaleFlying;
-	selectedFlyDown = femaleFlyingDown;
-	selectedSideStepLeft = femaleSideStepLeft;
-	selectedSideStepRight = femaleSideStepRight;
-}
-var currentAnimation = selectedStand; // the current animation
-var selectedJointIndex = 0; // the index of the joint currently selected for editing
-var currentTransition = null; // used as a pointer to a Transition
+// load filters (Bezier, Butterworth, add harmonics, averaging)
+Script.include("./libraries/walkFilters.js");
 
-// walkwheel (foot / ground speed matching)
-var sideStepCycleStartLeft = 270;
-var sideStepCycleStartRight = 90;
-var walkWheelPosition = 0;
-var nextStep = DIRECTION_RIGHT; // first step is always right, because the sine waves say so. Unless you're mirrored.
-var nFrames = 0; // counts number of frames
-var strideLength = 0; // stride calibration
-var aviFootSize = {x:0.1, y:0.1, z:0.25}; // experimental values for addition to stride length - TODO: analyse and confirm is increasing smaller stride lengths accuracy once we have better ground detection
+// load objects, constructors and assets (state, Motion, Transition, walkAssets)
+Script.include("./libraries/walkApi.js");
 
-// stats
-var frameStartTime = 0;  // when the frame first starts we take a note of the time
-var frameExecutionTimeMax = 0; // keep track of the longest frame execution time
+// initialise the motion state / history object
+var motion = new Motion();
 
-// constructor for recent RecentMotion (i.e. frame data) class
-function RecentMotion(velocity, acceleration, principleDirection, state) {
-	this.velocity = velocity;
-	this.acceleration = acceleration;
-	this.principleDirection = principleDirection;
-	this.state = state;
-}
+// initialise Transitions
+var nullTransition = new Transition();
+motion.curTransition = nullTransition;
 
-// constructor for the FramesHistory object
-function FramesHistory() {
-	this.recentMotions = [];
-	for(var i = 0 ; i < 10 ; i++) {
-		var blank = new RecentMotion({ x:0, y:0, z:0 }, { x:0, y:0, z:0 }, DIRECTION_FORWARDS, STANDING );
-		this.recentMotions.push(blank);
-	}
+// initialise the UI
+walkInterface.initialise(state, motion, walkAssets);
 
-	// recentDirection 'method' args: (direction enum, number of steps back to compare)
-	this.recentDirection = function () {
+// wave shapes
+var SAWTOOTH = 1;
+var TRIANGLE = 2;
+var SQUARE = 4;
 
-		if( arguments[0] && arguments[1] ) {
+// filters for synthesising more complex, natural waveforms
+var leanPitchFilter = filter.createAveragingFilter(15);
+var leanRollFilter = filter.createAveragingFilter(15);
+var hipsYawShaper = filter.createWaveSynth(TRIANGLE, 3, 2);
+var hipsBobLPFilter = filter.createButterworthFilter(5);
 
-			var directionCount = 0;
-			if( arguments[1] > this.recentMotions.length )
-				arguments[1] = this.recentMotions.length;
-
-			for(var i = 0 ; i < arguments[1] ; i++ ) {
-				if( this.recentMotions[i].principleDirection === arguments[0] )
-					directionCount++;
-			}
-			return directionCount / arguments[1] === 1 ? true : false;
-		}
-		return false;
-	}
-
-	// recentState 'method' args: (state enum, number of steps back to compare)
-	this.recentState = function () {
-
-		if( arguments[0] && arguments[1] ) {
-
-			var stateCount = 0;
-			if( arguments[1] > this.recentMotions.length )
-				arguments[1] = this.recentMotions.length;
-
-			for(var i = 0 ; i < arguments[1] ; i++ ) {
-				if( this.recentMotions[i].state === arguments[0] )
-					stateCount++;
-			}
-			return stateCount / arguments[1] === 1 ? true : false;
-		}
-		return false;
-	}
-	this.lastWalkStartTime = 0; 	// short walks and long walks need different handling
-}
-var framesHistory = new FramesHistory();
-
-// constructor for animation Transition class
-function Transition(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) {
-	this.lastAnimation = lastAnimation; 			// name of last animation
-	if(lastAnimation === selectedWalk ||
-	   nextAnimation === selectedSideStepLeft ||
-	   nextAnimation === selectedSideStepRight)
-		this.walkingAtStart = true;					// boolean - is the last animation a walking animation?
-	else
-		this.walkingAtStart = false;					// boolean - is the last animation a walking animation?
-	this.nextAnimation = nextAnimation;				// name of next animation
-	if(nextAnimation === selectedWalk ||
-	   nextAnimation === selectedSideStepLeft ||
-	   nextAnimation === selectedSideStepRight)
-		this.walkingAtEnd = true;						// boolean - is the next animation a walking animation?
-	else
-		this.walkingAtEnd = false;					// boolean - is the next animation a walking animation?
-	this.reachPoses = reachPoses;					// array of reach poses - am very much looking forward to putting these in!
-	this.transitionDuration = transitionDuration;	// length of transition (seconds)
-	this.easingLower = easingLower;					// Bezier curve handle (normalised)
-	this.easingUpper = easingUpper;					// Bezier curve handle (normalised)
-	this.startTime = new Date().getTime();			// Starting timestamp (seconds)
-	this.progress = 0;								// how far are we through the transition?
-	this.walkWheelIncrement = 3;					// how much to turn the walkwheel each frame when coming to a halt. Get's set to 0 once the feet are under the avi
-	this.walkWheelAdvance = 0;						// how many degrees the walk wheel has been advanced during the transition
-	this.walkStopAngle = 0;							// what angle should we stop the walk cycle? (calculated on the fly)
-}
-
-// convert a local (to the avi) translation to a global one
-function localToGlobal(localTranslation) {
-
-	var aviOrientation = MyAvatar.orientation;
-	var front = Quat.getFront(aviOrientation);
-	var right = Quat.getRight(aviOrientation);
-	var up    = Quat.getUp   (aviOrientation);
-	var aviFront = Vec3.multiply(front,localTranslation.z);
-	var aviRight = Vec3.multiply(right,localTranslation.x);
-	var aviUp    = Vec3.multiply(up   ,localTranslation.y);
-	var globalTranslation = {x:0,y:0,z:0}; // final value
-	globalTranslation = Vec3.sum(globalTranslation, aviFront);
-	globalTranslation = Vec3.sum(globalTranslation, aviRight);
-	globalTranslation = Vec3.sum(globalTranslation, aviUp);
-	return globalTranslation;
-}
-
-// similar ot above - convert hips translations to global and apply
-function translateHips(localHipsTranslation) {
-
-	var aviOrientation = MyAvatar.orientation;
-	var front = Quat.getFront(aviOrientation);
-	var right = Quat.getRight(aviOrientation);
-	var up    = Quat.getUp   (aviOrientation);
-	var aviFront = Vec3.multiply(front,localHipsTranslation.y);
-	var aviRight = Vec3.multiply(right,localHipsTranslation.x);
-	var aviUp    = Vec3.multiply(up   ,localHipsTranslation.z);
-	var AviTranslationOffset = {x:0,y:0,z:0}; // final value
-	AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviFront);
-	AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviRight);
-	AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviUp);
-	MyAvatar.position = {x: MyAvatar.position.x + AviTranslationOffset.x,
-						 y: MyAvatar.position.y + AviTranslationOffset.y,
-						 z: MyAvatar.position.z + AviTranslationOffset.z };
-}
-
-// clear all joint data
-function resetJoints() {
-
-    var avatarJointNames = MyAvatar.getJointNames();
-    for (var i = 0; i < avatarJointNames.length; i++) {
-        MyAvatar.clearJointData(avatarJointNames[i]);
-    }
-}
-
-// play footstep sound
-function playFootstep(side) {
-
-    var options = new AudioInjectionOptions();
-    options.position = Camera.getPosition();
-    options.volume = 0.5;
-    var walkNumber = 2; // 0 to 2
-    if(side===DIRECTION_RIGHT && playFootStepSounds) {
-		Audio.playSound(footsteps[walkNumber+1], options);
-	}
-    else if(side===DIRECTION_LEFT && playFootStepSounds) {
-		Audio.playSound(footsteps[walkNumber], options);
-	}
-}
-
-// put the fingers into a relaxed pose
-function curlFingers() {
-
-    // left hand fingers
-    for(var i = 18 ; i < 34 ; i++) {
-        MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0));
-    }
-    // left hand thumb
-    for(var i = 34 ; i < 38 ; i++) {
-        MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0));
-    }
-    // right hand fingers
-    for(var i = 42 ; i < 58 ; i++) {
-        MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0));
-    }
-    // right hand thumb
-    for(var i = 58 ; i < 62 ; i++) {
-        MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0));
-    }
-}
-
-// additional maths functions
-function degToRad( degreesValue ) { return degreesValue * Math.PI / 180; }
-function radToDeg( radiansValue ) { return radiansValue * 180 / Math.PI; }
-
-// animateAvatar - sine wave generators working like clockwork
-function animateAvatar( deltaTime, velocity, principleDirection ) {
-
-	    // adjusting the walk speed in edit mode causes a nasty flicker. pausing the animation stops this
-        if(paused) return;
-
-		var cycle = cumulativeTime;
-        var transitionProgress = 1;
-        var adjustedFrequency = currentAnimation.settings.baseFrequency;
-
-		// upper legs phase reversal for walking backwards
-        var forwardModifier = 1;
-        if(principleDirection===DIRECTION_BACKWARDS)
-			forwardModifier = -1;
-
-        // don't want to lean forwards if going directly upwards
-        var leanPitchModifier = 1;
-        if(principleDirection===DIRECTION_UP)
-			leanPitchModifier = 0;
-
-        // is there a Transition to include?
-        if(currentTransition!==null) {
-
-			// if is a new transiton
-			if(currentTransition.progress===0) {
-
-				if( currentTransition.walkingAtStart ) {
-
-					if( INTERNAL_STATE !== SIDE_STEPPING ) {
-
-						// work out where we want the walk cycle to stop
-						var leftStop  = selectedWalk.settings.stopAngleForwards + 180;
-						var rightStop = selectedWalk.settings.stopAngleForwards;
-						if( principleDirection === DIRECTION_BACKWARDS ) {
-							leftStop  = selectedWalk.settings.stopAngleBackwards + 180;
-							rightStop = selectedWalk.settings.stopAngleBackwards;
-						}
-
-						// find the closest stop point from the walk wheel's angle
-						var angleToLeftStop  = 180 - Math.abs( Math.abs( walkWheelPosition - leftStop  ) - 180);
-						var angleToRightStop = 180 - Math.abs( Math.abs( walkWheelPosition - rightStop ) - 180);
-						if( walkWheelPosition > angleToLeftStop ) angleToLeftStop = 360 - angleToLeftStop;
-						if( walkWheelPosition > angleToRightStop ) angleToRightStop = 360 - angleToRightStop;
-
-						currentTransition.walkWheelIncrement = 6;
-
-						// keep the walkwheel turning by setting the walkWheelIncrement until our feet are tucked nicely underneath us.
-						if( angleToLeftStop < angleToRightStop ) {
-
-							currentTransition.walkStopAngle = leftStop;
-
-						} else {
-
-							currentTransition.walkStopAngle = rightStop;
-						}
-
-					} else {
-
-						// freeze wheel for sidestepping transitions
-						currentTransition.walkWheelIncrement = 0;
-					}
-				}
-			} // end if( currentTransition.walkingAtStart )
-
-			// calculate the Transition progress
-			var elapasedTime = (new Date().getTime() - currentTransition.startTime) / 1000;
-			currentTransition.progress = elapasedTime / currentTransition.transitionDuration;
-			transitionProgress = getBezier((1-currentTransition.progress), {x:0,y:0}, currentTransition.easingLower, currentTransition.easingUpper, {x:1,y:1}).y;
-
-			if(currentTransition.progress>=1) {
-
-				// time to kill off the transition
-				delete currentTransition;
-				currentTransition = null;
-
-			} else {
-
-				if( currentTransition.walkingAtStart ) {
-
-					if( INTERNAL_STATE !== SIDE_STEPPING ) {
-
-						// if at a stop angle, hold the walk wheel position for remainder of transition
-						var tolerance = 7;  // must be greater than the walkWheel increment
-						if(( walkWheelPosition > (currentTransition.walkStopAngle - tolerance )) &&
-						   ( walkWheelPosition < (currentTransition.walkStopAngle + tolerance ))) {
-
-							currentTransition.walkWheelIncrement = 0;
-						}
-						// keep turning walk wheel until both feet are below the avi
-						walkWheelPosition += currentTransition.walkWheelIncrement;
-						currentTransition.walkWheelAdvance += currentTransition.walkWheelIncrement;
-					}
-				}
-			}
-		}
-
-        // will we need to use the walk wheel this frame?
-        if(currentAnimation  === selectedWalk ||
-           currentAnimation  === selectedSideStepLeft ||
-           currentAnimation  === selectedSideStepRight ||
-           currentTransition !== null) {
-
-			// set the stride length
-			if(	INTERNAL_STATE!==SIDE_STEPPING &&
-		    	INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT &&
-		    	INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT &&
-				currentTransition===null ) {
-
-				// if the timing's right, take a snapshot of the stride max and recalibrate
-				var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy
-				var strideOne = 40;
-				var strideTwo = 220;
-
-				if( principleDirection === DIRECTION_BACKWARDS ) {
-					strideOne = 130;
-					strideTwo = 300;
-				}
-
-				if(( walkWheelPosition < (strideOne+tolerance) && walkWheelPosition > (strideOne-tolerance) ) ||
-				  (  walkWheelPosition < (strideTwo+tolerance) && walkWheelPosition > (strideTwo-tolerance) )) {
-
-					// calculate the feet's offset from each other (in local Z only)
-					var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
-					var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
-					var footOffsetZ = Math.abs(footRPos.z - footLPos.z);
-					if(footOffsetZ>1) strideLength = 2 * footOffsetZ + aviFootSize.z; // sometimes getting very low value here - just ignore for now
-					if(statsOn) print('Stride length calibrated to '+strideLength.toFixed(4)+' metres at '+walkWheelPosition.toFixed(1)+' degrees');
-					if(principleDirection===DIRECTION_FORWARDS) {
-						currentAnimation.calibration.strideLengthForwards = strideLength;
-					} else if (principleDirection===DIRECTION_BACKWARDS) {
-						currentAnimation.calibration.strideLengthBackwards = strideLength;
-					}
-
-				} else {
-
-					if(principleDirection===DIRECTION_FORWARDS)
-						strideLength = currentAnimation.calibration.strideLengthForwards;
-					else if (principleDirection===DIRECTION_BACKWARDS)
-						strideLength = currentAnimation.calibration.strideLengthBackwards;
-				}
-			}
-			else if(( INTERNAL_STATE===SIDE_STEPPING ||
-		        	  INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-		    		  INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT )  ) {
-
-				// calculate lateral stride - same same, but different
-				// if the timing's right, take a snapshot of the stride max and recalibrate the stride length
-				var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy
-				if(principleDirection===DIRECTION_LEFT) {
-
-					if( walkWheelPosition < (3+tolerance) && walkWheelPosition > (3-tolerance) ) {
-
-						// calculate the feet's offset from the hips (in local X only)
-						var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
-						var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
-						var footOffsetX = Math.abs(footRPos.x - footLPos.x);
-						strideLength = footOffsetX;
-						if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees');
-						currentAnimation.calibration.strideLengthLeft = strideLength;
-
-					} else {
-
-						strideLength = currentAnimation.calibration.strideLengthLeft;
-					}
-				}
-				else if (principleDirection===DIRECTION_RIGHT) {
-
-					if( walkWheelPosition < (170+tolerance) && walkWheelPosition > (170-tolerance) ) {
-
-						// calculate the feet's offset from the hips (in local X only)
-						var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
-						var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
-						var footOffsetX = Math.abs(footRPos.x - footLPos.x);
-						strideLength = footOffsetX;
-						if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees');
-						currentAnimation.calibration.strideLengthRight = strideLength;
-
-					} else {
-
-						strideLength = currentAnimation.calibration.strideLengthRight;
-					}
-				}
-			} // end stride length calculations
-
-			// wrap the stride length around a 'surveyor's wheel' twice and calculate the angular velocity at the given (linear) velocity
-			// omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride length
-			var wheelRadius = strideLength / Math.PI;
-			var angularVelocity = velocity / wheelRadius;
-
-			// calculate the degrees turned (at this angular velocity) since last frame
-			var radiansTurnedSinceLastFrame = deltaTime * angularVelocity;
-			var degreesTurnedSinceLastFrame = radToDeg(radiansTurnedSinceLastFrame);
-
-			// if we are in an edit mode, we will need fake time to turn the wheel
-			if( INTERNAL_STATE!==WALKING &&
-				INTERNAL_STATE!==SIDE_STEPPING )
-				degreesTurnedSinceLastFrame = currentAnimation.settings.baseFrequency / 70;
-
-			if( walkWheelPosition >= 360 )
-				walkWheelPosition = walkWheelPosition % 360;
-
-			// advance the walk wheel the appropriate amount
-			if( currentTransition===null || currentTransition.walkingAtEnd )
-			    walkWheelPosition += degreesTurnedSinceLastFrame;
-
-			// set the new values for the exact correct walk cycle speed at this velocity
-			adjustedFrequency = 1;
-			cycle = walkWheelPosition;
-
-			// show stats and walk wheel?
-			if(statsOn) {
-
-				var distanceTravelled = velocity * deltaTime;
-				var deltaTimeMS = deltaTime * 1000;
-
-				if( INTERNAL_STATE===SIDE_STEPPING ||
-		    		INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-		    		INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
-
-					// draw the walk wheel turning around the z axis for sidestepping
-					var directionSign = 1;
-					if(principleDirection===DIRECTION_RIGHT) directionSign = -1;
-					var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down
-					var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( directionSign * walkWheelPosition ));
-					var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( directionSign * -walkWheelPosition ));
-					var wheelXPos = {x: cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0};
-					var wheelXEnd = {x: -cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0};
-					sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( -directionSign * walkWheelPosition+90 ));
-					cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( -directionSign * walkWheelPosition+90 ));
-					var wheelYPos = {x:cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0};
-					var wheelYEnd = {x:-cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0};
-					Overlays.editOverlay(walkWheelYLine, {visible: true, position:wheelYPos, end:wheelYEnd});
-					Overlays.editOverlay(walkWheelZLine, {visible: true, position:wheelXPos, end:wheelXEnd});
-
-				} else {
-
-					// draw the walk wheel turning around the x axis for walking forwards or backwards
-					var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down
-					var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad((forwardModifier*-1) * walkWheelPosition));
-					var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad((forwardModifier*-1) * -walkWheelPosition));
-					var wheelZPos = {x:0, y:-sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
-					var wheelZEnd = {x:0, y:sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
-					sinWalkWheelPosition = wheelRadius * Math.sin(degToRad(forwardModifier * walkWheelPosition+90));
-					cosWalkWheelPosition = wheelRadius * Math.cos(degToRad(forwardModifier * walkWheelPosition+90));
-					var wheelYPos = {x:0, y:sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
-					var wheelYEnd = {x:0, y:-sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
-					Overlays.editOverlay(walkWheelYLine, { visible: true, position:wheelYPos, end:wheelYEnd });
-					Overlays.editOverlay(walkWheelZLine, { visible: true, position:wheelZPos, end:wheelZEnd });
-				}
-
-				// populate stats overlay
-				var walkWheelInfo =
-					'         Walk Wheel Stats\n--------------------------------------\n \n \n'
-					+ '\nFrame time: '+deltaTimeMS.toFixed(2)
-					+ ' mS\nVelocity: '+velocity.toFixed(2)
-					+ ' m/s\nDistance: '+distanceTravelled.toFixed(3)
-					+ ' m\nOmega: '+angularVelocity.toFixed(3)
-					+ ' rad / s\nDeg to turn: '+degreesTurnedSinceLastFrame.toFixed(2)
-					+ ' deg\nWheel position: '+cycle.toFixed(1)
-					+ ' deg\nWheel radius: '+wheelRadius.toFixed(3)
-					+ ' m\nHips To Feet: '+hipsToFeetDistance.toFixed(3)
-					+ ' m\nStride: '+strideLength.toFixed(3)
-					+ ' m\n';
-				Overlays.editOverlay(walkWheelStats, {text: walkWheelInfo});
-			}
-		} // end of walk wheel and stride length calculation
-
-
-		// Start applying motion
-		var pitchOscillation = 0;
-		var yawOscillation = 0;
-		var rollOscillation = 0;
-		var pitchOscillationLast = 0;
-		var yawOscillationLast = 0;
-		var rollOscillationLast = 0;
-        var pitchOffset = 0;
-        var yawOffset = 0;
-        var rollOffset = 0;
-        var pitchOffsetLast = 0;
-        var yawOffsetLast = 0;
-        var rollOffsetLast = 0;
-
-		// calcualte any hips translation
-		// Note: can only apply hips translations whilst in a config (edit) mode at present, not whilst under locomotion
-		if( INTERNAL_STATE===CONFIG_WALK_STYLES ||
-		    INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
-		    INTERNAL_STATE===CONFIG_WALK_JOINTS ||
-		    INTERNAL_STATE===CONFIG_SIDESTEP_LEFT  ||
-		    INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ||
-		    INTERNAL_STATE===CONFIG_FLYING ||
-		    INTERNAL_STATE===CONFIG_FLYING_UP ||
-		    INTERNAL_STATE===CONFIG_FLYING_DOWN ) {
-
-			// calculate hips translation
-			var motorOscillation  = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].thrustPhase)) + currentAnimation.joints[0].thrustOffset;
-			var swayOscillation   = Math.sin(degToRad((cycle * adjustedFrequency    ) + currentAnimation.joints[0].swayPhase)) + currentAnimation.joints[0].swayOffset;
-			var bobOscillation    = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].bobPhase)) + currentAnimation.joints[0].bobOffset;
-
-			// apply hips translation
-			translateHips({x:swayOscillation*currentAnimation.joints[0].sway, y:motorOscillation*currentAnimation.joints[0].thrust, z:bobOscillation*currentAnimation.joints[0].bob});
-		}
-
-        // hips rotation
-        // apply the current Transition?
-        if(currentTransition!==null) {
-
-			if( currentTransition.walkingAtStart ) {
-
-				pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
-									   + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[0].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
-									   + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset;
-
-				rollOscillation  = (currentAnimation.joints[0].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
-									   + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset);
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( walkWheelPosition * 2)
-									   + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset;
-
-				yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( walkWheelPosition )
-									   + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset;
-
-				rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( walkWheelPosition )
-									   + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2)
-									   + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[0].yaw   * Math.sin(degToRad((cycle * adjustedFrequency )
-									   + currentAnimation.joints[0].yawPhase))   + currentAnimation.joints[0].yawOffset;
-
-				rollOscillation  = (currentAnimation.joints[0].roll  * Math.sin(degToRad((cycle * adjustedFrequency )
-									   + currentAnimation.joints[0].rollPhase))  + currentAnimation.joints[0].rollOffset);
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2)
-									   + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset;
-
-				yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-									   + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset;
-
-				rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-									   + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset);
-
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2)
-								   + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
-
-			yawOscillation   = currentAnimation.joints[0].yaw   * Math.sin(degToRad((cycle * adjustedFrequency )
-								   + currentAnimation.joints[0].yawPhase))   + currentAnimation.joints[0].yawOffset;
-
-			rollOscillation  = (currentAnimation.joints[0].roll  * Math.sin(degToRad((cycle * adjustedFrequency )
-								   + currentAnimation.joints[0].rollPhase))  + currentAnimation.joints[0].rollOffset);
-		}
-
-        // apply hips rotation
-        MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees(-pitchOscillation + (leanPitchModifier * getLeanPitch(velocity)),   	// getLeanPitch - lean forwards as velocity increases
-                                                                    yawOscillation,                                       				// Yup, that's correct ;-)
-                                                                    rollOscillation + getLeanRoll(deltaTime, velocity))); 				// getLeanRoll - banking on cornering
-
-		// upper legs
-		if( INTERNAL_STATE!==SIDE_STEPPING &&
-		    INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT &&
-		    INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT ) {
-
-			// apply the current Transition to the upper legs?
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-
-					pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
-					yawOscillation   = currentAnimation.joints[1].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].yawPhase));
-					rollOscillation  = currentAnimation.joints[1].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].rollPhase));
-
-					pitchOffset = currentAnimation.joints[1].pitchOffset;
-					yawOffset = currentAnimation.joints[1].yawOffset;
-					rollOffset = currentAnimation.joints[1].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch
-											   * Math.sin(degToRad( walkWheelPosition + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase )));
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[1].yaw
-											   * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].yawPhase ));
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[1].roll
-											   * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].rollPhase ));
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
-					yawOscillation   = currentAnimation.joints[1].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
-					rollOscillation  = currentAnimation.joints[1].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
-
-					pitchOffset = currentAnimation.joints[1].pitchOffset;
-					yawOffset   = currentAnimation.joints[1].yawOffset;
-					rollOffset  = currentAnimation.joints[1].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch
-											   * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											   + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase )));
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[1].yaw
-											   * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											   + currentTransition.lastAnimation.joints[1].yawPhase ));
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[1].roll
-											   * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											   + currentTransition.lastAnimation.joints[1].rollPhase ));
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset;
-				}
-
-				pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
-				yawOscillation   = (transitionProgress * yawOscillation )   + ((1-transitionProgress) * yawOscillationLast);
-				rollOscillation  = (transitionProgress * rollOscillation )  + ((1-transitionProgress) * rollOscillationLast);
-
-				pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
-				yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
-				rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
-				yawOscillation   = currentAnimation.joints[1].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
-				rollOscillation  = currentAnimation.joints[1].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
-
-				pitchOffset = currentAnimation.joints[1].pitchOffset;
-				yawOffset   = currentAnimation.joints[1].yawOffset;
-				rollOffset  = currentAnimation.joints[1].rollOffset;
-			}
-
-			// apply the upper leg rotations
-			MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(  pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation - rollOffset ));
-			MyAvatar.setJointData("LeftUpLeg",  Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, -rollOscillation + rollOffset ));
-
-
-			// lower leg
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-
-					pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].pitchPhase));
-					yawOscillation   = currentAnimation.joints[2].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].yawPhase));
-					rollOscillation  = currentAnimation.joints[2].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].rollPhase));
-
-					pitchOffset = currentAnimation.joints[2].pitchOffset;
-					yawOffset = currentAnimation.joints[2].yawOffset;
-					rollOffset = currentAnimation.joints[2].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].pitchPhase));
-					yawOscillationLast   = currentTransition.lastAnimation.joints[2].yaw   * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].yawPhase));
-					rollOscillationLast  = currentTransition.lastAnimation.joints[2].roll  * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].rollPhase));
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase));
-					yawOscillation   = currentAnimation.joints[2].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase));
-					rollOscillation  = currentAnimation.joints[2].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase));
-
-					pitchOffset = currentAnimation.joints[2].pitchOffset;
-					yawOffset   = currentAnimation.joints[2].yawOffset;
-					rollOffset  = currentAnimation.joints[2].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch
-											* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										   	+ currentTransition.lastAnimation.joints[2].pitchPhase));
-					yawOscillationLast   = currentTransition.lastAnimation.joints[2].yaw
-											* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-					                       	+ currentTransition.lastAnimation.joints[2].yawPhase));
-					rollOscillationLast  = currentTransition.lastAnimation.joints[2].roll
-											* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-					                        + currentTransition.lastAnimation.joints[2].rollPhase));
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset;
-				}
-
-				pitchOscillation = ( transitionProgress * pitchOscillation ) + ( (1-transitionProgress) * pitchOscillationLast );
-				yawOscillation   = ( transitionProgress * yawOscillation )   + ( (1-transitionProgress) * yawOscillationLast );
-				rollOscillation  = ( transitionProgress * rollOscillation )  + ( (1-transitionProgress) * rollOscillationLast );
-
-				pitchOffset = ( transitionProgress * pitchOffset ) + ( (1-transitionProgress) * pitchOffsetLast );
-				yawOffset   = ( transitionProgress * yawOffset )   + ( (1-transitionProgress) * yawOffsetLast );
-				rollOffset  = ( transitionProgress * rollOffset )  + ( (1-transitionProgress) * rollOffsetLast );
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[2].pitch * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase ));
-				yawOscillation   = currentAnimation.joints[2].yaw   * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase ));
-				rollOscillation  = currentAnimation.joints[2].roll  * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase ));
-
-				pitchOffset = currentAnimation.joints[2].pitchOffset;
-				yawOffset   = currentAnimation.joints[2].yawOffset;
-				rollOffset  = currentAnimation.joints[2].rollOffset;
-			}
-
-			// apply lower leg joint rotations
-			MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset ));
-			MyAvatar.setJointData("LeftLeg",  Quat.fromPitchYawRollDegrees(  pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset ));
-
-	   } // end if !SIDE_STEPPING
-
-	   else if( INTERNAL_STATE===SIDE_STEPPING ||
-		    	INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-		    	INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
-
-		   	// sidestepping uses the sinewave generators slightly differently for the legs
-		   	pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].pitchPhase));
-			yawOscillation   = currentAnimation.joints[1].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
-			rollOscillation  = currentAnimation.joints[1].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
-
-			// apply upper leg rotations for sidestepping
-			MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(
-			   -pitchOscillation + currentAnimation.joints[1].pitchOffset,
-				yawOscillation + currentAnimation.joints[1].yawOffset,
-			    rollOscillation  + currentAnimation.joints[1].rollOffset ));
-			MyAvatar.setJointData("LeftUpLeg",  Quat.fromPitchYawRollDegrees(
-			    pitchOscillation + currentAnimation.joints[1].pitchOffset,
-				yawOscillation - currentAnimation.joints[1].yawOffset,
-			   -rollOscillation  - currentAnimation.joints[1].rollOffset ));
-
-			// calculate lower leg joint rotations for sidestepping
-			pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase));
-			yawOscillation   = currentAnimation.joints[2].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase));
-			rollOscillation  = currentAnimation.joints[2].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase));
-
-			// apply lower leg joint rotations
-			MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
-				-pitchOscillation + currentAnimation.joints[2].pitchOffset,
-				 yawOscillation - currentAnimation.joints[2].yawOffset,
-				 rollOscillation - currentAnimation.joints[2].rollOffset)); // TODO: needs a kick just before fwd peak
-			MyAvatar.setJointData("LeftLeg",  Quat.fromPitchYawRollDegrees(
-				 pitchOscillation + currentAnimation.joints[2].pitchOffset,
-				 yawOscillation + currentAnimation.joints[2].yawOffset,
-				 rollOscillation + currentAnimation.joints[2].rollOffset));
-	   	}
-
-		// feet
-		if(currentTransition!==null) {
-
-			if(currentTransition.walkingAtStart) {
-
-				pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].pitchPhase));
-				yawOscillation   = currentAnimation.joints[3].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].yawPhase));
-				rollOscillation  = currentAnimation.joints[3].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].rollPhase));
-
-				pitchOffset = currentAnimation.joints[3].pitchOffset;
-				yawOffset = currentAnimation.joints[3].yawOffset;
-				rollOffset = currentAnimation.joints[3].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].pitchPhase));
-				yawOscillationLast   = currentTransition.lastAnimation.joints[3].yaw   * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].yawPhase));
-				rollOscillationLast  = currentTransition.lastAnimation.joints[3].roll  * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].rollPhase));
-
-				pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset;
-				yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset;
-				rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset;
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
-				yawOscillation   = currentAnimation.joints[3].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase));
-				rollOscillation  = currentAnimation.joints[3].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase));
-
-				pitchOffset = currentAnimation.joints[3].pitchOffset;
-				yawOffset   = currentAnimation.joints[3].yawOffset;
-				rollOffset  = currentAnimation.joints[3].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[3].pitchPhase));
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[3].yaw
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[3].yawPhase));
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[3].roll
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[3].rollPhase));
-
-				pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset;
-				yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset;
-				rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation )   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation )  + ((1-transitionProgress) * rollOscillationLast);
-
-			pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
-			yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
-			rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
-			yawOscillation   = currentAnimation.joints[3].yaw   * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase));
-			rollOscillation  = currentAnimation.joints[3].roll  * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase));
-
-			pitchOffset = currentAnimation.joints[3].pitchOffset;
-			yawOffset   = currentAnimation.joints[3].yawOffset;
-			rollOffset  = currentAnimation.joints[3].rollOffset;
-		}
-
-		// apply foot rotations
-		MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees(  pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset ));
-		MyAvatar.setJointData("LeftFoot",  Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset ));
-
-		// play footfall sound yet? To determine this, we take the differential of the foot's pitch curve to decide when the foot hits the ground.
-		if( INTERNAL_STATE===WALKING ||
-		    INTERNAL_STATE===SIDE_STEPPING ||
-		    INTERNAL_STATE===CONFIG_WALK_STYLES ||
-		    INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
-		    INTERNAL_STATE===CONFIG_WALK_JOINTS ||
-		    INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-		    INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
-
-			// finding dy/dx is as simple as determining the cosine wave for the foot's pitch function.
-			var feetPitchDifferential = Math.cos(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
-			var threshHold = 0.9; // sets the audio trigger point. with accuracy.
-			if(feetPitchDifferential<-threshHold &&
-			   nextStep===DIRECTION_LEFT &&
-			   principleDirection!==DIRECTION_UP &&
-			   principleDirection!==DIRECTION_DOWN) {
-
-				playFootstep(DIRECTION_LEFT);
-				nextStep = DIRECTION_RIGHT;
-			}
-			else if(feetPitchDifferential>threshHold &&
-					nextStep===DIRECTION_RIGHT &&
-					principleDirection!==DIRECTION_UP &&
-					principleDirection!==DIRECTION_DOWN) {
-
-				playFootstep(DIRECTION_RIGHT);
-				nextStep = DIRECTION_LEFT;
-			}
-		}
-
-		// toes
-		if(currentTransition!==null) {
-
-			if(currentTransition.walkingAtStart) {
-
-				pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].pitchPhase));
-				yawOscillation   = currentAnimation.joints[4].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
-				rollOscillation  = currentAnimation.joints[4].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
-
-				pitchOffset = currentAnimation.joints[4].pitchOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].pitchPhase));
-				yawOscillationLast   = currentTransition.lastAnimation.joints[4].yaw   * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;;
-				rollOscillationLast  = currentTransition.lastAnimation.joints[4].roll  * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset;
-
-				pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset;
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase));
-				yawOscillation   = currentAnimation.joints[4].yaw   * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
-				rollOscillation  = currentAnimation.joints[4].roll  * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
-
-				pitchOffset = currentAnimation.joints[4].pitchOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[4].pitchPhase));
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[4].yaw
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;;
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[4].roll
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset;
-
-				pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-			pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase));
-			yawOscillation   = currentAnimation.joints[4].yaw   * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
-			rollOscillation  = currentAnimation.joints[4].roll  * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
-
-			pitchOffset = currentAnimation.joints[4].pitchOffset;
-		}
-
-		// apply toe rotations
-		MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees(-pitchOscillation + pitchOffset, yawOscillation, rollOscillation));
-		MyAvatar.setJointData("LeftToeBase",  Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation, rollOscillation));
-
-		// spine
-		if( currentTransition !== null ) {
-
-			if( currentTransition.walkingAtStart ) {
-
-				pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2 ) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
-				yawOscillation   = currentAnimation.joints[5].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[5].yawPhase))   + currentAnimation.joints[5].yawOffset;
-				rollOscillation  = currentAnimation.joints[5].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[5].rollPhase))  + currentAnimation.joints[5].rollOffset;
-
-				// calculate where we would have been if we'd continued in the last state
-				pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch
-										* Math.sin(degToRad(( walkWheelPosition * 2  ) + currentTransition.lastAnimation.joints[5].pitchPhase))
-										+ currentTransition.lastAnimation.joints[5].pitchOffset;
-				yawOscillationLast   = currentTransition.lastAnimation.joints[5].yaw
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].yawPhase))
-										+ currentTransition.lastAnimation.joints[5].yawOffset;
-				rollOscillationLast  = currentTransition.lastAnimation.joints[5].roll
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].rollPhase))
-										+ currentTransition.lastAnimation.joints[5].rollOffset;
-			} else {
-
-				pitchOscillation = currentAnimation.joints[5].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
-									+ currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[5].yaw   * Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset;
-
-				rollOscillation  = currentAnimation.joints[5].roll  * Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2  )
-										+ currentTransition.lastAnimation.joints[5].pitchPhase))
-										+ currentTransition.lastAnimation.joints[5].pitchOffset;
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[5].yaw
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[5].yawPhase))
-										+ currentTransition.lastAnimation.joints[5].yawOffset;
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[5].roll
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[5].rollPhase))
-										+ currentTransition.lastAnimation.joints[5].rollOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation )   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation )  + ((1-transitionProgress) * rollOscillationLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
-			yawOscillation   = currentAnimation.joints[5].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[5].yawPhase))   + currentAnimation.joints[5].yawOffset;
-			rollOscillation  = currentAnimation.joints[5].roll  * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[5].rollPhase))  + currentAnimation.joints[5].rollOffset;
-		}
-
-		// apply spine joint rotations
-		MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
-
-		// spine 1
-		if(currentTransition!==null) {
-
-			if(currentTransition.walkingAtStart) {
-
-				pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
-				yawOscillation   = currentAnimation.joints[6].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[6].yawPhase))   + currentAnimation.joints[6].yawOffset;
-				rollOscillation  = currentAnimation.joints[6].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[6].rollPhase))  + currentAnimation.joints[6].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch
-										* Math.sin(degToRad(( walkWheelPosition * 2 ) + currentTransition.lastAnimation.joints[6].pitchPhase))
-										+ currentTransition.lastAnimation.joints[6].pitchOffset;
-				yawOscillationLast   = currentTransition.lastAnimation.joints[6].yaw
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].yawPhase))
-										+ currentTransition.lastAnimation.joints[6].yawOffset;
-				rollOscillationLast  = currentTransition.lastAnimation.joints[6].roll
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].rollPhase))
-										+ currentTransition.lastAnimation.joints[6].rollOffset;
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[6].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
-									+ currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[6].yaw   * Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[6].yawPhase))   + currentAnimation.joints[6].yawOffset;
-
-				rollOscillation  = currentAnimation.joints[6].roll  * Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[6].rollPhase))  + currentAnimation.joints[6].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2 )
-										+ currentTransition.lastAnimation.joints[6].pitchPhase))
-										+ currentTransition.lastAnimation.joints[6].pitchOffset;
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[6].yaw
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[6].yawPhase))
-										+ currentTransition.lastAnimation.joints[6].yawOffset;
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[6].roll
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[6].rollPhase))
-										+ currentTransition.lastAnimation.joints[6].rollOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
-			yawOscillation   = currentAnimation.joints[6].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[6].yawPhase))   + currentAnimation.joints[6].yawOffset;
-			rollOscillation  = currentAnimation.joints[6].roll  * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[6].rollPhase))  + currentAnimation.joints[6].rollOffset;
-		}
-		// apply spine1 joint rotations
-		MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
-
-		// spine 2
-		if(currentTransition!==null) {
-
-			if(currentTransition.walkingAtStart) {
-				pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[7].pitchPhase)) + currentAnimation.joints[7].pitchOffset;
-				yawOscillation   = currentAnimation.joints[7].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[7].yawPhase))   + currentAnimation.joints[7].yawOffset;
-				rollOscillation  = currentAnimation.joints[7].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    ) + currentAnimation.joints[7].rollPhase))  + currentAnimation.joints[7].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].pitchPhase))
-										+ currentTransition.lastAnimation.joints[7].pitchOffset;
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[7].yaw
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].yawPhase))
-										+ currentTransition.lastAnimation.joints[7].yawOffset;
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[7].roll
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].rollPhase))
-										+ currentTransition.lastAnimation.joints[7].rollOffset;
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[7].pitch
-									* Math.sin( degToRad(( cycle * adjustedFrequency * 2)
-									+ currentAnimation.joints[7].pitchPhase))
-								   	+ currentAnimation.joints[7].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[7].yaw
-									* Math.sin( degToRad(( cycle * adjustedFrequency    )
-									+ currentAnimation.joints[7].yawPhase))
-								   	+ currentAnimation.joints[7].yawOffset;
-
-				rollOscillation  = currentAnimation.joints[7].roll
-									* Math.sin( degToRad(( cycle * adjustedFrequency    )
-									+ currentAnimation.joints[7].rollPhase))
-								   	+ currentAnimation.joints[7].rollOffset;
-
-				pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[7].pitchPhase))
-										+ currentTransition.lastAnimation.joints[7].pitchOffset;
-
-				yawOscillationLast   = currentTransition.lastAnimation.joints[7].yaw
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[7].yawPhase))
-										+ currentTransition.lastAnimation.joints[7].yawOffset;
-
-				rollOscillationLast  = currentTransition.lastAnimation.joints[7].roll
-										* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-										+ currentTransition.lastAnimation.joints[7].rollPhase))
-										+ currentTransition.lastAnimation.joints[7].rollOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation )   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation )  + ((1-transitionProgress) * rollOscillationLast);
-
-		} else {
-
-			pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[7].pitchPhase))
-							   + currentAnimation.joints[7].pitchOffset;
-
-			yawOscillation   = currentAnimation.joints[7].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[7].yawPhase))
-			                   + currentAnimation.joints[7].yawOffset;
-
-			rollOscillation  = currentAnimation.joints[7].roll  * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[7].rollPhase))
-			                   + currentAnimation.joints[7].rollOffset;
-		}
-		// apply spine2 joint rotations
-		MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
-
-		if(!armsFree) {
-
-			// shoulders
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-					pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    )
-										+ currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
-
-					yawOscillation   = currentAnimation.joints[8].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    )
-										+ currentAnimation.joints[8].yawPhase));
-
-					rollOscillation  = currentAnimation.joints[8].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
-										+ currentAnimation.joints[8].rollPhase))  + currentAnimation.joints[8].rollOffset;
-
-					yawOffset = currentAnimation.joints[8].yawOffset;
-
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].pitchPhase))
-											+ currentTransition.lastAnimation.joints[8].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[8].yaw
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].yawPhase))
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[8].roll
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].rollPhase))
-											+ currentTransition.lastAnimation.joints[8].rollOffset;
-
-					yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[8].pitch * Math.sin( degToRad(( cycle * adjustedFrequency    )
-										+ currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
-
-					yawOscillation   = currentAnimation.joints[8].yaw   * Math.sin( degToRad(( cycle * adjustedFrequency    )
-										+ currentAnimation.joints[8].yawPhase));
-
-					rollOscillation  = currentAnimation.joints[8].roll  * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
-										+ currentAnimation.joints[8].rollPhase))  + currentAnimation.joints[8].rollOffset;
-
-					yawOffset = currentAnimation.joints[8].yawOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[8].pitchPhase))
-											+ currentTransition.lastAnimation.joints[8].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[8].yaw
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[8].yawPhase))
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[8].roll
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[8].rollPhase))
-											+ currentTransition.lastAnimation.joints[8].rollOffset;
-
-					yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset;
-				}
-
-				pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-				yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-				rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-				yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cycle * adjustedFrequency    )
-									+ currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[8].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    )
-									+ currentAnimation.joints[8].yawPhase));
-
-				rollOscillation  = currentAnimation.joints[8].roll  * Math.sin(degToRad(( cycle * adjustedFrequency * 2)
-									+ currentAnimation.joints[8].rollPhase))  + currentAnimation.joints[8].rollOffset;
-
-				yawOffset = currentAnimation.joints[8].yawOffset;
-			}
-
-			MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation + yawOffset,  rollOscillation ));
-			MyAvatar.setJointData("LeftShoulder",  Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation - yawOffset, -rollOscillation ));
-
-			// upper arms
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-
-					pitchOscillation = currentAnimation.joints[9].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].pitchPhase));
-					yawOscillation   = currentAnimation.joints[9].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].yawPhase));
-					rollOscillation  = currentAnimation.joints[9].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[9].rollPhase)) + currentAnimation.joints[9].rollOffset;
-
-					pitchOffset = currentAnimation.joints[9].pitchOffset;
-					yawOffset = currentAnimation.joints[9].yawOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].pitchPhase))
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[9].yaw
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].yawPhase))
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[9].roll
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].rollPhase))
-											+ currentTransition.lastAnimation.joints[9].rollOffset;
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[9].pitch
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[9].pitchPhase));
-
-					yawOscillation   = currentAnimation.joints[9].yaw
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[9].yawPhase));
-
-					rollOscillation  = currentAnimation.joints[9].roll
-										* Math.sin(degToRad(( cycle * adjustedFrequency * 2)
-										+ currentAnimation.joints[9].rollPhase))
-										+ currentAnimation.joints[9].rollOffset;
-
-					pitchOffset = currentAnimation.joints[9].pitchOffset;
-					yawOffset = currentAnimation.joints[9].yawOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[9].pitchPhase))
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[9].yaw
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[9].yawPhase))
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[9].roll
-											* Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[9].rollPhase))
-											+ currentTransition.lastAnimation.joints[9].rollOffset;
-
-					pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset;
-					yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset;
-				}
-
-				pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-				yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-				rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-				pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
-				yawOffset   = (transitionProgress * yawOffset)   + ((1-transitionProgress) * yawOffsetLast);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[9].pitch
-									* Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[9].pitchPhase));
-
-				yawOscillation   = currentAnimation.joints[9].yaw
-									* Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[9].yawPhase));
-
-				rollOscillation  = currentAnimation.joints[9].roll
-									* Math.sin( degToRad(( cycle * adjustedFrequency * 2)
-									+ currentAnimation.joints[9].rollPhase))
-									+ currentAnimation.joints[9].rollOffset;
-
-				pitchOffset = currentAnimation.joints[9].pitchOffset;
-				yawOffset   = currentAnimation.joints[9].yawOffset;
-
-			}
-			MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset,  rollOscillation ));
-			MyAvatar.setJointData("LeftArm",  Quat.fromPitchYawRollDegrees(  pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation ));
-
-			// forearms
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-
-					pitchOscillation = currentAnimation.joints[10].pitch
-										* Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
-										+ currentAnimation.joints[10].pitchPhase ))
-										+ currentAnimation.joints[10].pitchOffset;
-
-					yawOscillation   = currentAnimation.joints[10].yaw
-										* Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
-										+ currentAnimation.joints[10].yawPhase ));
-
-					rollOscillation  = currentAnimation.joints[10].roll
-										* Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
-										+ currentAnimation.joints[10].rollPhase ));
-
-					yawOffset = currentAnimation.joints[10].yawOffset;
-					rollOffset = currentAnimation.joints[10].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch
-											* Math.sin( degToRad(( walkWheelPosition )
-											+ currentTransition.lastAnimation.joints[10].pitchPhase))
-											+ currentTransition.lastAnimation.joints[10].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[10].yaw
-											* Math.sin( degToRad(( walkWheelPosition )
-											+ currentTransition.lastAnimation.joints[10].yawPhase));
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[10].roll
-											* Math.sin( degToRad(( walkWheelPosition )
-											+ currentTransition.lastAnimation.joints[10].rollPhase));
-
-					yawOffsetLast  = currentTransition.lastAnimation.joints[10].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[10].pitch
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[10].pitchPhase ))
-										+ currentAnimation.joints[10].pitchOffset;
-
-					yawOscillation   = currentAnimation.joints[10].yaw
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[10].yawPhase ));
-
-					rollOscillation  = currentAnimation.joints[10].roll
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[10].rollPhase ));
-
-					yawOffset  = currentAnimation.joints[10].yawOffset;
-					rollOffset = currentAnimation.joints[10].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch
-											* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-											+ currentTransition.lastAnimation.joints[10].pitchPhase))
-											+ currentTransition.lastAnimation.joints[10].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[10].yaw
-											* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-											+ currentTransition.lastAnimation.joints[10].yawPhase));
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[10].roll
-											* Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
-											+ currentTransition.lastAnimation.joints[10].rollPhase));
-
-					yawOffsetLast  = currentTransition.lastAnimation.joints[10].yawOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset;
-				}
-
-				// blend the previous and next
-				pitchOscillation =  (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-				yawOscillation   = -(transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-				rollOscillation  =  (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-				yawOffset  = (transitionProgress * yawOffset)  + ((1-transitionProgress) * yawOffsetLast);
-				rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[10].pitch
-									* Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[10].pitchPhase ))
-									+ currentAnimation.joints[10].pitchOffset;
-
-				yawOscillation   = currentAnimation.joints[10].yaw
-									* Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[10].yawPhase ));
-
-				rollOscillation  = currentAnimation.joints[10].roll
-									* Math.sin( degToRad(( cycle * adjustedFrequency )
-									+ currentAnimation.joints[10].rollPhase ));
-
-				yawOffset  = currentAnimation.joints[10].yawOffset;
-				rollOffset = currentAnimation.joints[10].rollOffset;
-			}
-
-			// apply forearms rotations
-			MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation,  yawOscillation + yawOffset,  rollOscillation + rollOffset ));
-			MyAvatar.setJointData("LeftForeArm",  Quat.fromPitchYawRollDegrees( pitchOscillation,  yawOscillation - yawOffset,  rollOscillation - rollOffset ));
-
-			// hands
-			var sideStepSign = 1;
-			if(INTERNAL_STATE===SIDE_STEPPING) {
-				sideStepSign = 1;
-			}
-			if(currentTransition!==null) {
-
-				if(currentTransition.walkingAtStart) {
-
-					pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset;
-					yawOscillation   = currentAnimation.joints[11].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].yawPhase))   + currentAnimation.joints[11].yawOffset;
-					rollOscillation  = currentAnimation.joints[11].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].rollPhase))  ;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].pitchPhase))
-											+ currentTransition.lastAnimation.joints[11].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[11].yaw
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].yawPhase))
-											+ currentTransition.lastAnimation.joints[11].yawOffset;
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[11].roll
-											* Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].rollPhase))
-
-					rollOffset = currentAnimation.joints[11].rollOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset;
-
-				} else {
-
-					pitchOscillation = currentAnimation.joints[11].pitch
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[11].pitchPhase))
-										+ currentAnimation.joints[11].pitchOffset;
-
-					yawOscillation   = currentAnimation.joints[11].yaw
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[11].yawPhase))
-										+ currentAnimation.joints[11].yawOffset;
-
-					rollOscillation  = currentAnimation.joints[11].roll
-										* Math.sin( degToRad(( cycle * adjustedFrequency )
-										+ currentAnimation.joints[11].rollPhase));
-
-					rollOffset = currentAnimation.joints[11].rollOffset;
-
-					pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch
-											* Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[11].pitchPhase))
-											+ currentTransition.lastAnimation.joints[11].pitchOffset;
-
-					yawOscillationLast   = currentTransition.lastAnimation.joints[11].yaw
-											* Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[11].yawPhase))
-											+ currentTransition.lastAnimation.joints[11].yawOffset;
-
-					rollOscillationLast  = currentTransition.lastAnimation.joints[11].roll
-											* Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
-											+ currentTransition.lastAnimation.joints[11].rollPhase))
-
-					rollOffset = currentAnimation.joints[11].rollOffset;
-					rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset;
-				}
-
-				pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-				yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-				rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-				rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
-
-			} else {
-
-				pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset;
-				yawOscillation   = currentAnimation.joints[11].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].yawPhase))   + currentAnimation.joints[11].yawOffset;
-				rollOscillation  = currentAnimation.joints[11].roll  * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].rollPhase));
-
-				rollOffset = currentAnimation.joints[11].rollOffset;
-			}
-
-			// set the hand rotations
-			MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( sideStepSign * pitchOscillation,  yawOscillation,  rollOscillation + rollOffset));
-			MyAvatar.setJointData("LeftHand",  Quat.fromPitchYawRollDegrees( pitchOscillation, -yawOscillation,  rollOscillation - rollOffset));
-
-		} // end if(!armsFree)
-
-        // head (includes neck joint) - currently zeroed out in STANDING animation files by request
-        if( currentTransition !== null ) {
-
-			if(currentTransition.walkingAtStart) {
-
-				pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
-									+ currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
-
-				yawOscillation   = 0.5 * currentAnimation.joints[12].yaw   * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    )
-									+ currentAnimation.joints[12].yawPhase))   + currentAnimation.joints[12].yawOffset;
-
-				rollOscillation  = 0.5 * currentAnimation.joints[12].roll  * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency    )
-									+ currentAnimation.joints[12].rollPhase))  + currentAnimation.joints[12].rollOffset;
-
-				pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch
-										* Math.sin(degToRad(( walkWheelPosition * 2) + currentTransition.lastAnimation.joints[12].pitchPhase))
-										+ currentTransition.lastAnimation.joints[12].pitchOffset;
-
-				yawOscillationLast   = 0.5 * currentTransition.lastAnimation.joints[12].yaw
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].yawPhase))
-										+ currentTransition.lastAnimation.joints[12].yawOffset;
-
-				rollOscillationLast  = 0.5 * currentTransition.lastAnimation.joints[12].roll
-										* Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].rollPhase))
-										+ currentTransition.lastAnimation.joints[12].rollOffset;
-
-			} else {
-
-				pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
-				yawOscillation   = 0.5 * currentAnimation.joints[12].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[12].yawPhase))   + currentAnimation.joints[12].yawOffset;
-				rollOscillation  = 0.5 * currentAnimation.joints[12].roll  * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[12].rollPhase))  + currentAnimation.joints[12].rollOffset;
-
-				pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency  * 2)
-										+ currentTransition.lastAnimation.joints[12].pitchPhase))
-										+ currentTransition.lastAnimation.joints[12].pitchOffset;
-
-				yawOscillationLast   = 0.5 * currentTransition.lastAnimation.joints[12].yaw
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency  )
-										+ currentTransition.lastAnimation.joints[12].yawPhase))
-										+ currentTransition.lastAnimation.joints[12].yawOffset;
-
-				rollOscillationLast  = 0.5 * currentTransition.lastAnimation.joints[12].roll
-										* Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency  )
-										+ currentTransition.lastAnimation.joints[12].rollPhase))
-										+ currentTransition.lastAnimation.joints[12].rollOffset;
-			}
-
-			pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
-			yawOscillation   = (transitionProgress * yawOscillation)   + ((1-transitionProgress) * yawOscillationLast);
-			rollOscillation  = (transitionProgress * rollOscillation)  + ((1-transitionProgress) * rollOscillationLast);
-
-		} else {
-
-			pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
-			yawOscillation   = 0.5 * currentAnimation.joints[12].yaw   * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[12].yawPhase))   + currentAnimation.joints[12].yawOffset;
-			rollOscillation  = 0.5 * currentAnimation.joints[12].roll  * Math.sin(degToRad(( cycle * adjustedFrequency    ) + currentAnimation.joints[12].rollPhase))  + currentAnimation.joints[12].rollOffset;
-		}
-
-		MyAvatar.setJointData( "Head", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
-		MyAvatar.setJointData( "Neck", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
-}
-
-
-
-// Bezier function for applying to transitions - src: Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
-Coord = function (x,y) {
-	if(!x) var x=0;
-	if(!y) var y=0;
-	return {x: x, y: y};
-}
-
-function B1(t) { return t*t*t }
-function B2(t) { return 3*t*t*(1-t) }
-function B3(t) { return 3*t*(1-t)*(1-t) }
-function B4(t) { return (1-t)*(1-t)*(1-t) }
-
-function getBezier(percent,C1,C2,C3,C4) {
-	var pos = new Coord();
-	pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
-	pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
-	return pos;
-}
-
-// Butterworth LP filter - coeffs calculated using: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
-var NZEROS = 8;
-var NPOLES = 8;
-var GAIN =   17.40692157;
-var xv = [0,0,0,0,0,0,0,0,0]; //xv.length = NZEROS+1;
-var yv = [0,0,0,0,0,0,0,0,0]; //yv.length = NPOLES+1;
-
-function filterButterworth(nextInputValue)
-{
-	xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8];
-	xv[8] = nextInputValue / GAIN;
-	yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8];
-	yv[8] =   (xv[0] + xv[8]) + 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6])
-				 + 56 * (xv[3] + xv[5]) + 70 * xv[4]
-				 + ( -0.0033008230 * yv[0]) + ( -0.0440409341 * yv[1])
-				 + ( -0.2663485333 * yv[2]) + ( -0.9570250765 * yv[3])
-				 + ( -2.2596729000 * yv[4]) + ( -3.6088345059 * yv[5])
-				 + ( -3.9148571397 * yv[6]) + ( -2.6527135283 * yv[7]);
-	return yv[8];
-}
-
-// the faster we go, the further we lean forward. the angle is calcualted here
-var leanAngles = []; // smooth out and add damping with simple averaging filter.
-leanAngles.length = 15;
-
-function getLeanPitch(velocity) {
-
-	if(velocity>TERMINAL_VELOCITY) velocity=TERMINAL_VELOCITY;
-	var leanProgress = velocity / TERMINAL_VELOCITY;
-	var responseSharpness = 1.8;
-	if(principleDirection==DIRECTION_BACKWARDS) responseSharpness = 3.6; // lean back a bit extra when walking backwards
-	var leanProgressBezier = getBezier((1-leanProgress),{x:0,y:0},{x:0,y:responseSharpness},{x:0,y:1},{x:1,y:1}).y;
-
-	// simple averaging filter seems to give best results
-    leanAngles.push(leanProgressBezier);
-    leanAngles.shift(); // FIFO
-    var totalLeanAngles = 0;
-    for(ea in leanAngles) totalLeanAngles += leanAngles[ea];
-    var leanProgressAverageFiltered = totalLeanAngles / leanAngles.length;
-
-	// calculate final return value
-	var leanPitchFinal = 0;
-	if(principleDirection===DIRECTION_BACKWARDS) {
-		leanPitchFinal = -currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse;
-	} else {
-		leanPitchFinal = currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse;
-	}
-    return leanPitchFinal;
-}
-
-// calculate the angle at which to bank into corners when turning
-var leanRollAngles = [];  // smooth out and add damping with simple averaging filter
-leanRollAngles.length = 25;
-
-var angularVelocities = []; // keep a record of the last few so can filter out spurious values
-angularVelocities.length = 5;
-
-function getLeanRoll(deltaTime, velocity) {
-
-	// what's our current anglular velocity?
-	var angularVelocityMax = 70; // from observation
-	var currentOrientationVec3 = Quat.safeEulerAngles(MyAvatar.orientation);
-    var lastOrientationVec3 = Quat.safeEulerAngles(lastOrientation);
-    var deltaYaw = lastOrientationVec3.y-currentOrientationVec3.y;
-    lastOrientation = MyAvatar.orientation;
-
-    var angularVelocity = deltaYaw / deltaTime;
-    if(angularVelocity>angularVelocityMax) angularVelocity = angularVelocityMax;
-    if(angularVelocity<-angularVelocityMax) angularVelocity = -angularVelocityMax;
-
-    // filter the angular velocity for a nicer response and a bit of wobble (intentional overshoot / ringing)
-    angularVelocity = filterButterworth(angularVelocity);
-
-    var turnSign = 1;
-    if(angularVelocity<0) turnSign = -1;
-    if(principleDirection===DIRECTION_BACKWARDS)
-    	turnSign *= -1;
-
-    // calculate the amount of roll based on both angular and linear velocities
-	if(velocity>TERMINAL_VELOCITY) velocity = TERMINAL_VELOCITY;
-	var leanRollProgress = (velocity / TERMINAL_VELOCITY) * (Math.abs(angularVelocity) / angularVelocityMax);
-
-	// apply our response curve
-	var leanRollProgressBezier = getBezier((1-leanRollProgress),{x:0,y:0},{x:0,y:2.5},{x:0,y:1},{x:1,y:1}).y;
-
-	// simple averaging filter
-    leanRollAngles.push(turnSign * leanRollProgressBezier);
-    leanRollAngles.shift(); // FIFO
-    var totalLeanRollAngles = 0;
-    for(var ea in leanRollAngles) totalLeanRollAngles += leanRollAngles[ea];
-    var leanRollProgressAverageFiltered = totalLeanRollAngles / leanRollAngles.length;
-
-    return currentAnimation.settings.maxBankingAngle * leanRollProgressAverageFiltered;
-}
-
-// set up the interface components, update the internal state and kick off any transitions
-function setInternalState(newInternalState) {
-
-	switch(newInternalState) {
-
-        case WALKING:
-
-            if(!minimised) doStandardMenu();
-            INTERNAL_STATE = WALKING;
-            return;
-
-        case FLYING:
-
-            if(!minimised) doStandardMenu();
-            INTERNAL_STATE = FLYING;
-            return;
-
-        case SIDE_STEPPING:
-
-            if(!minimised) doStandardMenu();
-            INTERNAL_STATE = SIDE_STEPPING;
-            return;
-
-        case CONFIG_WALK_STYLES:
-
-            INTERNAL_STATE = CONFIG_WALK_STYLES;
-            currentAnimation = selectedWalk;
-            if(!minimised) {
-				hidebuttonOverlays();
-				hideJointControls();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(true);
-				setBackground(controlsBackgroundWalkEditStyles);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configWalkStylesButtonSelected);
-				setButtonOverlayVisible(configWalkTweaksButton);
-				setButtonOverlayVisible(configWalkJointsButton);
-				setButtonOverlayVisible(backButton);
-				setSliderThumbsVisible(false);
-			}
-            return;
-
-        case CONFIG_WALK_TWEAKS:
-
-            INTERNAL_STATE = CONFIG_WALK_TWEAKS;
-            currentAnimation = selectedWalk;
-            if(!minimised) {
-				hidebuttonOverlays();
-				hideJointControls();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditTweaks);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configWalkStylesButton);
-				setButtonOverlayVisible(configWalkTweaksButtonSelected);
-				setButtonOverlayVisible(configWalkJointsButton);
-				setButtonOverlayVisible(backButton);
-				initialiseWalkTweaks();
-			}
-            return;
-
-        case CONFIG_WALK_JOINTS:
-
-        	INTERNAL_STATE = CONFIG_WALK_JOINTS;
-        	currentAnimation = selectedWalk;
-        	if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configWalkStylesButton);
-				setButtonOverlayVisible(configWalkTweaksButton);
-				setButtonOverlayVisible(configWalkJointsButtonSelected);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_STANDING:
-
-            INTERNAL_STATE = CONFIG_STANDING;
-            currentAnimation = selectedStand;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configSideStepRightButton);
-				setButtonOverlayVisible(configSideStepLeftButton);
-				setButtonOverlayVisible(configStandButtonSelected);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_SIDESTEP_LEFT:
-
-            INTERNAL_STATE = CONFIG_SIDESTEP_LEFT;
-            currentAnimation = selectedSideStepLeft;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configSideStepRightButton);
-				setButtonOverlayVisible(configSideStepLeftButtonSelected);
-				setButtonOverlayVisible(configStandButton);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_SIDESTEP_RIGHT:
-
-            INTERNAL_STATE = CONFIG_SIDESTEP_RIGHT;
-            currentAnimation = selectedSideStepRight;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configSideStepRightButtonSelected);
-				setButtonOverlayVisible(configSideStepLeftButton);
-				setButtonOverlayVisible(configStandButton);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_FLYING:
-
-            INTERNAL_STATE = CONFIG_FLYING;
-            currentAnimation = selectedFly;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configFlyingUpButton);
-				setButtonOverlayVisible(configFlyingDownButton);
-				setButtonOverlayVisible(configFlyingButtonSelected);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_FLYING_UP:
-
-            INTERNAL_STATE = CONFIG_FLYING_UP;
-            currentAnimation = selectedFlyUp;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configFlyingUpButtonSelected);
-				setButtonOverlayVisible(configFlyingDownButton);
-				setButtonOverlayVisible(configFlyingButton);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case CONFIG_FLYING_DOWN:
-
-            INTERNAL_STATE = CONFIG_FLYING_DOWN;
-            currentAnimation = selectedFlyDown;
-            if(!minimised) {
-				hidebuttonOverlays();
-				showFrontPanelButtons(false);
-				showWalkStyleButtons(false);
-				setBackground(controlsBackgroundWalkEditJoints);
-				setButtonOverlayVisible(onButton);
-				setButtonOverlayVisible(configFlyingUpButton);
-				setButtonOverlayVisible(configFlyingDownButtonSelected);
-				setButtonOverlayVisible(configFlyingButton);
-				setButtonOverlayVisible(backButton);
-				initialiseJointsEditingPanel(selectedJointIndex);
-			}
-            return;
-
-        case STANDING:
-        default:
-
-            INTERNAL_STATE = STANDING;
-            if(!minimised) doStandardMenu();
-
-            // initialisation - runs at script startup only
-            if(strideLength===0) {
-
-				if(principleDirection===DIRECTION_BACKWARDS)
-					strideLength = selectedWalk.calibration.strideLengthBackwards;
-				else
-            		strideLength = selectedWalk.calibration.strideLengthForwards;
-
-				curlFingers();
-			}
-            return;
-    }
-}
 
 // Main loop
-
-// stabilising vars -  most state changes are preceded by a couple of hints that they are about to happen rather than
-// momentarilly switching between states (causes flicker), we count the number of hints in a row before actually changing state
-var standHints = 0;
-var walkHints = 0;
-var flyHints = 0;
-var requiredHints = 2; // tweakable - debounce state changes - how many times do we get a state change request in a row before we actually change state? (used to be 4 or 5)
-var principleDirection = 0;
-
-// helper function for stats output
-function directionAsString(directionEnum) {
-
-	switch(directionEnum) {
-		case DIRECTION_UP: return 'Up';
-		case DIRECTION_DOWN: return 'Down';
-		case DIRECTION_LEFT: return 'Left';
-		case DIRECTION_RIGHT: return 'Right';
-		case DIRECTION_FORWARDS: return 'Forwards';
-		case DIRECTION_BACKWARDS: return 'Backwards';
-		default: return 'Unknown';
-	}
-}
-// helper function for stats output
-function internalStateAsString(internalState) {
-
-	switch(internalState) {
-		case STANDING: return 'Standing';
-		case WALKING: return 'Walking';
-		case SIDE_STEPPING: return 'Side Stepping';
-		case FLYING: return 'Flying';
-		default: return 'Editing';
-	}
-}
-
 Script.update.connect(function(deltaTime) {
 
-	if(powerOn) {
+	if (state.powerOn) {
 
-		frameStartTime = new Date().getTime();
-        cumulativeTime += deltaTime;
-        nFrames++;
-        var speed = 0;
+		motion.frameStartTime = new Date().getTime();
+		motion.cumulativeTime += deltaTime;
+		motion.nFrames++;
+		var speed = 0;
 
-        // firstly check for editing modes, as these require no positioning calculations
-        var editing = false;
-        switch(INTERNAL_STATE) {
+		// check for editing modes first, as these require no positioning calculations
+		switch (state.currentState) {
 
-            case CONFIG_WALK_STYLES:
-	            currentAnimation = selectedWalk;
-                animateAvatar(deltaTime, speed, principleDirection);
-                editing = true;
-                break;
-            case CONFIG_WALK_TWEAKS:
-                currentAnimation = selectedWalk;
-                animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
-                editing = true;
-                break;
-            case CONFIG_WALK_JOINTS:
-                currentAnimation = selectedWalk;
-                animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
-                editing = true;
-                break;
-            case CONFIG_STANDING:
-                currentAnimation = selectedStand;
-                animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
-                editing = true;
-                break;
-            case CONFIG_SIDESTEP_LEFT:
-                currentAnimation = selectedSideStepLeft;
-                animateAvatar(deltaTime, speed, DIRECTION_LEFT);
-                editing = true;
-                break;
-            case CONFIG_SIDESTEP_RIGHT:
-                currentAnimation = selectedSideStepRight;
-                animateAvatar(deltaTime, speed, DIRECTION_RIGHT);
-                editing = true;
-                break;
-            case CONFIG_FLYING:
-                currentAnimation = selectedFly;
-                animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
-                editing = true;
-                break;
-            case CONFIG_FLYING_UP:
-                currentAnimation = selectedFlyUp;
-                animateAvatar(deltaTime, speed, DIRECTION_UP);
-                editing = true;
-                break;
-            case CONFIG_FLYING_DOWN:
-                currentAnimation = selectedFlyDown;
-                animateAvatar(deltaTime, speed, DIRECTION_DOWN);
-                editing = true;
-                break;
-            default:
-                break;
-        }
+			case state.EDIT_WALK_STYLES:
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
 
-		// we have to declare these vars here ( outside 'if(!editing)' ), so they are still in scope
-		// when we record the frame's data and when we do the stats update at the end
-		var deltaX = 0;
-		var deltaY = 0;
-		var deltaZ = 0;
-		var acceleration = { x:0, y:0, z:0 };
-		var accelerationJS = MyAvatar.getAcceleration();
+			case state.EDIT_WALK_TWEAKS:
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
 
-		// calculate overriding (local) direction of translation for use later when decide which animation should be played
-		var inverseRotation = Quat.inverse(MyAvatar.orientation);
-		var localVelocity = Vec3.multiplyQbyV(inverseRotation, MyAvatar.getVelocity());
+			case state.EDIT_WALK_JOINTS:
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
 
-		if(!editing) {
+			case state.EDIT_STANDING:
+				motion.curAnim = motion.selStand;
+				motion.direction = FORWARDS;
+				animateAvatar(deltaTime, speed);
+				break;
 
-			// the first thing to do is find out how fast we're going,
-			// what our acceleration is and which direction we're principly moving in
+			case state.EDIT_SIDESTEP_LEFT:
+				motion.curAnim = motion.selSideStepLeft;
+				motion.direction = LEFT;
+				animateAvatar(deltaTime, speed);
+				break;
 
-			// calcualte (local) change in velocity
-			var velocity = MyAvatar.getVelocity();
-			speed = Vec3.length(velocity);
+			case state.EDIT_SIDESTEP_RIGHT:
+				motion.curAnim = motion.selSideStepRight;
+				motion.direction = RIGHT;
+				animateAvatar(deltaTime, speed);
+				break;
 
-			// determine the candidate animation to play
-			var actionToTake = 0;
-			if( speed < 0.5) {
-				actionToTake = STANDING;
-				standHints++;
-			}
-			else if( speed < FLYING_SPEED ) {
-				actionToTake = WALKING;
-				walkHints++;
-			}
-			else if( speed >= FLYING_SPEED ) {
-				actionToTake = FLYING;
-				flyHints++;
-			}
+			case state.EDIT_FLYING:
+				motion.curAnim = motion.selFly;
+				motion.direction = FORWARDS;
+				animateAvatar(deltaTime, speed);
+				break;
 
-			deltaX = localVelocity.x;
-			deltaY = localVelocity.y;
-			deltaZ = -localVelocity.z;
+			case state.EDIT_FLYING_UP:
+				motion.curAnim = motion.selFlyUp;
+				motion.direction = UP;
+				animateAvatar(deltaTime, speed);
+				break;
+
+			case state.EDIT_FLYING_DOWN:
+				motion.curAnim = motion.selFlyDown;
+				motion.direction = DOWN;
+				animateAvatar(deltaTime, speed);
+				break;
+
+			default:
+				break;
+		}
+
+		// calcualte velocity and speed
+		var velocity = MyAvatar.getVelocity();
+		speed = Vec3.length(velocity);
+
+		if (motion.curTransition !== nullTransition) {
+
+			// finish any live transition before changing state
+			animateAvatar(deltaTime, speed);
+			return;
+		}
+		var localVelocity = {x: 0, y: 0, z: 0};
+		if (speed > 0)
+			localVelocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), velocity);
+
+		if (!state.editing) {
+
+			// determine the candidate animation state
+			var actionToTake = undefined;
+			if (speed < 0.05) actionToTake = state.STANDING; // as per MIN_AVATAR_SPEED, MyAvatar.cpp
+			else if (speed < TAKE_FLIGHT_SPEED) actionToTake = state.WALKING;
+			else if (speed >= TAKE_FLIGHT_SPEED) actionToTake = state.FLYING;
 
 			// determine the principle direction
-			if(Math.abs(deltaX)>Math.abs(deltaY)
-			 &&Math.abs(deltaX)>Math.abs(deltaZ)) {
-				if(deltaX<0) {
-					principleDirection = DIRECTION_RIGHT;
-				} else {
-					principleDirection = DIRECTION_LEFT;
-				}
-			}
-			else if(Math.abs(deltaY)>Math.abs(deltaX)
-				  &&Math.abs(deltaY)>Math.abs(deltaZ)) {
-				if(deltaY>0) {
-					principleDirection = DIRECTION_UP;
-				}
-				else {
-					principleDirection = DIRECTION_DOWN;
-				}
-			}
-			else if(Math.abs(deltaZ)>Math.abs(deltaX)
-				  &&Math.abs(deltaZ)>Math.abs(deltaY)) {
-				if(deltaZ>0) {
-					principleDirection = DIRECTION_FORWARDS;
-				} else {
-					principleDirection = DIRECTION_BACKWARDS;
-				}
-			}
+			if (Math.abs(localVelocity.x) > Math.abs(localVelocity.y) &&
+				Math.abs(localVelocity.x) > Math.abs(localVelocity.z)) {
 
-			// NB: this section will change significantly once we are ground plane aware
-			//     it will change even more once we have uneven surfaces to deal with
+				if (localVelocity.x < 0) motion.direction = LEFT;
+				else motion.direction = RIGHT;
+
+			} else if (Math.abs(localVelocity.y) > Math.abs(localVelocity.x) &&
+					   Math.abs(localVelocity.y) > Math.abs(localVelocity.z)) {
+
+				if (localVelocity.y > 0) motion.direction = UP;
+				else motion.direction = DOWN;
+
+			} else if (Math.abs(localVelocity.z) > Math.abs(localVelocity.x) &&
+					   Math.abs(localVelocity.z) > Math.abs(localVelocity.y)) {
+
+				if (localVelocity.z < 0) motion.direction = FORWARDS;
+				else motion.direction = BACKWARDS;
+			}
 
 			// maybe at walking speed, but sideways?
-			if( actionToTake === WALKING &&
-			  ( principleDirection === DIRECTION_LEFT ||
-				principleDirection === DIRECTION_RIGHT )) {
-
-					actionToTake = SIDE_STEPPING;
-			}
-
-			// maybe at walking speed, but flying up?
-			if( actionToTake === WALKING &&
-			  ( principleDirection === DIRECTION_UP )) {
-
-					actionToTake = FLYING;
-					standHints--;
-					flyHints++;
-			}
-
-			// maybe at walking speed, but flying down?
-			if( actionToTake === WALKING &&
-			  ( principleDirection === DIRECTION_DOWN )) {
-
-					actionToTake = FLYING;
-					standHints--;
-					flyHints++;
-			}
-
-			// log this frame's motion for later reference
-			var accelerationX = ( framesHistory.recentMotions[0].velocity.x - localVelocity.x ) / deltaTime;
-			var accelerationY = ( localVelocity.y - framesHistory.recentMotions[0].velocity.y ) / deltaTime;
-			var accelerationZ = ( framesHistory.recentMotions[0].velocity.z - localVelocity.z ) / deltaTime;
-			acceleration = {x:accelerationX, y:accelerationY, z:accelerationZ};
+			if (actionToTake === state.WALKING &&
+				(motion.direction === LEFT ||
+					motion.direction === RIGHT))
+						actionToTake = state.SIDE_STEP;
 
+			// maybe at walking speed, but flying up or down?
+			if (actionToTake === state.WALKING &&
+				(motion.direction === UP))// ||
+					//motion.direction === DOWN))
+						actionToTake = state.FLYING;
 
 			// select appropriate animation and initiate Transition if required
-			switch(actionToTake) {
+			switch (actionToTake) {
 
-				case STANDING:
+				case state.STANDING:
 
-					if( standHints > requiredHints || INTERNAL_STATE===STANDING) { // wait for a few consecutive hints (17mS each)
+					// do we need to change state?
+					if (state.currentState !== state.STANDING) {
 
-						standHints = 0;
-						walkHints = 0;
-						flyHints = 0;
+						switch (motion.curAnim) {
 
-						// do we need to change state?
-						if( INTERNAL_STATE!==STANDING ) {
+							case motion.selWalk:
 
-							// initiate the transition
-							if(currentTransition) {
-								delete currentTransition;
-								currentTransition = null;
-							}
+								// Walking to standing
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selWalk,
+									[], 0.25,
+									{x: 0.1, y: 0.5},
+									{x: -0.25, y: 1.22});
+								break;
 
-							switch(currentAnimation) {
+							case motion.selSideStepLeft:
+							case motion.selSideStepRight:
 
-								case selectedWalk:
+								break;
 
-									// Walking to Standing
-									var timeWalking = new Date().getTime() - framesHistory.lastWalkStartTime;
+							default:
 
-									var bezierCoeffsOne = {x:0.0, y:1.0};
-									var bezierCoeffsTwo = {x:0.0, y:1.0};
-									var transitionTime = 0.4;
+								// flying to standing
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selStand,
+									[], 0.25,
+									{x: 0.5, y: 0.08},
+									{x: 0.28, y: 1});
+								break;
+						}
+						state.setInternalState(state.STANDING);
+						motion.curAnim = motion.selStand;
+					}
+					animateAvatar(deltaTime, speed);
+					break;
 
-									// very different curves for incremental steps
-									if( timeWalking < 550 ) {
-										bezierCoeffsOne = {x:0.63, y:0.17};
-										bezierCoeffsTwo = {x:0.77, y:0.3};
-										transitionTime = 0.75;
-									}
-									currentTransition = new Transition( currentAnimation, selectedStand, [], transitionTime, bezierCoeffsOne, bezierCoeffsTwo );
-									break;
+				case state.WALKING:
 
+					if (state.currentState !== state.WALKING) {
 
-								case selectedSideStepLeft:
-								case selectedSideStepRight:
+						if (motion.direction === BACKWARDS)
+								 motion.walkWheelPos = motion.selWalk.calibration.startAngleBackwards;
+
+						else motion.walkWheelPos = motion.selWalk.calibration.startAngleForwards;
+
+						switch (motion.curAnim) {
+
+							case motion.selStand:
+
+								// Standing to Walking
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selWalk,
+									[], 0.1,
+									{x: 0.5, y: 0.1},
+									{x: 0.5, y: 0.9});
+								break;
+
+							case motion.selSideStepLeft:
+							case motion.selSideStepRight:
+
+								break;
+
+							default:
+
+								// Flying to Walking
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selWalk,
+									[], 0.1,
+									{x: 0.24, y: 0.03},
+									{x: 0.42, y: 1.0});
+								break;
+						}
+						state.setInternalState(state.WALKING);
+					}
+					motion.curAnim = motion.selWalk;
+					animateAvatar(deltaTime, speed);
+					break;
+
+				case state.SIDE_STEP:
+
+					var selSideStep = 0;
+					if (motion.direction === LEFT) {
+
+						if (motion.lastDirection !== LEFT)
+							motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart;
+						selSideStep = motion.selSideStepLeft;
+
+					} else {
+
+						if (motion.lastDirection !== RIGHT)
+							motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart;
+						selSideStep = motion.selSideStepRight;
+					}
+
+					if (state.currentState !== state.SIDE_STEP) {
+
+						if (motion.direction === LEFT) {
+
+							motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart;
+							switch (motion.curAnim) {
+
+								case motion.selStand:
 
 									break;
 
 								default:
 
-									currentTransition = new Transition(currentAnimation, selectedStand, [], 0.3, {x:0.5,y:0.08}, {x:0.28,y:1});
 									break;
 							}
 
-							setInternalState(STANDING);
-							currentAnimation = selectedStand;
-
-						}
-						animateAvatar(1,0,principleDirection);
-					}
-					break;
-
-				case WALKING:
-				case SIDE_STEPPING:
-					if( walkHints > requiredHints ||
-						INTERNAL_STATE===WALKING ||
-						INTERNAL_STATE===SIDE_STEPPING ) { // wait for few consecutive hints (17mS each)
-
-						standHints = 0;
-						walkHints = 0;
-						flyHints = 0;
-
-						if( actionToTake === WALKING && INTERNAL_STATE !== WALKING) {
-
-							// initiate the transition
-							if(currentTransition) {
-								delete currentTransition;
-								currentTransition = null;
-							}
-
-							// set the appropriate start position for the walk wheel
-							if( principleDirection === DIRECTION_BACKWARDS ) {
-
-								walkWheelPosition = selectedWalk.settings.startAngleBackwards;
-
-							} else {
-
-								walkWheelPosition = selectedWalk.settings.startAngleForwards;
-							}
-
-							switch(currentAnimation) {
-
-								case selectedStand:
-
-									// Standing to Walking
-									currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.25, {x:0.5,y:0.08}, {x:0.05,y:0.75});
-									break;
-
-								case selectedSideStepLeft:
-								case selectedSideStepRight:
-
-									break;
-
-								default:
-
-									currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.3, {x:0.5,y:0.08}, {x:0.05,y:0.75});
-									break;
-							}
-							framesHistory.lastWalkStartTime = new Date().getTime();
-							setInternalState(WALKING);
-							currentAnimation = selectedWalk;
-						}
-						else if(actionToTake===SIDE_STEPPING) {
-
-							var selectedSideStep = selectedSideStepRight;
-							if( principleDirection === DIRECTION_LEFT ) {
-
-								selectedSideStep = selectedSideStepLeft;
-
-							} else {
-
-								selectedSideStep = selectedSideStepRight;
-							}
-
-							if( INTERNAL_STATE !== SIDE_STEPPING ) {
-
-								if( principleDirection === DIRECTION_LEFT ) {
-
-									walkWheelPosition = sideStepCycleStartLeft;
-
-								} else {
-
-									walkWheelPosition = sideStepCycleStartRight;
-								}
-								switch(currentAnimation) {
-
-									case selectedStand:
-
-										break;
-
-									default:
-
-										break;
-								}
-								setInternalState(SIDE_STEPPING);
-							}
-
-							currentAnimation = selectedSideStep;
-						}
-						animateAvatar( deltaTime, speed, principleDirection );
-					}
-					break;
-
-				case FLYING:
-
-					if( flyHints > requiredHints - 1  || INTERNAL_STATE===FLYING ) { // wait for a few consecutive hints (17mS each)
-
-						standHints = 0;
-						walkHints = 0;
-						flyHints = 0;
-
-						if(INTERNAL_STATE!==FLYING) setInternalState(FLYING);
-
-						// change animation for flying directly up or down. TODO - check RecentMotions, if is a change then put a transition on it
-						if(principleDirection===DIRECTION_UP) {
-
-							if(currentAnimation !== selectedFlyUp) {
-
-								// initiate a Transition
-								if(currentTransition && currentTransition.nextAnimation!==selectedFlyUp) {
-									delete currentTransition;
-									currentTransition = null;
-								}
-								switch(currentAnimation) {
-
-									case selectedStand:
-
-										currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-
-									case selectedSideStepLeft:
-									case selectedSideStepRight:
-
-										break;
-
-									default:
-
-										currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-								}
-								currentAnimation = selectedFlyUp;
-							}
-
-						} else if(principleDirection==DIRECTION_DOWN) {
-
-							if(currentAnimation !== selectedFlyDown) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced
-
-								// initiate a Transition
-								if(currentTransition && currentTransition.nextAnimation!==selectedFlyDown) {
-									delete currentTransition;
-									currentTransition = null;
-								}
-								switch(currentAnimation) {
-
-									case selectedStand:
-
-										currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-
-									case selectedSideStepLeft:
-									case selectedSideStepRight:
-
-										break;
-
-									default:
-
-										currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-								}
-								currentAnimation = selectedFlyDown;
-							}
-
 						} else {
 
-							if(currentAnimation !== selectedFly) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced
+							motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart;
+							switch (motion.curAnim) {
 
-								// initiate a Transition
-								if(currentTransition && currentTransition.nextAnimation!==selectedFly) {
-									delete currentTransition;
-									currentTransition = null;
-								}
-								switch(currentAnimation) {
+								case motion.selStand:
 
-									case selectedStand:
+									break;
 
-										currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
+								default:
 
-									case selectedSideStepLeft:
-									case selectedSideStepRight:
-
-										break;
-
-									default:
-
-										currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-								}
-								currentAnimation = selectedFly;
+									break;
 							}
 						}
-						animateAvatar(deltaTime, speed, principleDirection);
+						state.setInternalState(state.SIDE_STEP);
 					}
+					motion.curAnim = selSideStep;
+					animateAvatar(deltaTime, speed);
+					break;
+
+				case state.FLYING:
+
+					if (state.currentState !== state.FLYING)
+						state.setInternalState(state.FLYING);
+
+					// change animation for flying directly up or down
+					if (motion.direction === UP) {
+
+						if (motion.curAnim !== motion.selFlyUp) {
+
+							switch (motion.curAnim) {
+
+								case motion.selStand:
+								case motion.selWalk:
+
+									// standing | walking to flying up
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyUp,
+										[], 0.25,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+
+								case motion.selSideStepLeft:
+								case motion.selSideStepRight:
+
+									break;
+
+								default:
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyUp,
+										[], 0.35,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+							}
+							motion.curAnim = motion.selFlyUp;
+						}
+
+					} else if (motion.direction == DOWN) {
+
+						if (motion.curAnim !== motion.selFlyDown) {
+
+							switch (motion.curAnim) {
+
+								case motion.selStand:
+								case motion.selWalk:
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyDown,
+										[], 0.35,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+
+								case motion.selSideStepLeft:
+								case motion.selSideStepRight:
+
+									break;
+
+								default:
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyDown,
+										[], 0.5,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+							}
+							motion.curAnim = motion.selFlyDown;
+						}
+
+					} else {
+
+						if (motion.curAnim !== motion.selFly) {
+
+							switch (motion.curAnim) {
+
+								case motion.selStand:
+								case motion.selWalk:
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFly,
+										[], 0.35,
+										{x: 1.44, y:0.24},
+										{x: 0.61, y:0.92});
+									break;
+
+								case motion.selSideStepLeft:
+								case motion.selSideStepRight:
+
+									break;
+
+								default:
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFly,
+										[], 0.75,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+							}
+							motion.curAnim = motion.selFly;
+						}
+					}
+					animateAvatar(deltaTime, speed);
 					break;
 
 			} // end switch(actionToTake)
 
-		} // end if(!editing)
+		} // end if (!state.editing)
+
+		// record the frame's direction and local avatar's local velocity for future reference
+		motion.lastDirection = motion.direction;
+		motion.lastVelocity = localVelocity;
+	}
+});
+
+// the faster we go, the further we lean forward. the angle is calcualted here
+function getLeanPitch(speed) {
+
+	if (speed > TOP_SPEED) speed = TOP_SPEED;
+	var leanProgress = speed / TOP_SPEED;
+
+	if (motion.direction === LEFT ||
+	   motion.direction === RIGHT)
+		leanProgress = 0;
+
+	else {
+
+		var responseSharpness = 1.5;
+		if (motion.direction == BACKWARDS) responseSharpness = 3.0;
+
+		leanProgress = filter.bezier((1 - leanProgress),
+										{x: 0, y: 0.0},
+										{x: 0, y: responseSharpness},
+										{x: 0, y: 1.5},
+										{x: 1, y: 1}).y;
+
+		// determine final pitch and adjust for direction of momentum
+		if (motion.direction === BACKWARDS)
+			leanProgress = -motion.motionPitchMax * leanProgress;
+		else
+			leanProgress = motion.motionPitchMax * leanProgress;
+	}
+
+	// return the smoothed response
+	return leanPitchFilter.process(leanProgress);
+}
+
+// calculate the angle at which to bank into corners when turning
+function getLeanRoll(deltaTime, speed) {
+
+	var leanRollProgress = 0;
+	if (speed > TOP_SPEED) speed = TOP_SPEED;
+
+	// what's our our anglular velocity?
+	var angularVelocityMax = 70; // from observation
+	var angularVelocity = filter.radToDeg(MyAvatar.getAngularVelocity().y);
+	if (angularVelocity > angularVelocityMax) angularVelocity = angularVelocityMax;
+	if (angularVelocity < -angularVelocityMax) angularVelocity = -angularVelocityMax;
+
+	leanRollProgress = speed / TOP_SPEED;
+
+	if (motion.direction !== LEFT &&
+	   motion.direction !== RIGHT)
+			leanRollProgress *= (Math.abs(angularVelocity) / angularVelocityMax);
+
+	// apply our response curve
+	leanRollProgress = filter.bezier((1 - leanRollProgress),
+											   {x: 0, y: 0},
+											   {x: 0, y: 1},
+											   {x: 0, y: 1},
+											   {x: 1, y: 1}).y;
+	// which way to lean?
+	var turnSign = -1;
+	if (angularVelocity < 0.001) turnSign = 1;
+	if (motion.direction === BACKWARDS ||
+	   motion.direction === LEFT)
+		turnSign *= -1;
+
+	if (motion.direction === LEFT ||
+	   motion.direction === RIGHT)
+	   		leanRollProgress *= 2;
+
+	// add damping with simple averaging filter
+	leanRollProgress = leanRollFilter.process(turnSign * leanRollProgress);
+	return motion.motionRollMax * leanRollProgress;
+}
+
+function playFootstep(side) {
+
+	var options = new AudioInjectionOptions();
+	options.position = Camera.getPosition();
+	options.volume = 0.2;
+	var soundNumber = 2; // 0 to 2
+	if (side === RIGHT && motion.makesFootStepSounds)
+		Audio.playSound(walkAssets.footsteps[soundNumber + 1], options);
+	else if (side === LEFT && motion.makesFootStepSounds)
+		Audio.playSound(walkAssets.footsteps[soundNumber], options);
+}
+
+// animate the avatar using sine wave generators. inspired by Victorian clockwork dolls
+function animateAvatar(deltaTime, speed) {
+
+	var cycle = motion.cumulativeTime;
+	var transProgress = 1;
+	var adjFreq = motion.curAnim.calibration.frequency;
+
+	// legs phase and cycle reversal for walking backwards
+	var reverseModifier = 0;
+	var reverseSignModifier = 1;
+	if (motion.direction === BACKWARDS) {
+		reverseModifier = -180;
+		reverseSignModifier = -1;
+	}
+
+	// don't lean into the direction of travel if going up
+	var leanMod = 1;
+	if (motion.direction === UP)
+		leanMod = 0;
+
+	// adjust leaning direction for flying
+	var flyingModifier = 1;
+	if (state.currentState.FLYING)
+		flyingModifier = -1;
+
+	if (motion.curTransition !== nullTransition) {
+
+		// new transiton?
+		if (motion.curTransition.progress === 0 &&
+			motion.curTransition.walkingAtStart) {
+
+			if (state.currentState !== state.SIDE_STEP) {
+
+				// work out where we want the walk cycle to stop
+				var leftStop = motion.selWalk.calibration.stopAngleForwards + 180;
+				var rightStop = motion.selWalk.calibration.stopAngleForwards;
+
+				if (motion.direction === BACKWARDS) {
+					leftStop = motion.selWalk.calibration.stopAngleBackwards + 180;
+					rightStop = motion.selWalk.calibration.stopAngleBackwards;
+				}
+
+				// find the closest stop point from the walk wheel's angle
+				var angleToLeftStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - leftStop) - 180);
+				var angleToRightStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - rightStop) - 180);
+				if (motion.walkWheelPos > angleToLeftStop) angleToLeftStop = 360 - angleToLeftStop;
+				if (motion.walkWheelPos > angleToRightStop) angleToRightStop = 360 - angleToRightStop;
 
 
-		// record the frame's stats for later reference
-        var thisMotion = new RecentMotion(localVelocity, acceleration, principleDirection, INTERNAL_STATE);
-		framesHistory.recentMotions.push(thisMotion);
-		framesHistory.recentMotions.shift();
+				motion.curTransition.walkWheelIncrement = 3;
 
+				// keep the walkwheel turning by setting the walkWheelIncrement
+				// until our feet are tucked nicely underneath us.
+				if (angleToLeftStop < angleToRightStop)
 
-		// before we go, populate the stats overlay
-		if( statsOn ) {
+					motion.curTransition.walkStopAngle = leftStop;
 
-			var cumulativeTimeMS = Math.floor(cumulativeTime*1000);
-			var deltaTimeMS = deltaTime * 1000;
-			var frameExecutionTime = new Date().getTime() - frameStartTime;
-			if(frameExecutionTime>frameExecutionTimeMax) frameExecutionTimeMax = frameExecutionTime;
+				else
+					motion.curTransition.walkStopAngle = rightStop;
 
-			var angluarVelocity = Vec3.length(MyAvatar.getAngularVelocity());
+			} else {
 
-			var debugInfo = '\n \n \n \n                   Stats\n--------------------------------------\n \n \n'
-						  + '\nFrame number: '+nFrames
-						  + '\nFrame time: '+deltaTimeMS.toFixed(2)
-						  + ' mS\nRender time: '+frameExecutionTime.toFixed(0)
-						  + ' mS\nLocalised speed: '+speed.toFixed(3)
-						  + ' m/s\nCumulative Time '+cumulativeTimeMS.toFixed(0)
-						  + ' mS\nState: '+internalStateAsString(INTERNAL_STATE)
-						  + ' \nDirection: '+directionAsString(principleDirection)
-						  + ' \nAngular Velocity: ' + angluarVelocity.toFixed(3)
-						  + ' rad/s';
-			Overlays.editOverlay(debugStats, {text: debugInfo});
-
-			// update these every 250 mS (assuming 60 fps)
-			if( nFrames % 15 === 0 ) {
-				var debugInfo = '           Periodic Stats\n--------------------------------------\n \n \n'
-							  + ' \n \nRender time peak hold: '+frameExecutionTimeMax.toFixed(0)
-							  + ' mS\n \n \n(L) MyAvatar.getVelocity()'
-							  + ' \n \nlocalVelocityX: '+deltaX.toFixed(1)
-							  + ' m/s\nlocalVelocityY: '+deltaY.toFixed(1)
-							  + ' m/s\nlocalVelocityZ: '+deltaZ.toFixed(1)
-							  + ' m/s\n \n(G) MyAvatar.getAcceleration()'
-							  + ' \n\nAcceleration X: '+accelerationJS.x.toFixed(1)
-							  + ' m/s/s\nAcceleration Y: '+accelerationJS.y.toFixed(1)
-							  + ' m/s/s\nAcceleration Z: '+accelerationJS.z.toFixed(1)
-							  + ' m/s/s\n \n(L) Acceleration using\nMyAvatar.getVelocity()'
-							  + ' \n \nAcceleration X: '+acceleration.x.toFixed(1)
-							  + ' m/s/s\nAcceleration Y: '+acceleration.y.toFixed(1)
-							  + ' m/s/s\nAcceleration Z: '+acceleration.z.toFixed(1)
-						      + ' m/s/s';
-				Overlays.editOverlay(debugStatsPeriodic, {text: debugInfo});
-				frameExecutionTimeMax = 0;
+				// freeze wheel for sidestepping transitions (for now)
+				motion.curTransition.walkWheelIncrement = 0;
 			}
+		} // end if (new transition and curTransition.walkingAtStart)
+
+		// update the Transition progress
+		var elapasedTime = (new Date().getTime() - motion.curTransition.startTime) / 1000;
+		motion.curTransition.progress = elapasedTime / motion.curTransition.transitionDuration;
+		transProgress = filter.bezier((1 - motion.curTransition.progress), {x: 0, y: 0},
+							motion.curTransition.easingLower,
+							motion.curTransition.easingUpper, {x: 1, y: 1}).y;
+
+		if (motion.curTransition.progress >= 1) {
+
+			// time to kill off the transition
+			delete motion.curTransition;
+			motion.curTransition = nullTransition;
+
+		} else {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				if (state.currentState !== state.SIDE_STEP) {
+
+					// if at a stop angle, hold the walk wheel position for remainder of transition
+					var tolerance = 7; // must be greater than the walkWheel increment
+					if ((motion.walkWheelPos > (motion.curTransition.walkStopAngle - tolerance)) &&
+						(motion.walkWheelPos < (motion.curTransition.walkStopAngle + tolerance))) {
+
+						motion.curTransition.walkWheelIncrement = 0;
+					}
+					// keep turning walk wheel until both feet are below the avi
+
+					motion.advanceWalkWheel(motion.curTransition.walkWheelIncrement);
+					//motion.curTransition.walkWheelAdvance += motion.curTransition.walkWheelIncrement;
+				}
+				else motion.curTransition.walkWheelIncrement = 0; // sidestep
+			}
+		} } // end motion.curTransition !== nullTransition
+
+
+	// walking? then get the stride length
+	if (motion.curAnim === motion.selWalk) {
+
+		// if the timing's right, take a snapshot of the stride max and recalibrate
+		var strideMaxAt = motion.curAnim.calibration.forwardStrideMaxAt;
+		if (motion.direction === BACKWARDS)
+			strideMaxAt = motion.curAnim.calibration.backwardsStrideMaxAt;
+
+		var tolerance = 1.0;
+		if (motion.walkWheelPos < (strideMaxAt + tolerance) &&
+			motion.walkWheelPos > (strideMaxAt - tolerance)) {
+
+			// measure and save stride length
+			var footRPos = MyAvatar.getJointPosition("RightFoot");
+			var footLPos = MyAvatar.getJointPosition("LeftFoot");
+			motion.strideLength = Vec3.distance(footRPos, footLPos);
+
+			if (motion.direction === FORWARDS)
+				motion.curAnim.calibration.strideLengthForwards = motion.strideLength;
+			else if (motion.direction === BACKWARDS)
+				motion.curAnim.calibration.strideLengthBackwards = motion.strideLength;
+
+		} else {
+
+			// use the saved value for stride length
+			if (motion.direction === FORWARDS)
+				motion.strideLength = motion.curAnim.calibration.strideLengthForwards;
+			else if (motion.direction === BACKWARDS)
+				motion.strideLength = motion.curAnim.calibration.strideLengthBackwards;
 		}
-    }
-});
+	} // end get walk stride length
 
+	// sidestepping? get the stride length
+	if (motion.curAnim === motion.selSideStepLeft ||
+		motion.curAnim === motion.selSideStepRight) {
 
-// overlays start
+		// if the timing's right, take a snapshot of the stride max and recalibrate the stride length
+		var tolerance = 1.0;
+		if (motion.direction === LEFT) {
 
-//  controller dimensions
-var backgroundWidth = 350;
-var backgroundHeight = 700;
-var backgroundX = Window.innerWidth-backgroundWidth-58;
-var backgroundY = Window.innerHeight/2 - backgroundHeight/2;
-var minSliderX = backgroundX + 30;
-var maxSliderX = backgroundX + 295;
-var sliderRangeX = 295 - 30;
-var jointsControlWidth = 200;
-var jointsControlHeight = 300;
-var jointsControlX = backgroundX + backgroundWidth/2 - jointsControlWidth/2;
-var jointsControlY = backgroundY + 242 - jointsControlHeight/2;
-var buttonsY = 20;  // distance from top of panel to buttons
+			if (motion.walkWheelPos < motion.curAnim.calibration.strideMaxAt + tolerance &&
+				motion.walkWheelPos > motion.curAnim.calibration.strideMaxAt - tolerance) {
 
-// arrays of overlay names
-var sliderThumbOverlays = []; // thumb sliders
-var backgroundOverlays = [];
-var buttonOverlays = [];
-var jointsControlOverlays = [];
-var bigButtonOverlays = [];
+				var footRPos = MyAvatar.getJointPosition("RightFoot");
+				var footLPos = MyAvatar.getJointPosition("LeftFoot");
+				motion.strideLength = Vec3.distance(footRPos, footLPos);
+				motion.curAnim.calibration.strideLength = motion.strideLength;
 
+			} else motion.strideLength = motion.selSideStepLeft.calibration.strideLength;
 
-// load UI backgrounds
-var controlsBackground = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
-                    imageURL: pathToOverlays+"ddao-background.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-backgroundOverlays.push(controlsBackground);
+		} else if (motion.direction === RIGHT) {
 
-var controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
-                    imageURL: pathToOverlays+"ddao-background-edit-styles.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-backgroundOverlays.push(controlsBackgroundWalkEditStyles);
+			if (motion.walkWheelPos < motion.curAnim.calibration.strideMaxAt + tolerance &&
+				motion.walkWheelPos > motion.curAnim.calibration.strideMaxAt - tolerance) {
 
-var controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
-                    imageURL: pathToOverlays+"ddao-background-edit-tweaks.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-backgroundOverlays.push(controlsBackgroundWalkEditTweaks);
+				var footRPos = MyAvatar.getJointPosition("RightFoot");
+				var footLPos = MyAvatar.getJointPosition("LeftFoot");
+				motion.strideLength = Vec3.distance(footRPos, footLPos);
+				motion.curAnim.calibration.strideLength = motion.strideLength;
 
-var controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
-                    imageURL: pathToOverlays+"ddao-background-edit-joints.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-backgroundOverlays.push(controlsBackgroundWalkEditJoints);
+			} else motion.strideLength = motion.selSideStepRight.calibration.strideLength;
+		}
+	} // end get sidestep stride length
 
-var controlsBackgroundFlyingEdit = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
-                    imageURL: pathToOverlays+"ddao-background-flying-edit.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-backgroundOverlays.push(controlsBackgroundFlyingEdit);
+	// turn the walk wheel
+	if (motion.curAnim === motion.selWalk ||
+	   motion.curAnim === motion.selSideStepLeft ||
+	   motion.curAnim === motion.selSideStepRight ||
+	   motion.curTransition.walkingAtStart) {
 
-// minimised tab - not put in array, as is a one off
-var controlsMinimisedTab = Overlays.addOverlay("image", {
-					x: Window.innerWidth-58, y: Window.innerHeight -145, width: 50, height: 50,
-					//subImage: { x: 0, y: 50, width: 50, height: 50 },
-					imageURL: pathToOverlays + 'ddao-minimise-tab.png',
-					visible: minimised,
-					alpha: 0.9
-					});
+		// wrap the stride length around a 'surveyor's wheel' twice and calculate
+		// the angular speed at the given (linear) speed:
+		// omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride length
+		var strideLength = motion.strideLength;
+		var wheelRadius = strideLength / Math.PI;
+		var angularVelocity = speed / wheelRadius;
 
-// load character joint selection control images
-var hipsJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-hips.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(hipsJointControl);
+		// calculate the degrees turned (at this angular speed) since last frame
+		var radiansTurnedSinceLastFrame = deltaTime * angularVelocity;
+		var degreesTurnedSinceLastFrame = filter.radToDeg(radiansTurnedSinceLastFrame);
 
-var upperLegsJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-upper-legs.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(upperLegsJointControl);
+		// if we are in an edit mode, we will need fake time to turn the wheel
+		if (state.currentState !== state.WALKING &&
+			state.currentState !== state.SIDE_STEP)
+			degreesTurnedSinceLastFrame = motion.curAnim.calibration.frequency / 70;
 
-var lowerLegsJointControl  = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-lower-legs.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(lowerLegsJointControl);
+		// advance the walk wheel the appropriate amount
+		motion.advanceWalkWheel(degreesTurnedSinceLastFrame);
 
-var feetJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-feet.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(feetJointControl);
+		// set the new values for the exact correct walk cycle speed
+		adjFreq = 1;
+		cycle = motion.walkWheelPos;
 
-var toesJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-toes.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(toesJointControl);
+	} // end of walk wheel and stride length calculation
 
-var spineJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-spine.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(spineJointControl);
+	// motion vars
+	var pitchOsc = 0;
+	var pitchOscLeft = 0;
+	var pitchOscRight = 0;
+	var yawOsc = 0;
+	var yawOscLeft = 0;
+	var yawOscRight = 0;
+	var rollOsc = 0;
+	var pitchOffset = 0;
+	var yawOffset = 0;
+	var rollOffset = 0;
+	var swayOsc = 0;
+	var bobOsc = 0;
+	var thrustOsc = 0;
+	var swayOscLast = 0;
+	var bobOscLast = 0;
+	var thrustOscLast = 0;
 
-var spine1JointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-spine1.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(spine1JointControl);
+	// historical (for transitions)
+	var pitchOscLast = 0;
+	var pitchOscLeftLast = 0;
+	var pitchOscRightLast = 0;
+	var yawOscLast = 0;
+	var rollOscLast = 0;
+	var pitchOffsetLast = 0;
+	var yawOffsetLast = 0;
+	var rollOffsetLast = 0;
 
-var spine2JointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-spine2.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(spine2JointControl);
+	// feet
+	var sideStepFootPitchModifier = 1;
+	var sideStepHandPitchSign = 1;
 
-var shouldersJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-shoulders.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(shouldersJointControl);
+	// The below code should probably be optimised into some sort of loop, where
+	// the joints are iterated through. However, this has not been done yet, as there
+	// are still some quite fundamental changes to be made (e.g. turning on the spot
+	// animation and sidestepping transitions) so it's been left as is for ease of
+	// understanding and editing.
 
-var upperArmsJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-upper-arms.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(upperArmsJointControl);
+	// calculate hips translation
+	if (motion.curTransition !== nullTransition) {
 
-var forearmsJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-forearms.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(forearmsJointControl);
+		if (motion.curTransition.walkingAtStart) {
 
-var handsJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-hands.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(handsJointControl);
+			swayOsc = motion.curAnim.joints[0].sway *
+					  Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency +
+					  motion.curAnim.joints[0].swayPhase)) + motion.curAnim.joints[0].swayOffset;
 
-var headJointControl = Overlays.addOverlay("image", {
-                    bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
-                    imageURL: pathToOverlays+"ddao-background-edit-head.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-jointsControlOverlays.push(headJointControl);
+			var bobPhase = motion.curAnim.joints[0].bobPhase;
+			if (motion.direction === motion.BACKWARDS) bobPhase += 90;
+			bobOsc = motion.curAnim.joints[0].bob *
+					 Math.sin(filter.degToRad(motion.cumulativeTime *
+					 motion.curAnim.calibration.frequency + bobPhase)) +
+					 motion.curAnim.joints[0].bobOffset;
 
+			thrustOsc = motion.curAnim.joints[0].thrust *
+						Math.sin(filter.degToRad(motion.cumulativeTime *
+						motion.curAnim.calibration.frequency * 2 +
+						motion.curAnim.joints[0].thrustPhase)) +
+						motion.curAnim.joints[0].thrustOffset;
 
-// sider thumb overlays
-var sliderOne = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderOne);
-var sliderTwo = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderTwo);
-var sliderThree = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderThree);
-var sliderFour = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderFour);
-var sliderFive = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderFive);
-var sliderSix = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderSix);
-var sliderSeven = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderSeven);
-var sliderEight = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderEight);
-var sliderNine = Overlays.addOverlay("image", {
-                    bounds: { x: 0, y: 0, width: 25, height: 25 },
-                    imageURL: pathToOverlays+"ddao-slider-handle.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-sliderThumbOverlays.push(sliderNine);
+			swayOscLast = motion.curTransition.lastAnim.joints[0].sway *
+						  Math.sin(filter.degToRad(motion.walkWheelPos +
+						  motion.curTransition.lastAnim.joints[0].swayPhase)) +
+						  motion.curTransition.lastAnim.joints[0].swayOffset;
 
+			var bobPhaseLast = motion.curTransition.lastAnim.joints[0].bobPhase;
+			if (motion.direction === motion.BACKWARDS) bobPhaseLast +=90;
+			bobOscLast = motion.curTransition.lastAnim.joints[0].bob *
+						 Math.sin(filter.degToRad(motion.walkWheelPos + bobPhaseLast));
+			bobOscLast = filter.clipTrough(bobOscLast, motion.curTransition.lastAnim.joints[0].bob , 2);
+			bobOscLast = hipsBobLPFilter.process(bobOscLast);
+			bobOscLast += motion.curTransition.lastAnim.joints[0].bobOffset;
 
-// button overlays
-var onButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-on-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(onButton);
-var offButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-off-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(offButton);
-var configWalkButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-walk-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkButton);
-var configWalkButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-walk-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkButtonSelected);
-var configStandButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-stand-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configStandButton);
-var configStandButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-stand-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configStandButtonSelected);
+			thrustOscLast = motion.curTransition.lastAnim.joints[0].thrust *
+							Math.sin(filter.degToRad(motion.walkWheelPos * 2 +
+							motion.curTransition.lastAnim.joints[0].thrustPhase)) +
+							motion.curTransition.lastAnim.joints[0].thrustOffset;
 
-var configFlyingButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingButton);
-var configFlyingButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingButtonSelected);
+		} // end if walking at start of transition
+		else {
 
-var configFlyingUpButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-up-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingUpButton);
-var configFlyingUpButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-up-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingUpButtonSelected);
+			swayOsc = motion.curAnim.joints[0].sway *
+					  Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) +
+					  motion.curAnim.joints[0].swayOffset;
 
-var configFlyingDownButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-down-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingDownButton);
-var configFlyingDownButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-fly-down-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configFlyingDownButtonSelected);
+			var bobPhase = motion.curAnim.joints[0].bobPhase;
+			if (motion.direction === motion.BACKWARDS) bobPhase += 90;
+			bobOsc = motion.curAnim.joints[0].bob *
+					 Math.sin(filter.degToRad(cycle * adjFreq * 2 + bobPhase));
+			if (state.currentState === state.WALKING ||
+			   state.currentState === state.EDIT_WALK_STYLES ||
+			   state.currentState === state.EDIT_WALK_TWEAKS ||
+			   state.currentState === state.EDIT_WALK_JOINTS) {
 
-var hideButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-hide-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(hideButton);
-var hideButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-hide-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(hideButtonSelected);
-var configWalkStylesButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-walk-styles-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkStylesButton);
-var configWalkStylesButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-walk-styles-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkStylesButtonSelected);
-var configWalkTweaksButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-walk-tweaks-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkTweaksButton);
-var configWalkTweaksButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-walk-tweaks-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkTweaksButtonSelected);
+					// apply clipping filter to flatten the curve's peaks (inputValue, peak, strength)
+					bobOsc = filter.clipTrough(bobOsc, motion.curAnim.joints[0].bob , 2);
+					bobOsc = hipsBobLPFilter.process(bobOsc);
+			}
+			bobOsc += motion.curAnim.joints[0].bobOffset;
 
-var configSideStepLeftButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-sidestep-left-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configSideStepLeftButton);
-var configSideStepLeftButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-sidestep-left-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configSideStepLeftButtonSelected);
+			thrustOsc = motion.curAnim.joints[0].thrust *
+						Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+						motion.curAnim.joints[0].thrustPhase)) +
+						motion.curAnim.joints[0].thrustOffset;
 
-var configSideStepRightButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-sidestep-right-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configSideStepRightButton);
-var configSideStepRightButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-edit-sidestep-right-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configSideStepRightButtonSelected);
+			swayOscLast = motion.curTransition.lastAnim.joints[0].sway *
+						  Math.sin(filter.degToRad(motion.cumulativeTime *
+						  motion.curTransition.lastAnim.calibration.frequency +
+						  motion.curTransition.lastAnim.joints[0].swayPhase)) +
+						  motion.curTransition.lastAnim.joints[0].swayOffset;
 
-var configWalkJointsButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-bones-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkJointsButton);
-var configWalkJointsButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-bones-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(configWalkJointsButtonSelected);
+			bobOscLast = motion.curTransition.lastAnim.joints[0].bob *
+						 Math.sin(filter.degToRad(motion.cumulativeTime *
+						 motion.curTransition.lastAnim.calibration.frequency * 2 +
+						 motion.curTransition.lastAnim.joints[0].bobPhase)) +
+						 motion.curTransition.lastAnim.joints[0].bobOffset;
 
-var backButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-back-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(backButton);
-var backButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
-                    imageURL: pathToOverlays+"ddao-back-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-buttonOverlays.push(backButtonSelected);
+			thrustOscLast = motion.curTransition.lastAnim.joints[0].thrust *
+							Math.sin(filter.degToRad(motion.cumulativeTime *
+							motion.curTransition.lastAnim.calibration.frequency * 2 +
+							motion.curTransition.lastAnim.joints[0].thrustPhase)) +
+							motion.curTransition.lastAnim.joints[0].thrustOffset;
+		}
 
-// big button overlays - front panel
-var bigButtonYOffset = 408;  //  distance from top of panel to top of first button
+		swayOsc = (transProgress * swayOsc) + ((1 - transProgress) * swayOscLast);
+		bobOsc = (transProgress * bobOsc) + ((1 - transProgress) * bobOscLast);
+		thrustOsc = (transProgress * thrustOsc) + ((1 - transProgress) * thrustOscLast);
 
-var femaleBigButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-female-big-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(femaleBigButton);
+	}// if current transition active
+	else {
 
-var femaleBigButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-female-big-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(femaleBigButtonSelected);
+		swayOsc = motion.curAnim.joints[0].sway *
+				  Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) +
+				  motion.curAnim.joints[0].swayOffset;
 
-var maleBigButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-male-big-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(maleBigButton);
+		bobPhase = motion.curAnim.joints[0].bobPhase;
+		if (motion.direction === motion.BACKWARDS) bobPhase += 90;
+		bobOsc = motion.curAnim.joints[0].bob * Math.sin(filter.degToRad(cycle * adjFreq * 2 + bobPhase));
+		if (state.currentState === state.WALKING ||
+		   state.currentState === state.EDIT_WALK_STYLES ||
+		   state.currentState === state.EDIT_WALK_TWEAKS ||
+		   state.currentState === state.EDIT_WALK_JOINTS) {
 
-var maleBigButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-male-big-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(maleBigButtonSelected);
+				// apply clipping filter to flatten the curve's peaks (inputValue, peak, strength)
+				bobOsc = filter.clipTrough(bobOsc, motion.curAnim.joints[0].bob , 2);
+				bobOsc = hipsBobLPFilter.process(bobOsc);
+		}
+		bobOsc += motion.curAnim.joints[0].bobOffset;
 
-var armsFreeBigButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-arms-free-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(armsFreeBigButton);
+		thrustOsc = motion.curAnim.joints[0].thrust *
+					Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+					motion.curAnim.joints[0].thrustPhase)) +
+					motion.curAnim.joints[0].thrustOffset;
+	}
 
-var armsFreeBigButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-arms-free-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(armsFreeBigButtonSelected);
+	// convert local hips translations to global and apply
+	var aviOrientation = MyAvatar.orientation;
+	var front = Quat.getFront(aviOrientation);
+	var right = Quat.getRight(aviOrientation);
+	var up = Quat.getUp(aviOrientation);
+	var aviFront = Vec3.multiply(front, thrustOsc);
+	var aviRight = Vec3.multiply(right, swayOsc);
+	var aviUp = Vec3.multiply(up, bobOsc);
+	var aviTranslationOffset = {x: 0, y: 0, z: 0};
 
-var footstepsBigButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-footsteps-big-button.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(footstepsBigButton);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviFront);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviRight);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviUp);
 
-var footstepsBigButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36},
-                    imageURL: pathToOverlays+"ddao-footsteps-big-button-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(footstepsBigButtonSelected);
+	MyAvatar.setSkeletonOffset({
+		x: aviTranslationOffset.x,
+		y: aviTranslationOffset.y,
+		z: aviTranslationOffset.z
+	});
 
+	// hips rotation
+	if (motion.curTransition !== nullTransition) {
 
-// walk styles
-bigButtonYOffset = 121;
-var strutWalkBigButton = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 },
-                    imageURL: pathToOverlays+"ddao-walk-select-button-strut.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(strutWalkBigButton);
+		if (motion.curTransition.walkingAtStart) {
 
-var strutWalkBigButtonSelected = Overlays.addOverlay("image", {
-                    bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 },
-                    imageURL: pathToOverlays+"ddao-walk-select-button-strut-selected.png",
-                    color: { red: 255, green: 255, blue: 255},
-                    alpha: 1,
-                    visible: false
-                });
-bigButtonOverlays.push(strutWalkBigButtonSelected);
+			pitchOsc = motion.curAnim.joints[0].pitch *
+				Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency * 2 +
+				motion.curAnim.joints[0].pitchPhase)) + motion.curAnim.joints[0].pitchOffset;
 
-// overlays to show the walk wheel stats
-var walkWheelZLine = Overlays.addOverlay("line3d", {
-                    position: { x: 0, y: 0, z:hipsToFeetDistance },
-                    end: { x: 0, y: 0, z: -hipsToFeetDistance },
-                    color: { red: 0, green: 255, blue: 255},
-                    alpha: 1,
-                    lineWidth: 5,
-                    visible: statsOn,
-                    anchor: "MyAvatar"
-                });
-var walkWheelYLine = Overlays.addOverlay("line3d", {
-                    position: { x: 0, y: hipsToFeetDistance, z:0 },
-                    end: { x: 0, y: -hipsToFeetDistance, z:0 },
-                    color: { red: 255, green: 0, blue: 255},
-                    alpha: 1,
-                    lineWidth: 5,
-                    visible: statsOn,
-                    anchor: "MyAvatar"
-                });
-var debugStats = Overlays.addOverlay("text", {
-                    x: backgroundX-199, y: backgroundY,
-                    width: 200,
-                    height: 180,
-                    color: { red: 204, green: 204, blue: 204},
-                    topMargin: 10,
-                    leftMargin: 15,
-                    visible: statsOn,
-                    backgroundColor: { red: 34, green: 34, blue: 34},
-                    alpha: 1.0,
-                    text: "Debug Stats\n\n\nNothing to report yet."
-                });
-var debugStatsPeriodic = Overlays.addOverlay("text", {
-                    x: backgroundX-199, y: backgroundY+179,
-                    width: 200,
-                    height: 392,
-                    color: { red: 204, green: 204, blue: 204},
-                    topMargin: 5,
-                    leftMargin: 15,
-                    visible: statsOn,
-                    backgroundColor: { red: 34, green: 34, blue: 34},
-                    alpha: 1.0,
-                    text: "Debug Stats\n\n\nNothing to report yet."
-                });
-var walkWheelStats = Overlays.addOverlay("text", {
-                    x: backgroundX-199, y: backgroundY+510,
-                    width: 200,
-                    height: 190,
-                    color: { red: 204, green: 204, blue: 204},
-                    topMargin: 5,
-                    leftMargin: 15,
-                    visible: statsOn,
-                    backgroundColor: { red: 34, green: 34, blue: 34},
-                    alpha: 1.0,
-                    text: "WalkWheel Stats\n\n\nNothing to report yet.\n\n\nPlease start walking\nto see the walkwheel."
-                });
+			yawOsc = motion.curAnim.joints[0].yaw *
+				Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency +
+				motion.curAnim.joints[0].yawPhase - reverseModifier)) + motion.curAnim.joints[0].yawOffset;
 
-// various show / hide GUI element functions
-function doStandardMenu() {
-    hidebuttonOverlays();
-    hideJointControls();
-    setBackground(controlsBackground);
-    if(powerOn) setButtonOverlayVisible(onButton);
-    else setButtonOverlayVisible(offButton);
-    setButtonOverlayVisible(configWalkButton);
-    setButtonOverlayVisible(configStandButton);
-    setButtonOverlayVisible(configFlyingButton);
-    setButtonOverlayVisible(hideButton);
-    setSliderThumbsVisible(false);
-    showFrontPanelButtons(true);
-    showWalkStyleButtons(false);
-}
-function showFrontPanelButtons(showButtons) {
+			rollOsc = motion.curAnim.joints[0].roll *
+				Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency +
+				motion.curAnim.joints[0].rollPhase)) + motion.curAnim.joints[0].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[0].pitch *
+				Math.sin(filter.degToRad(motion.walkWheelPos * 2 +
+				motion.curTransition.lastAnim.joints[0].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[0].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[0].yaw *
+				Math.sin(filter.degToRad(motion.walkWheelPos +
+				motion.curTransition.lastAnim.joints[0].yawPhase));
+
+			yawOscLast += motion.curTransition.lastAnim.joints[0].yaw *
+				hipsYawShaper.shapeWave(filter.degToRad(motion.walkWheelPos +
+				motion.curTransition.lastAnim.joints[0].yawPhase - reverseModifier)) +
+				motion.curTransition.lastAnim.joints[0].yawOffset;
+
+			rollOscLast = (motion.curTransition.lastAnim.joints[0].roll *
+				Math.sin(filter.degToRad(motion.walkWheelPos +
+				motion.curTransition.lastAnim.joints[0].rollPhase)) +
+				motion.curTransition.lastAnim.joints[0].rollOffset);
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[0].pitch *
+				Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+				motion.curAnim.joints[0].pitchPhase)) +
+				motion.curAnim.joints[0].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[0].yaw *
+				Math.sin(filter.degToRad(cycle * adjFreq +
+				motion.curAnim.joints[0].yawPhase - reverseModifier)) +
+				motion.curAnim.joints[0].yawOffset;
+
+			rollOsc = motion.curAnim.joints[0].roll *
+				Math.sin(filter.degToRad(cycle * adjFreq +
+				motion.curAnim.joints[0].rollPhase)) +
+				motion.curAnim.joints[0].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[0].pitch *
+				Math.sin(filter.degToRad(motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency * 2 +
+				motion.curTransition.lastAnim.joints[0].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[0].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[0].yaw *
+				Math.sin(filter.degToRad(motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency +
+				motion.curTransition.lastAnim.joints[0].yawPhase - reverseModifier)) +
+				motion.curTransition.lastAnim.joints[0].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[0].roll *
+				Math.sin(filter.degToRad(motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency +
+				motion.curTransition.lastAnim.joints[0].rollPhase)) +
+				motion.curTransition.lastAnim.joints[0].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
 
-	if(avatarGender===FEMALE) {
-		Overlays.editOverlay(femaleBigButtonSelected, { visible: showButtons } );
-		Overlays.editOverlay(femaleBigButton, { visible: false } );
-		Overlays.editOverlay(maleBigButtonSelected, { visible: false } );
-		Overlays.editOverlay(maleBigButton, { visible: showButtons } );
 	} else {
-		Overlays.editOverlay(femaleBigButtonSelected, { visible: false } );
-		Overlays.editOverlay(femaleBigButton, { visible: showButtons } );
-		Overlays.editOverlay(maleBigButtonSelected, { visible: showButtons } );
-		Overlays.editOverlay(maleBigButton, { visible: false } );
+
+		pitchOsc = motion.curAnim.joints[0].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+			motion.curAnim.joints[0].pitchPhase)) +
+			motion.curAnim.joints[0].pitchOffset;
+
+		yawOsc = motion.curAnim.joints[0].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[0].yawPhase));
+
+		yawOsc += motion.curAnim.joints[0].yaw *
+			  hipsYawShaper.shapeWave(filter.degToRad(cycle * adjFreq) +
+			  motion.curAnim.joints[0].yawPhase - reverseModifier)+
+			  motion.curAnim.joints[0].yawOffset;
+
+		rollOsc = (motion.curAnim.joints[0].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[0].rollPhase)) +
+				motion.curAnim.joints[0].rollOffset);
 	}
-	if(armsFree) {
-		Overlays.editOverlay(armsFreeBigButtonSelected, { visible: showButtons } );
-		Overlays.editOverlay(armsFreeBigButton, { visible: false } );
+
+	// apply hips rotation
+	MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees(
+									pitchOsc + (leanMod * getLeanPitch(speed)),
+									yawOsc,
+									rollOsc + getLeanRoll(deltaTime, speed)));
+
+	// upper legs
+	if (state.currentState !== state.SIDE_STEP &&
+	   state.currentState !== state.EDIT_SIDESTEP_LEFT &&
+	   state.currentState !== state.EDIT_SIDESTEP_RIGHT) {
+
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOscLeft = motion.curAnim.joints[1].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					reverseModifier * motion.curAnim.joints[1].pitchPhase));
+
+				pitchOscRight = motion.curAnim.joints[1].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					reverseModifier * motion.curAnim.joints[1].pitchPhase));
+
+				yawOsc = motion.curAnim.joints[1].yaw *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[1].yawPhase));
+
+				rollOsc = motion.curAnim.joints[1].roll *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[1].rollPhase));
+
+				pitchOffset = motion.curAnim.joints[1].pitchOffset;
+				yawOffset = motion.curAnim.joints[1].yawOffset;
+				rollOffset = motion.curAnim.joints[1].rollOffset;
+
+				pitchOscLeftLast = motion.curTransition.lastAnim.joints[1].pitch *
+					motion.curTransition.lastAnim.harmonics.leftUpperLeg.calculate(
+					filter.degToRad(motion.walkWheelPos +
+						motion.curTransition.lastAnim.joints[1].pitchPhase + 180 + reverseModifier));
+
+				pitchOscRightLast = motion.curTransition.lastAnim.joints[1].pitch *
+					motion.curTransition.lastAnim.harmonics.rightUpperLeg.calculate(
+					filter.degToRad(motion.walkWheelPos +
+						motion.curTransition.lastAnim.joints[1].pitchPhase + reverseModifier));
+
+				yawOscLast = motion.curTransition.lastAnim.joints[1].yaw *
+					Math.sin(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[1].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[1].roll *
+					Math.sin(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[1].rollPhase));
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[1].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[1].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[1].rollOffset;
+
+			} else {
+
+				if (state.currentState === state.WALKING ||
+				   state.currentState === state.EDIT_WALK_STYLES ||
+				   state.currentState === state.EDIT_WALK_TWEAKS ||
+				   state.currentState === state.EDIT_WALK_JOINTS) {
+
+					pitchOscLeft = motion.curAnim.joints[1].pitch *
+								   motion.curAnim.harmonics.leftUpperLeg.calculate(filter.degToRad(cycle * adjFreq +
+								   motion.curAnim.joints[1].pitchPhase + 180 + reverseModifier));
+					pitchOscRight = motion.curAnim.joints[1].pitch *
+									motion.curAnim.harmonics.rightUpperLeg.calculate(filter.degToRad(cycle * adjFreq +
+									motion.curAnim.joints[1].pitchPhase + reverseModifier));
+				} else {
+
+					pitchOscLeft = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq
+												+ motion.curAnim.joints[1].pitchPhase));
+					pitchOscRight = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq
+												+ motion.curAnim.joints[1].pitchPhase));
+				}
+
+				yawOsc = motion.curAnim.joints[1].yaw *
+					Math.sin(filter.degToRad(cycle * adjFreq +
+					motion.curAnim.joints[1].yawPhase));
+
+				rollOsc = motion.curAnim.joints[1].roll *
+					Math.sin(filter.degToRad(cycle * adjFreq +
+					motion.curAnim.joints[1].rollPhase));
+
+				pitchOffset = motion.curAnim.joints[1].pitchOffset;
+				yawOffset = motion.curAnim.joints[1].yawOffset;
+				rollOffset = motion.curAnim.joints[1].rollOffset;
+
+				pitchOscLeftLast = motion.curTransition.lastAnim.joints[1].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					reverseModifier * motion.curTransition.lastAnim.joints[1].pitchPhase));
+
+				pitchOscRightLast = motion.curTransition.lastAnim.joints[1].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					reverseModifier * motion.curTransition.lastAnim.joints[1].pitchPhase));
+
+				yawOscLast = motion.curTransition.lastAnim.joints[1].yaw *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[1].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[1].roll *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[1].rollPhase));
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[1].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[1].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[1].rollOffset;
+			}
+			pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast);
+			pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast);
+			yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast);
+			yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+			rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
+		} else {
+
+			if (state.currentState === state.WALKING ||
+			   state.currentState === state.EDIT_WALK_STYLES ||
+			   state.currentState === state.EDIT_WALK_TWEAKS ||
+			   state.currentState === state.EDIT_WALK_JOINTS) {
+
+				pitchOscLeft = motion.curAnim.joints[1].pitch *
+							   motion.curAnim.harmonics.leftUpperLeg.calculate(filter.degToRad(cycle * adjFreq +
+							   motion.curAnim.joints[1].pitchPhase + 180 + reverseModifier));
+				pitchOscRight = motion.curAnim.joints[1].pitch *
+								motion.curAnim.harmonics.rightUpperLeg.calculate(filter.degToRad(cycle * adjFreq +
+								motion.curAnim.joints[1].pitchPhase + reverseModifier));
+			} else {
+
+				pitchOscLeft = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq
+											+ motion.curAnim.joints[1].pitchPhase));
+				pitchOscRight = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq
+											+ motion.curAnim.joints[1].pitchPhase));
+			}
+
+			yawOsc = motion.curAnim.joints[1].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[1].yawPhase));
+
+			rollOsc = motion.curAnim.joints[1].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[1].rollPhase));
+
+			pitchOffset = motion.curAnim.joints[1].pitchOffset;
+			yawOffset = motion.curAnim.joints[1].yawOffset;
+			rollOffset = motion.curAnim.joints[1].rollOffset;
+		}
+
+		// apply the upper leg rotations
+		MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(
+			pitchOscLeft + pitchOffset,
+			yawOsc - yawOffset,
+			-rollOsc + rollOffset));
+
+		MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(
+			pitchOscRight + pitchOffset,
+			yawOsc + yawOffset,
+			-rollOsc - rollOffset));
+
+		// lower leg
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOscLeft = motion.curAnim.joints[2].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[2].pitchPhase + 180));
+
+				pitchOscRight = motion.curAnim.joints[2].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[2].pitchPhase));
+
+				yawOsc = motion.curAnim.joints[2].yaw *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[2].yawPhase));
+
+				rollOsc = motion.curAnim.joints[2].roll *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curAnim.calibration.frequency +
+					motion.curAnim.joints[2].rollPhase));
+
+				pitchOffset = motion.curAnim.joints[2].pitchOffset;
+				yawOffset = motion.curAnim.joints[2].yawOffset;
+				rollOffset = motion.curAnim.joints[2].rollOffset;
+
+				pitchOscLeftLast = motion.curTransition.lastAnim.joints[2].pitch *
+					motion.curTransition.lastAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[2].pitchPhase + 180));
+
+				pitchOscRightLast = motion.curTransition.lastAnim.joints[2].pitch *
+					motion.curTransition.lastAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[2].pitchPhase));
+
+				yawOscLast = motion.curTransition.lastAnim.joints[2].yaw *
+					Math.sin(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[2].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[2].roll *
+					Math.sin(filter.degToRad(motion.walkWheelPos +
+					motion.curTransition.lastAnim.joints[2].rollPhase));
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[2].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[2].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[2].rollOffset;
+
+			} else {
+
+				if (state.currentState === state.WALKING ||
+				   state.currentState === state.EDIT_WALK_STYLES ||
+				   state.currentState === state.EDIT_WALK_TWEAKS ||
+				   state.currentState === state.EDIT_WALK_JOINTS) {
+
+					pitchOscLeft = motion.curAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+								   motion.curAnim.joints[2].pitchPhase + 180));
+					pitchOscRight = motion.curAnim.harmonics.rightLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+									motion.curAnim.joints[2].pitchPhase));
+
+				} else {
+
+					pitchOscLeft = Math.sin(filter.degToRad((cycle * adjFreq) +
+								   motion.curAnim.joints[2].pitchPhase + 180));
+
+					pitchOscRight = Math.sin(filter.degToRad((cycle * adjFreq) +
+									motion.curAnim.joints[2].pitchPhase));
+				}
+				pitchOscLeft *= motion.curAnim.joints[2].pitch;
+				pitchOscRight *= motion.curAnim.joints[2].pitch;
+
+				yawOsc = motion.curAnim.joints[2].yaw *
+						 Math.sin(filter.degToRad(cycle * adjFreq +
+						 motion.curAnim.joints[2].yawPhase));
+
+				rollOsc = motion.curAnim.joints[2].roll *
+						  Math.sin(filter.degToRad(cycle * adjFreq +
+						  motion.curAnim.joints[2].rollPhase));
+
+				pitchOffset = motion.curAnim.joints[2].pitchOffset;
+				yawOffset = motion.curAnim.joints[2].yawOffset;
+				rollOffset = motion.curAnim.joints[2].rollOffset;
+
+				pitchOscLeftLast = motion.curTransition.lastAnim.joints[2].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[2].pitchPhase + 180));
+
+				pitchOscRightLast = motion.curTransition.lastAnim.joints[2].pitch *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[2].pitchPhase));
+
+				yawOscLast = motion.curTransition.lastAnim.joints[2].yaw *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[2].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[2].roll *
+					Math.sin(filter.degToRad(motion.cumulativeTime *
+					motion.curTransition.lastAnim.calibration.frequency +
+					motion.curTransition.lastAnim.joints[2].rollPhase));
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[2].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[2].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[2].rollOffset;
+			}
+
+			pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast);
+			pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast);
+			yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast);
+			yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+			rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
+			rollOscLeft = rollOsc;
+			rollOscRight = rollOsc;
+
+		} else { // end if transitioning
+
+			if (state.currentState === state.WALKING ||
+			   state.currentState === state.EDIT_WALK_STYLES ||
+			   state.currentState === state.EDIT_WALK_TWEAKS ||
+			   state.currentState === state.EDIT_WALK_JOINTS) {
+
+				pitchOscLeft = motion.curAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+							   motion.curAnim.joints[2].pitchPhase + 180));
+				pitchOscRight = motion.curAnim.harmonics.rightLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+								motion.curAnim.joints[2].pitchPhase));
+
+			} else {
+
+				pitchOscLeft = Math.sin(filter.degToRad((cycle * adjFreq) +
+							   motion.curAnim.joints[2].pitchPhase + 180));
+
+				pitchOscRight = Math.sin(filter.degToRad((cycle * adjFreq) +
+								motion.curAnim.joints[2].pitchPhase));
+			}
+
+			pitchOscLeft *= motion.curAnim.joints[2].pitch;
+			pitchOscRight *= motion.curAnim.joints[2].pitch;
+
+			yawOsc = motion.curAnim.joints[2].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[2].yawPhase));
+
+			rollOsc = Math.sin(filter.degToRad((cycle * adjFreq) +
+						   motion.curAnim.joints[2].rollPhase));
+
+			rollOsc = motion.curAnim.joints[2].roll;
+
+			pitchOffset = motion.curAnim.joints[2].pitchOffset;
+			yawOffset = motion.curAnim.joints[2].yawOffset;
+			rollOffset = motion.curAnim.joints[2].rollOffset;
+		}
+
+		pitchOscLeft += pitchOffset;
+		pitchOscRight += pitchOffset;
+
+		// apply lower leg joint rotations
+		MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(
+			pitchOscLeft,
+			yawOsc + yawOffset,
+			rollOsc + rollOffset));
+		MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
+			pitchOscRight,
+			yawOsc - yawOffset,
+			rollOsc - rollOffset));
+
+	} // end if !state.SIDE_STEP
+
+	else if (state.currentState === state.SIDE_STEP ||
+		state.currentState === state.EDIT_SIDESTEP_LEFT ||
+		state.currentState === state.EDIT_SIDESTEP_RIGHT) {
+
+		// sidestepping uses the sinewave generators slightly differently for the legs
+		pitchOsc = motion.curAnim.joints[1].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[1].pitchPhase));
+
+		yawOsc = motion.curAnim.joints[1].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[1].yawPhase));
+
+		rollOsc = motion.curAnim.joints[1].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[1].rollPhase));
+
+		// apply upper leg rotations for sidestepping
+		MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(
+			-pitchOsc + motion.curAnim.joints[1].pitchOffset,
+			yawOsc + motion.curAnim.joints[1].yawOffset,
+			rollOsc + motion.curAnim.joints[1].rollOffset));
+
+		MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(
+			pitchOsc + motion.curAnim.joints[1].pitchOffset,
+			yawOsc - motion.curAnim.joints[1].yawOffset,
+			-rollOsc - motion.curAnim.joints[1].rollOffset));
+
+		// calculate lower leg joint rotations for sidestepping
+		pitchOsc = motion.curAnim.joints[2].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[2].pitchPhase));
+
+		yawOsc = motion.curAnim.joints[2].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[2].yawPhase));
+
+		rollOsc = motion.curAnim.joints[2].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[2].rollPhase));
+
+		// apply lower leg joint rotations
+		MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
+			-pitchOsc + motion.curAnim.joints[2].pitchOffset,
+			yawOsc - motion.curAnim.joints[2].yawOffset,
+			rollOsc - motion.curAnim.joints[2].rollOffset));
+
+		MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(
+			pitchOsc + motion.curAnim.joints[2].pitchOffset,
+			yawOsc + motion.curAnim.joints[2].yawOffset,
+			rollOsc + motion.curAnim.joints[2].rollOffset));
+	}
+
+	// feet
+	if (motion.curAnim === motion.selSideStepLeft ||
+		motion.curAnim === motion.selSideStepRight ) {
+
+		sideStepHandPitchSign = -1;
+		sideStepFootPitchModifier = 0.5;
+	}
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOscLeft = motion.curAnim.joints[3].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[3].pitchPhase) + 180);
+
+			pitchOscRight = motion.curAnim.joints[3].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[3].pitchPhase));
+
+			yawOsc = motion.curAnim.joints[3].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[3].yawPhase));
+
+			rollOsc = motion.curAnim.joints[3].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[3].rollPhase));
+
+			pitchOffset = motion.curAnim.joints[3].pitchOffset;
+			yawOffset = motion.curAnim.joints[3].yawOffset;
+			rollOffset = motion.curAnim.joints[3].rollOffset;
+
+			pitchOscLeftLast = motion.curTransition.lastAnim.joints[3].pitch *
+				motion.curTransition.lastAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * motion.walkWheelPos +
+				motion.curTransition.lastAnim.joints[3].pitchPhase + reverseModifier));
+
+			pitchOscRightLast = motion.curTransition.lastAnim.joints[3].pitch *
+				motion.curTransition.lastAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * motion.walkWheelPos +
+				motion.curTransition.lastAnim.joints[3].pitchPhase + 180 + reverseModifier));
+
+			yawOscLast = motion.curTransition.lastAnim.joints[3].yaw *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[3].yawPhase));
+
+			rollOscLast = motion.curTransition.lastAnim.joints[3].roll *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[3].rollPhase));
+
+			pitchOffsetLast = motion.curTransition.lastAnim.joints[3].pitchOffset;
+			yawOffsetLast = motion.curTransition.lastAnim.joints[3].yawOffset;
+			rollOffsetLast = motion.curTransition.lastAnim.joints[3].rollOffset;
+
+		} else {
+
+			if (state.currentState === state.WALKING ||
+			   state.currentState === state.EDIT_WALK_STYLES ||
+			   state.currentState === state.EDIT_WALK_TWEAKS ||
+			   state.currentState === state.EDIT_WALK_JOINTS) {
+
+				pitchOscLeft = motion.curAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+							   motion.curAnim.joints[3].pitchPhase + reverseModifier));
+
+				pitchOscRight = motion.curAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+								motion.curAnim.joints[3].pitchPhase + 180 + reverseModifier));
+
+			} else {
+
+				pitchOscLeft = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier +
+							   motion.curAnim.joints[3].pitchPhase));
+
+				pitchOscRight = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier +
+								motion.curAnim.joints[3].pitchPhase + 180));
+			}
+
+			yawOsc = motion.curAnim.joints[3].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[3].yawPhase));
+
+			rollOsc = motion.curAnim.joints[3].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[3].rollPhase));
+
+			pitchOffset = motion.curAnim.joints[3].pitchOffset;
+			yawOffset = motion.curAnim.joints[3].yawOffset;
+			rollOffset = motion.curAnim.joints[3].rollOffset;
+
+			pitchOscLeftLast = motion.curTransition.lastAnim.joints[3].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[3].pitchPhase + 180));
+
+			pitchOscRightLast = motion.curTransition.lastAnim.joints[3].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[3].pitchPhase));
+
+			yawOscLast = motion.curTransition.lastAnim.joints[3].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[3].yawPhase));
+
+			rollOscLast = motion.curTransition.lastAnim.joints[3].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[3].rollPhase));
+
+			pitchOffsetLast = motion.curTransition.lastAnim.joints[3].pitchOffset;
+			yawOffsetLast = motion.curTransition.lastAnim.joints[3].yawOffset;
+			rollOffsetLast = motion.curTransition.lastAnim.joints[3].rollOffset;
+		}
+
+		pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast);
+		pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+		pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast);
+		yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+		rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
 	} else {
-		Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } );
-		Overlays.editOverlay(armsFreeBigButton, { visible: showButtons } );
+
+		if (state.currentState === state.WALKING ||
+		   state.currentState === state.EDIT_WALK_STYLES ||
+		   state.currentState === state.EDIT_WALK_TWEAKS ||
+		   state.currentState === state.EDIT_WALK_JOINTS) {
+
+			pitchOscLeft = motion.curAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+						   motion.curAnim.joints[3].pitchPhase + reverseModifier));
+
+			pitchOscRight = motion.curAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq +
+							motion.curAnim.joints[3].pitchPhase + 180 + reverseModifier));
+
+		} else {
+
+			pitchOscLeft = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier +
+						   motion.curAnim.joints[3].pitchPhase));
+
+			pitchOscRight = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier +
+							motion.curAnim.joints[3].pitchPhase + 180));
+		}
+
+		yawOsc = Math.sin(filter.degToRad((cycle * adjFreq) +
+						motion.curAnim.joints[3].yawPhase));
+
+		pitchOscLeft *= motion.curAnim.joints[3].pitch;
+		pitchOscRight *= motion.curAnim.joints[3].pitch;
+
+		yawOsc *= motion.curAnim.joints[3].yaw;
+
+		rollOsc = motion.curAnim.joints[3].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[3].rollPhase));
+
+		pitchOffset = motion.curAnim.joints[3].pitchOffset;
+		yawOffset = motion.curAnim.joints[3].yawOffset;
+		rollOffset = motion.curAnim.joints[3].rollOffset;
 	}
-	if(playFootStepSounds) {
-		Overlays.editOverlay(footstepsBigButtonSelected, { visible: showButtons } );
-		Overlays.editOverlay(footstepsBigButton, { visible: false } );
+
+	// apply foot rotations
+	MyAvatar.setJointData("LeftFoot", Quat.fromPitchYawRollDegrees(
+		pitchOscLeft + pitchOffset,
+		yawOsc - yawOffset,
+		rollOsc - rollOffset));
+
+	MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees(
+		pitchOscRight + pitchOffset,
+		yawOsc + yawOffset,
+		rollOsc + rollOffset));
+
+	// play footfall sound yet? To determine this, we take the differential of the
+	// foot's pitch curve to decide when the foot hits the ground.
+	if (state.currentState === state.WALKING ||
+		state.currentState === state.SIDE_STEP ||
+		state.currentState === state.EDIT_WALK_STYLES ||
+		state.currentState === state.EDIT_WALK_TWEAKS ||
+		state.currentState === state.EDIT_WALK_JOINTS ||
+		state.currentState === state.EDIT_SIDESTEP_LEFT ||
+		state.currentState === state.EDIT_SIDESTEP_RIGHT) {
+
+		// find dy/dx by determining the cosine wave for the foot's pitch function.
+		var feetPitchDifferential = Math.cos(filter.degToRad((cycle * adjFreq) + motion.curAnim.joints[3].pitchPhase));
+		var threshHold = 0.9; // sets the audio trigger point. with accuracy.
+		if (feetPitchDifferential < -threshHold &&
+			motion.nextStep === LEFT &&
+			motion.direction !== UP &&
+			motion.direction !== DOWN) {
+
+			playFootstep(LEFT);
+			motion.nextStep = RIGHT;
+		} else if (feetPitchDifferential > threshHold &&
+			motion.nextStep === RIGHT &&
+			motion.direction !== UP &&
+			motion.direction !== DOWN) {
+
+			playFootstep(RIGHT);
+			motion.nextStep = LEFT;
+		}
+	}
+
+	// toes
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOsc = motion.curAnim.joints[4].pitch *
+				Math.sin(filter.degToRad((2 * motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[4].pitchPhase));
+
+			yawOsc = motion.curAnim.joints[4].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[4].yawPhase));
+
+			rollOsc = motion.curAnim.joints[4].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[4].rollPhase));
+
+			pitchOffset = motion.curAnim.joints[4].pitchOffset;
+			yawOffset = motion.curAnim.joints[4].yawOffset;
+			rollOffset = motion.curAnim.joints[4].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[4].pitch *
+				Math.sin(filter.degToRad((2 * motion.walkWheelPos) +
+					motion.curTransition.lastAnim.joints[4].pitchPhase));
+
+			yawOscLast = motion.curTransition.lastAnim.joints[4].yaw *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+					motion.curTransition.lastAnim.joints[4].yawPhase));
+
+			rollOscLast = motion.curTransition.lastAnim.joints[4].roll *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+					motion.curTransition.lastAnim.joints[4].rollPhase));
+
+			pitchOffsetLast = motion.curTransition.lastAnim.joints[4].pitchOffset;
+			yawOffsetLast = motion.curTransition.lastAnim.joints[4].yawOffset;
+			rollOffsetLast = motion.curTransition.lastAnim.joints[4].rollOffset;
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[4].pitch *
+				Math.sin(filter.degToRad((2 * cycle * adjFreq) +
+					motion.curAnim.joints[4].pitchPhase));
+
+			yawOsc = motion.curAnim.joints[4].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[4].yawPhase));
+
+			rollOsc = motion.curAnim.joints[4].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+					motion.curAnim.joints[4].rollPhase));
+
+			pitchOffset = motion.curAnim.joints[4].pitchOffset;
+			yawOffset = motion.curAnim.joints[4].yawOffset;
+			rollOffset = motion.curAnim.joints[4].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[4].pitch *
+				Math.sin(filter.degToRad((2 * motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[4].pitchPhase));
+
+			yawOscLast = motion.curTransition.lastAnim.joints[4].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[4].yawPhase));
+
+			rollOscLast = motion.curTransition.lastAnim.joints[4].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[4].rollPhase));
+
+			pitchOffsetLast = motion.curTransition.lastAnim.joints[4].pitchOffset;
+			yawOffsetLast = motion.curTransition.lastAnim.joints[4].yawOffset;
+			rollOffsetLast = motion.curTransition.lastAnim.joints[4].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+		pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast);
+		yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+		rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
 	} else {
-		Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } );
-		Overlays.editOverlay(footstepsBigButton, { visible: showButtons } );
-	}
-}
-function minimiseDialog() {
 
-	if(minimised) {
-        setBackground();
-        hidebuttonOverlays();
-        setSliderThumbsVisible(false);
-        hideJointControls();
-        showFrontPanelButtons(false);
-        Overlays.editOverlay(controlsMinimisedTab, { visible: true } );
-    } else {
-        setInternalState(STANDING); // show all the controls again
-        Overlays.editOverlay(controlsMinimisedTab, { visible: false } );
-    }
-}
-function setBackground(backgroundName)  {
-    for(var i in backgroundOverlays) {
-        if(backgroundOverlays[i] === backgroundName)
-            Overlays.editOverlay(backgroundName, { visible: true } );
-        else Overlays.editOverlay(backgroundOverlays[i], { visible: false } );
-    }
-}
-function setButtonOverlayVisible(buttonOverlayName) {
-    for(var i in buttonOverlays) {
-        if(buttonOverlays[i] === buttonOverlayName) {
-            Overlays.editOverlay(buttonOverlayName, { visible: true } );
-        }
-    }
-}
-// top row menu type buttons (smaller)
-function hidebuttonOverlays()  {
-    for(var i in buttonOverlays) {
-        Overlays.editOverlay(buttonOverlays[i], { visible: false } );
-    }
-}
-function hideJointControls() {
-    for(var i in jointsControlOverlays) {
-        Overlays.editOverlay(jointsControlOverlays[i], { visible: false } );
-    }
-}
-function setSliderThumbsVisible(thumbsVisible) {
-    for(var i = 0 ; i < sliderThumbOverlays.length ; i++) {
-        Overlays.editOverlay(sliderThumbOverlays[i], { visible: thumbsVisible } );
-    }
-}
-function initialiseJointsEditingPanel(propertyIndex) {
+		pitchOsc = motion.curAnim.joints[4].pitch *
+			Math.sin(filter.degToRad((2 * cycle * adjFreq) +
+				motion.curAnim.joints[4].pitchPhase));
 
-    selectedJointIndex = propertyIndex;
+		yawOsc = motion.curAnim.joints[4].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[4].yawPhase));
 
-    // set the image for the selected joint on the character control
-    hideJointControls();
-    switch (selectedJointIndex) {
-        case 0:
-            Overlays.editOverlay(hipsJointControl, { visible: true });
-            break;
-        case 1:
-            Overlays.editOverlay(upperLegsJointControl, { visible: true });
-            break;
-        case 2:
-            Overlays.editOverlay(lowerLegsJointControl, { visible: true });
-            break;
-        case 3:
-            Overlays.editOverlay(feetJointControl, { visible: true });
-            break;
-        case 4:
-            Overlays.editOverlay(toesJointControl, { visible: true });
-            break;
-        case 5:
-            Overlays.editOverlay(spineJointControl, { visible: true });
-            break;
-        case 6:
-            Overlays.editOverlay(spine1JointControl, { visible: true });
-            break;
-        case 7:
-            Overlays.editOverlay(spine2JointControl, { visible: true });
-            break;
-        case 8:
-            Overlays.editOverlay(shouldersJointControl, { visible: true });
-            break;
-        case 9:
-            Overlays.editOverlay(upperArmsJointControl, { visible: true });
-            break;
-        case 10:
-            Overlays.editOverlay(forearmsJointControl, { visible: true });
-            break;
-        case 11:
-            Overlays.editOverlay(handsJointControl, { visible: true });
-            break;
-        case 12:
-            Overlays.editOverlay(headJointControl, { visible: true });
-            break;
-    }
+		rollOsc = motion.curAnim.joints[4].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[4].rollPhase));
 
-    // set sliders to adjust individual joint properties
-    var i = 0;
-    var yLocation = backgroundY+359;
-
-	// pitch your role
-    var sliderXPos = currentAnimation.joints[selectedJointIndex].pitch
-    		/ sliderRanges.joints[selectedJointIndex].pitchRange * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = currentAnimation.joints[selectedJointIndex].yaw
-    		/ sliderRanges.joints[selectedJointIndex].yawRange * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = currentAnimation.joints[selectedJointIndex].roll
-    		/ sliderRanges.joints[selectedJointIndex].rollRange * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-
-    // set phases (full range, -180 to 180)
-    sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].pitchPhase/2)/180 * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].yawPhase/2)/180 * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].rollPhase/2)/180 * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-
-    // offset ranges are also -ve thr' zero to +ve, so have to offset
-    sliderXPos = (((sliderRanges.joints[selectedJointIndex].pitchOffsetRange+currentAnimation.joints[selectedJointIndex].pitchOffset)/2)
-    			/sliderRanges.joints[selectedJointIndex].pitchOffsetRange) * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = (((sliderRanges.joints[selectedJointIndex].yawOffsetRange+currentAnimation.joints[selectedJointIndex].yawOffset)/2)
-    			/sliderRanges.joints[selectedJointIndex].yawOffsetRange) * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-    sliderXPos = (((sliderRanges.joints[selectedJointIndex].rollOffsetRange+currentAnimation.joints[selectedJointIndex].rollOffset)/2)
-    			/sliderRanges.joints[selectedJointIndex].rollOffsetRange) * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
-}
-
-function initialiseWalkTweaks() {
-
-    // set sliders to adjust walk properties
-    var i = 0;
-    var yLocation = backgroundY+71;
-
-    var sliderXPos = currentAnimation.settings.baseFrequency / MAX_WALK_SPEED * sliderRangeX;   // walk speed
-    Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = 0 * sliderRangeX;             // start flying speed - depricated
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = currentAnimation.joints[0].sway / sliderRanges.joints[0].swayRange * sliderRangeX;     // Hips sway
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = currentAnimation.joints[0].bob / sliderRanges.joints[0].bobRange * sliderRangeX;       // Hips bob
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = currentAnimation.joints[0].thrust / sliderRanges.joints[0].thrustRange * sliderRangeX; // Hips thrust
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = (((sliderRanges.joints[1].rollOffsetRange+currentAnimation.joints[1].rollOffset)/2) // legs separation - is upper legs roll offset
-    				/ sliderRanges.joints[1].rollOffsetRange) * sliderRangeX;
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = currentAnimation.joints[1].pitch / sliderRanges.joints[1].pitchRange * sliderRangeX; // stride - is upper legs pitch
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = currentAnimation.joints[9].yaw / sliderRanges.joints[9].yawRange * sliderRangeX; // arms swing - is upper arms yaw
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-    sliderXPos = (((sliderRanges.joints[9].pitchOffsetRange-currentAnimation.joints[9].pitchOffset)/2)
-    				/ sliderRanges.joints[9].pitchOffsetRange) * sliderRangeX; // arms out - is upper arms pitch offset
-    Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
-}
-
-function showWalkStyleButtons(showButtons) {
-
-	// set all big buttons to hidden, but skip the first 8, as are for the front panel
-	for(var i = 8 ; i < bigButtonOverlays.length ; i++) {
-		Overlays.editOverlay(bigButtonOverlays[i], { visible: false });
+		pitchOffset = motion.curAnim.joints[4].pitchOffset;
+		yawOffset = motion.curAnim.joints[4].yawOffset;
+		rollOffset = motion.curAnim.joints[4].rollOffset;
 	}
 
-	if(!showButtons) return;
+	// apply toe rotations
+	MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees(
+		pitchOsc + pitchOffset,
+		yawOsc + yawOffset,
+		rollOsc + rollOffset));
 
-	// set all the non-selected ones to showing
-	for(var i = 8 ; i < bigButtonOverlays.length ; i+=2) {
-		Overlays.editOverlay(bigButtonOverlays[i], { visible: showButtons });
+	MyAvatar.setJointData("LeftToeBase", Quat.fromPitchYawRollDegrees(
+		pitchOsc + pitchOffset,
+		yawOsc - yawOffset,
+		rollOsc - rollOffset));
+
+	// spine
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOsc = motion.curAnim.joints[5].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency * 2) +
+				motion.curAnim.joints[5].pitchPhase)) +
+				motion.curAnim.joints[5].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[5].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[5].yawPhase)) +
+				motion.curAnim
+				.joints[5].yawOffset;
+
+			rollOsc = motion.curAnim.joints[5].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[5].rollPhase)) +
+				motion.curAnim.joints[5].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[5].pitch *
+				Math.sin(filter.degToRad((motion.walkWheelPos * 2) +
+				motion.curTransition.lastAnim.joints[5].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[5].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[5].yaw *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[5].yawPhase)) +
+				motion.curTransition.lastAnim.joints[5].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[5].roll *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[5].rollPhase)) +
+				motion.curTransition.lastAnim.joints[5].rollOffset;
+		} else {
+
+			pitchOsc = motion.curAnim.joints[5].pitch *
+				Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+				motion.curAnim.joints[5].pitchPhase)) +
+				motion.curAnim.joints[5].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[5].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[5].yawPhase)) +
+				motion.curAnim.joints[5].yawOffset;
+
+			rollOsc = motion.curAnim.joints[5].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[5].rollPhase)) +
+				motion.curAnim.joints[5].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[5].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency * 2) +
+				motion.curTransition.lastAnim.joints[5].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[5].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[5].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[5].yawPhase)) +
+				motion.curTransition.lastAnim.joints[5].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[5].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[5].rollPhase)) +
+				motion.curTransition.lastAnim.joints[5].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+	} else {
+
+		pitchOsc = motion.curAnim.joints[5].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+			motion.curAnim.joints[5].pitchPhase)) +
+			motion.curAnim.joints[5].pitchOffset;
+
+		yawOsc = motion.curAnim.joints[5].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[5].yawPhase)) +
+			motion.curAnim.joints[5].yawOffset;
+
+		rollOsc = motion.curAnim.joints[5].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[5].rollPhase)) +
+			motion.curAnim.joints[5].rollOffset;
 	}
 
-	// set the currently selected one
-	if(selectedWalk === femaleStrutWalk || selectedWalk === maleStrutWalk) {
-		Overlays.editOverlay(strutWalkBigButtonSelected, { visible: showButtons });
-		Overlays.editOverlay(strutWalkBigButton, {visible: false});
+	// apply spine joint rotations
+	MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc));
+
+	// spine 1
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOsc = motion.curAnim.joints[6].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency * 2) +
+				motion.curAnim.joints[6].pitchPhase)) +
+				motion.curAnim.joints[6].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[6].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[6].yawPhase)) +
+				motion.curAnim.joints[6].yawOffset;
+
+			rollOsc = motion.curAnim.joints[6].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[6].rollPhase)) +
+				motion.curAnim.joints[6].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[6].pitch *
+				Math.sin(filter.degToRad((motion.walkWheelPos * 2) +
+				motion.curTransition.lastAnim.joints[6].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[6].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[6].yaw *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[6].yawPhase)) +
+				motion.curTransition.lastAnim.joints[6].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[6].roll *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[6].rollPhase)) +
+				motion.curTransition.lastAnim.joints[6].rollOffset;
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[6].pitch *
+				Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+				motion.curAnim.joints[6].pitchPhase)) +
+				motion.curAnim.joints[6].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[6].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[6].yawPhase)) +
+				motion.curAnim.joints[6].yawOffset;
+
+			rollOsc = motion.curAnim.joints[6].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[6].rollPhase)) +
+				motion.curAnim.joints[6].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[6].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency * 2) +
+				motion.curTransition.lastAnim.joints[6].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[6].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[6].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[6].yawPhase)) +
+				motion.curTransition.lastAnim.joints[6].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[6].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[6].rollPhase)) +
+				motion.curTransition.lastAnim.joints[6].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+	} else {
+
+		pitchOsc = motion.curAnim.joints[6].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+			motion.curAnim.joints[6].pitchPhase)) +
+			motion.curAnim.joints[6].pitchOffset;
+
+		yawOsc = motion.curAnim.joints[6].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[6].yawPhase)) +
+			motion.curAnim.joints[6].yawOffset;
+
+		rollOsc = motion.curAnim.joints[6].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[6].rollPhase)) +
+			motion.curAnim.joints[6].rollOffset;
 	}
+
+	// apply spine1 joint rotations
+	MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc));
+
+	// spine 2
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOsc = motion.curAnim.joints[7].pitch *
+					   Math.sin(filter.degToRad(motion.cumulativeTime *
+					   motion.curAnim.calibration.frequency * 2 +
+					   motion.curAnim.joints[7].pitchPhase)) +
+					   motion.curAnim.joints[7].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[7].yaw *
+					 Math.sin(filter.degToRad(motion.cumulativeTime *
+					 motion.curAnim.calibration.frequency +
+					 motion.curAnim.joints[7].yawPhase)) +
+					 motion.curAnim.joints[7].yawOffset;
+
+			rollOsc = motion.curAnim.joints[7].roll *
+					  Math.sin(filter.degToRad(motion.cumulativeTime *
+					  motion.curAnim.calibration.frequency +
+					  motion.curAnim.joints[7].rollPhase)) +
+					  motion.curAnim.joints[7].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[7].pitch *
+						   Math.sin(filter.degToRad(motion.walkWheelPos * 2 +
+						   motion.curTransition.lastAnim.joints[7].pitchPhase)) +
+						   motion.curTransition.lastAnim.joints[7].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[7].yaw *
+						 Math.sin(filter.degToRad(motion.walkWheelPos +
+						 motion.curTransition.lastAnim.joints[7].yawPhase)) +
+						 motion.curTransition.lastAnim.joints[7].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[7].roll *
+						  Math.sin(filter.degToRad(motion.walkWheelPos +
+						  motion.curTransition.lastAnim.joints[7].rollPhase)) +
+						  motion.curTransition.lastAnim.joints[7].rollOffset;
+		} else {
+
+			pitchOsc = motion.curAnim.joints[7].pitch *
+					   Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+					   motion.curAnim.joints[7].pitchPhase)) +
+					   motion.curAnim.joints[7].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[7].yaw *
+					 Math.sin(filter.degToRad(cycle * adjFreq +
+					 motion.curAnim.joints[7].yawPhase)) +
+					 motion.curAnim.joints[7].yawOffset;
+
+			rollOsc = motion.curAnim.joints[7].roll *
+					  Math.sin(filter.degToRad(cycle * adjFreq +
+					  motion.curAnim.joints[7].rollPhase)) +
+					  motion.curAnim.joints[7].rollOffset;
+
+			pitchOscLast = motion.curTransition.lastAnim.joints[7].pitch *
+						   Math.sin(filter.degToRad(motion.cumulativeTime * 2 *
+						   motion.curTransition.lastAnim.calibration.frequency +
+						   motion.curTransition.lastAnim.joints[7].pitchPhase)) +
+						   motion.curTransition.lastAnim.joints[7].pitchOffset;
+
+			yawOscLast = motion.curTransition.lastAnim.joints[7].yaw *
+						 Math.sin(filter.degToRad(motion.cumulativeTime *
+						 motion.curTransition.lastAnim.calibration.frequency +
+						 motion.curTransition.lastAnim.joints[7].yawPhase)) +
+						 motion.curTransition.lastAnim.joints[7].yawOffset;
+
+			rollOscLast = motion.curTransition.lastAnim.joints[7].roll *
+						  Math.sin(filter.degToRad(motion.cumulativeTime *
+						  motion.curTransition.lastAnim.calibration.frequency +
+						  motion.curTransition.lastAnim.joints[7].rollPhase)) +
+						  motion.curTransition.lastAnim.joints[7].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+	} else {
+
+		pitchOsc = motion.curAnim.joints[7].pitch *
+				   Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+				   motion.curAnim.joints[7].pitchPhase)) +
+				   motion.curAnim.joints[7].pitchOffset;
+
+		yawOsc = motion.curAnim.joints[7].yaw *
+				 Math.sin(filter.degToRad(cycle * adjFreq +
+				 motion.curAnim.joints[7].yawPhase)) +
+				 motion.curAnim.joints[7].yawOffset;
+
+		rollOsc = motion.curAnim.joints[7].roll *
+				  Math.sin(filter.degToRad(cycle * adjFreq +
+				  motion.curAnim.joints[7].rollPhase)) +
+				  motion.curAnim.joints[7].rollOffset;
+	}
+
+	// apply spine2 joint rotations
+	MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc));
+
+	if (!motion.armsFree) {
+
+		// shoulders
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOsc = motion.curAnim.joints[8].pitch *
+						   Math.sin(filter.degToRad(motion.cumulativeTime *
+						   motion.curAnim.calibration.frequency +
+						   motion.curAnim.joints[8].pitchPhase)) +
+						   motion.curAnim.joints[8].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[8].yaw *
+						 Math.sin(filter.degToRad(motion.cumulativeTime *
+						 motion.curAnim.calibration.frequency +
+						 motion.curAnim.joints[8].yawPhase));
+
+				rollOsc = motion.curAnim.joints[8].roll *
+						  Math.sin(filter.degToRad(motion.cumulativeTime *
+						  motion.curAnim.calibration.frequency +
+						  motion.curAnim.joints[8].rollPhase)) +
+						  motion.curAnim.joints[8].rollOffset;
+
+				yawOffset = motion.curAnim.joints[8].yawOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[8].pitch *
+							   Math.sin(filter.degToRad(motion.walkWheelPos +
+							   motion.curTransition.lastAnim.joints[8].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[8].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[8].yaw *
+							 Math.sin(filter.degToRad(motion.walkWheelPos +
+							 motion.curTransition.lastAnim.joints[8].yawPhase))
+
+				rollOscLast = motion.curTransition.lastAnim.joints[8].roll *
+							  Math.sin(filter.degToRad(motion.walkWheelPos +
+							  motion.curTransition.lastAnim.joints[8].rollPhase)) +
+							  motion.curTransition.lastAnim.joints[8].rollOffset;
+
+				yawOffsetLast = motion.curTransition.lastAnim.joints[8].yawOffset;
+
+			} else {
+
+				pitchOsc = motion.curAnim.joints[8].pitch *
+						   Math.sin(filter.degToRad((cycle * adjFreq) +
+						   motion.curAnim.joints[8].pitchPhase)) +
+						   motion.curAnim.joints[8].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[8].yaw *
+						 Math.sin(filter.degToRad((cycle * adjFreq) +
+						 motion.curAnim.joints[8].yawPhase));
+
+				rollOsc = motion.curAnim.joints[8].roll *
+						  Math.sin(filter.degToRad((cycle * adjFreq) +
+						  motion.curAnim.joints[8].rollPhase)) +
+						  motion.curAnim.joints[8].rollOffset;
+
+				yawOffset = motion.curAnim.joints[8].yawOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[8].pitch *
+							   Math.sin(filter.degToRad(motion.cumulativeTime *
+							   motion.curTransition.lastAnim.calibration.frequency +
+							   motion.curTransition.lastAnim.joints[8].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[8].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[8].yaw *
+							 Math.sin(filter.degToRad(motion.cumulativeTime *
+							 motion.curTransition.lastAnim.calibration.frequency +
+							 motion.curTransition.lastAnim.joints[8].yawPhase))
+
+				rollOscLast = motion.curTransition.lastAnim.joints[8].roll *
+							  Math.sin(filter.degToRad(motion.cumulativeTime *
+							  motion.curTransition.lastAnim.calibration.frequency +
+							  motion.curTransition.lastAnim.joints[8].rollPhase)) +
+							  motion.curTransition.lastAnim.joints[8].rollOffset;
+
+				yawOffsetLast = motion.curTransition.lastAnim.joints[8].yawOffset;
+			}
+
+			pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+			yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[8].pitch *
+					   Math.sin(filter.degToRad((cycle * adjFreq) +
+					   motion.curAnim.joints[8].pitchPhase)) +
+					   motion.curAnim.joints[8].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[8].yaw *
+					 Math.sin(filter.degToRad((cycle * adjFreq) +
+					 motion.curAnim.joints[8].yawPhase));
+
+			rollOsc = motion.curAnim.joints[8].roll *
+					  Math.sin(filter.degToRad((cycle * adjFreq) +
+					  motion.curAnim.joints[8].rollPhase)) +
+					  motion.curAnim.joints[8].rollOffset;
+
+			yawOffset = motion.curAnim.joints[8].yawOffset;
+		}
+
+		MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc + yawOffset, rollOsc));
+		MyAvatar.setJointData("LeftShoulder", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc - yawOffset, -rollOsc));
+
+		// upper arms
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOsc = motion.curAnim.joints[9].pitch *
+						   Math.sin(filter.degToRad((motion.cumulativeTime *
+						   motion.curAnim.calibration.frequency) +
+						   motion.curAnim.joints[9].pitchPhase));
+
+				yawOsc = motion.curAnim.joints[9].yaw *
+						 Math.sin(filter.degToRad((motion.cumulativeTime *
+						 motion.curAnim.calibration.frequency) +
+						 motion.curAnim.joints[9].yawPhase));
+
+				rollOsc = motion.curAnim.joints[9].roll *
+						  Math.sin(filter.degToRad((motion.cumulativeTime *
+						  motion.curAnim.calibration.frequency * 2) +
+						  motion.curAnim.joints[9].rollPhase)) +
+						  motion.curAnim.joints[9].rollOffset;
+
+				pitchOffset = motion.curAnim.joints[9].pitchOffset;
+				yawOffset = motion.curAnim.joints[9].yawOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[9].pitch *
+							   Math.sin(filter.degToRad(motion.walkWheelPos +
+							   motion.curTransition.lastAnim.joints[9].pitchPhase));
+
+				yawOscLast = motion.curTransition.lastAnim.joints[9].yaw *
+							 Math.sin(filter.degToRad(motion.walkWheelPos +
+							 motion.curTransition.lastAnim.joints[9].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[9].roll *
+							  Math.sin(filter.degToRad(motion.walkWheelPos +
+							  motion.curTransition.lastAnim.joints[9].rollPhase)) +
+							  motion.curTransition.lastAnim.joints[9].rollOffset;
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[9].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[9].yawOffset;
+
+			} else {
+
+				pitchOsc = motion.curAnim.joints[9].pitch *
+						   Math.sin(filter.degToRad((cycle * adjFreq) +
+						   motion.curAnim.joints[9].pitchPhase));
+
+				yawOsc = motion.curAnim.joints[9].yaw *
+						 Math.sin(filter.degToRad((cycle * adjFreq) +
+						 motion.curAnim.joints[9].yawPhase));
+
+				rollOsc = motion.curAnim.joints[9].roll *
+						  Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+						  motion.curAnim.joints[9].rollPhase)) +
+						  motion.curAnim.joints[9].rollOffset;
+
+				pitchOffset = motion.curAnim.joints[9].pitchOffset;
+				yawOffset = motion.curAnim.joints[9].yawOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[9].pitch *
+							   Math.sin(filter.degToRad(motion.cumulativeTime *
+							   motion.curTransition.lastAnim.calibration.frequency +
+							   motion.curTransition.lastAnim.joints[9].pitchPhase))
+
+				yawOscLast = motion.curTransition.lastAnim.joints[9].yaw *
+							 Math.sin(filter.degToRad(motion.cumulativeTime *
+							 motion.curTransition.lastAnim.calibration.frequency +
+							 motion.curTransition.lastAnim.joints[9].yawPhase))
+
+				rollOscLast = motion.curTransition.lastAnim.joints[9].roll *
+							  Math.sin(filter.degToRad(motion.cumulativeTime *
+							  motion.curTransition.lastAnim.calibration.frequency +
+							  motion.curTransition.lastAnim.joints[9].rollPhase)) +
+							  motion.curTransition.lastAnim.joints[9].rollOffset;
+
+				pitchOffsetLast = motion.curTransition.lastAnim.joints[9].pitchOffset;
+				yawOffsetLast = motion.curTransition.lastAnim.joints[9].yawOffset;
+			}
+
+			pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+			yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast);
+			yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[9].pitch *
+					   Math.sin(filter.degToRad((cycle * adjFreq) +
+					   motion.curAnim.joints[9].pitchPhase));
+
+			yawOsc = motion.curAnim.joints[9].yaw *
+					 Math.sin(filter.degToRad((cycle * adjFreq) +
+					 motion.curAnim.joints[9].yawPhase));
+
+			rollOsc = motion.curAnim.joints[9].roll *
+					  Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+					  motion.curAnim.joints[9].rollPhase)) +
+					  motion.curAnim.joints[9].rollOffset;
+
+			pitchOffset = motion.curAnim.joints[9].pitchOffset;
+			yawOffset = motion.curAnim.joints[9].yawOffset;
+
+		}
+
+		MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(
+											(-1 * flyingModifier) * pitchOsc + pitchOffset,
+											yawOsc - yawOffset,
+											rollOsc));
+
+		MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(
+											pitchOsc + pitchOffset,
+											yawOsc + yawOffset,
+											-rollOsc));
+
+		// forearms
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOsc = motion.curAnim.joints[10].pitch *
+						   Math.sin(filter.degToRad((motion.cumulativeTime *
+						   motion.curAnim.calibration.frequency) +
+						   motion.curAnim.joints[10].pitchPhase)) +
+						   motion.curAnim.joints[10].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[10].yaw *
+						 Math.sin(filter.degToRad((motion.cumulativeTime *
+						 motion.curAnim.calibration.frequency) +
+						 motion.curAnim.joints[10].yawPhase));
+
+				rollOsc = motion.curAnim.joints[10].roll *
+						  Math.sin(filter.degToRad((motion.cumulativeTime *
+						  motion.curAnim.calibration.frequency) +
+						  motion.curAnim.joints[10].rollPhase));
+
+				yawOffset = motion.curAnim.joints[10].yawOffset;
+				rollOffset = motion.curAnim.joints[10].rollOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[10].pitch *
+							   Math.sin(filter.degToRad((motion.walkWheelPos) +
+							   motion.curTransition.lastAnim.joints[10].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[10].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[10].yaw *
+							 Math.sin(filter.degToRad((motion.walkWheelPos) +
+							 motion.curTransition.lastAnim.joints[10].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[10].roll *
+							  Math.sin(filter.degToRad((motion.walkWheelPos) +
+							  motion.curTransition.lastAnim.joints[10].rollPhase));
+
+				yawOffsetLast = motion.curTransition.lastAnim.joints[10].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[10].rollOffset;
+
+			} else {
+
+				pitchOsc = motion.curAnim.joints[10].pitch *
+						   Math.sin(filter.degToRad((cycle * adjFreq) +
+						   motion.curAnim.joints[10].pitchPhase)) +
+						   motion.curAnim.joints[10].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[10].yaw *
+						 Math.sin(filter.degToRad((cycle * adjFreq) +
+						 motion.curAnim.joints[10].yawPhase));
+
+				rollOsc = motion.curAnim.joints[10].roll *
+						  Math.sin(filter.degToRad((cycle * adjFreq) +
+						  motion.curAnim.joints[10].rollPhase));
+
+				yawOffset = motion.curAnim.joints[10].yawOffset;
+				rollOffset = motion.curAnim.joints[10].rollOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[10].pitch *
+							   Math.sin(filter.degToRad((motion.cumulativeTime *
+							   motion.curTransition.lastAnim.calibration.frequency) +
+							   motion.curTransition.lastAnim.joints[10].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[10].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[10].yaw *
+							 Math.sin(filter.degToRad((motion.cumulativeTime *
+							 motion.curTransition.lastAnim.calibration.frequency) +
+							 motion.curTransition.lastAnim.joints[10].yawPhase));
+
+				rollOscLast = motion.curTransition.lastAnim.joints[10].roll *
+							  Math.sin(filter.degToRad((motion.cumulativeTime *
+							  motion.curTransition.lastAnim.calibration.frequency) +
+							  motion.curTransition.lastAnim.joints[10].rollPhase));
+
+				yawOffsetLast = motion.curTransition.lastAnim.joints[10].yawOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[10].rollOffset;
+			}
+
+			// blend the animations
+			pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+			yawOsc = -(transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast);
+			rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[10].pitch *
+					  Math.sin(filter.degToRad((cycle * adjFreq) +
+					  motion.curAnim.joints[10].pitchPhase)) +
+					  motion.curAnim.joints[10].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[10].yaw *
+					 Math.sin(filter.degToRad((cycle * adjFreq) +
+					 motion.curAnim.joints[10].yawPhase));
+
+			rollOsc = motion.curAnim.joints[10].roll *
+					  Math.sin(filter.degToRad((cycle * adjFreq) +
+					  motion.curAnim.joints[10].rollPhase));
+
+			yawOffset = motion.curAnim.joints[10].yawOffset;
+			rollOffset = motion.curAnim.joints[10].rollOffset;
+		}
+
+		// apply forearms rotations
+		MyAvatar.setJointData("RightForeArm",
+			Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc + yawOffset, rollOsc + rollOffset));
+		MyAvatar.setJointData("LeftForeArm",
+			Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc - yawOffset, rollOsc - rollOffset));
+
+		// hands
+		if (motion.curTransition !== nullTransition) {
+
+			if (motion.curTransition.walkingAtStart) {
+
+				pitchOsc = motion.curAnim.joints[11].pitch *
+						   Math.sin(filter.degToRad((motion.cumulativeTime *
+						   motion.curAnim.calibration.frequency) +
+						   motion.curAnim.joints[11].pitchPhase)) +
+						   motion.curAnim.joints[11].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[11].yaw *
+						 Math.sin(filter.degToRad((motion.cumulativeTime *
+						 motion.curAnim.calibration.frequency) +
+						 motion.curAnim.joints[11].yawPhase)) +
+						 motion.curAnim.joints[11].yawOffset;
+
+				rollOsc = motion.curAnim.joints[11].roll *
+						  Math.sin(filter.degToRad((motion.cumulativeTime *
+						  motion.curAnim.calibration.frequency) +
+						  motion.curAnim.joints[11].rollPhase));
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[11].pitch *
+							   Math.sin(filter.degToRad(motion.walkWheelPos +
+							   motion.curTransition.lastAnim.joints[11].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[11].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[11].yaw *
+							 Math.sin(filter.degToRad(motion.walkWheelPos +
+							 motion.curTransition.lastAnim.joints[11].yawPhase)) +
+							 motion.curTransition.lastAnim.joints[11].yawOffset;
+
+				rollOscLast = motion.curTransition.lastAnim.joints[11].roll *
+							  Math.sin(filter.degToRad(motion.walkWheelPos +
+							  motion.curTransition.lastAnim.joints[11].rollPhase))
+
+				rollOffset = motion.curAnim.joints[11].rollOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[11].rollOffset;
+
+			} else {
+
+				pitchOsc = motion.curAnim.joints[11].pitch *
+						   Math.sin(filter.degToRad((cycle * adjFreq) +
+						   motion.curAnim.joints[11].pitchPhase)) +
+						   motion.curAnim.joints[11].pitchOffset;
+
+				yawOsc = motion.curAnim.joints[11].yaw *
+						 Math.sin(filter.degToRad((cycle * adjFreq) +
+						 motion.curAnim.joints[11].yawPhase)) +
+						 motion.curAnim.joints[11].yawOffset;
+
+				rollOsc = motion.curAnim.joints[11].roll *
+						  Math.sin(filter.degToRad((cycle * adjFreq) +
+						  motion.curAnim.joints[11].rollPhase));
+
+				rollOffset = motion.curAnim.joints[11].rollOffset;
+
+				pitchOscLast = motion.curTransition.lastAnim.joints[11].pitch *
+							   Math.sin(filter.degToRad(motion.cumulativeTime *
+							   motion.curTransition.lastAnim.calibration.frequency +
+							   motion.curTransition.lastAnim.joints[11].pitchPhase)) +
+							   motion.curTransition.lastAnim.joints[11].pitchOffset;
+
+				yawOscLast = motion.curTransition.lastAnim.joints[11].yaw *
+							 Math.sin(filter.degToRad(motion.cumulativeTime *
+							 motion.curTransition.lastAnim.calibration.frequency +
+							 motion.curTransition.lastAnim.joints[11].yawPhase)) +
+							 motion.curTransition.lastAnim.joints[11].yawOffset;
+
+				rollOscLast = motion.curTransition.lastAnim.joints[11].roll *
+							  Math.sin(filter.degToRad(motion.cumulativeTime *
+							  motion.curTransition.lastAnim.calibration.frequency +
+							  motion.curTransition.lastAnim.joints[11].rollPhase))
+
+				rollOffset = motion.curAnim.joints[11].rollOffset;
+				rollOffsetLast = motion.curTransition.lastAnim.joints[11].rollOffset;
+			}
+
+			pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+			yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+			rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+			rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast);
+
+		} else {
+
+			pitchOsc = motion.curAnim.joints[11].pitch *
+					   Math.sin(filter.degToRad((cycle * adjFreq) +
+					   motion.curAnim.joints[11].pitchPhase)) +
+					   motion.curAnim.joints[11].pitchOffset;
+
+			yawOsc = motion.curAnim.joints[11].yaw *
+					 Math.sin(filter.degToRad((cycle * adjFreq) +
+					 motion.curAnim.joints[11].yawPhase)) +
+					 motion.curAnim.joints[11].yawOffset;
+
+			rollOsc = motion.curAnim.joints[11].roll *
+					  Math.sin(filter.degToRad((cycle * adjFreq) +
+					  motion.curAnim.joints[11].rollPhase));
+
+			rollOffset = motion.curAnim.joints[11].rollOffset;
+		}
+
+		// set the hand rotations
+		MyAvatar.setJointData("RightHand",
+			Quat.fromPitchYawRollDegrees(sideStepHandPitchSign * pitchOsc, yawOsc, rollOsc + rollOffset));
+		MyAvatar.setJointData("LeftHand",
+			Quat.fromPitchYawRollDegrees(pitchOsc, -yawOsc, rollOsc - rollOffset));
+
+	} // end if (!motion.armsFree)
+
+	// head and neck
+	if (motion.curTransition !== nullTransition) {
+
+		if (motion.curTransition.walkingAtStart) {
+
+			pitchOsc = 0.5 * motion.curAnim.joints[12].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency * 2) +
+				motion.curAnim.joints[12].pitchPhase)) +
+				motion.curAnim.joints[12].pitchOffset;
+
+			yawOsc = 0.5 * motion.curAnim.joints[12].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[12].yawPhase)) +
+				motion.curAnim.joints[12].yawOffset;
+
+			rollOsc = 0.5 * motion.curAnim.joints[12].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curAnim.calibration.frequency) +
+				motion.curAnim.joints[12].rollPhase)) +
+				motion.curAnim.joints[12].rollOffset;
+
+			pitchOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].pitch *
+				Math.sin(filter.degToRad((motion.walkWheelPos * 2) +
+				motion.curTransition.lastAnim.joints[12].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[12].pitchOffset;
+
+			yawOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].yaw *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[12].yawPhase)) +
+				motion.curTransition.lastAnim.joints[12].yawOffset;
+
+			rollOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].roll *
+				Math.sin(filter.degToRad((motion.walkWheelPos) +
+				motion.curTransition.lastAnim.joints[12].rollPhase)) +
+				motion.curTransition.lastAnim.joints[12].rollOffset;
+
+		} else {
+
+			pitchOsc = 0.5 * motion.curAnim.joints[12].pitch *
+				Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+				motion.curAnim.joints[12].pitchPhase)) +
+				motion.curAnim.joints[12].pitchOffset;
+
+			yawOsc = 0.5 * motion.curAnim.joints[12].yaw *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[12].yawPhase)) +
+				motion.curAnim.joints[12].yawOffset;
+
+			rollOsc = 0.5 * motion.curAnim.joints[12].roll *
+				Math.sin(filter.degToRad((cycle * adjFreq) +
+				motion.curAnim.joints[12].rollPhase)) +
+				motion.curAnim.joints[12].rollOffset;
+
+			pitchOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].pitch *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency * 2) +
+				motion.curTransition.lastAnim.joints[12].pitchPhase)) +
+				motion.curTransition.lastAnim.joints[12].pitchOffset;
+
+			yawOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].yaw *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[12].yawPhase)) +
+				motion.curTransition.lastAnim.joints[12].yawOffset;
+
+			rollOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].roll *
+				Math.sin(filter.degToRad((motion.cumulativeTime *
+				motion.curTransition.lastAnim.calibration.frequency) +
+				motion.curTransition.lastAnim.joints[12].rollPhase)) +
+				motion.curTransition.lastAnim.joints[12].rollOffset;
+		}
+
+		pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast);
+		yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast);
+		rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast);
+
+	} else {
+
+		pitchOsc = 0.5 * motion.curAnim.joints[12].pitch *
+			Math.sin(filter.degToRad((cycle * adjFreq * 2) +
+			motion.curAnim.joints[12].pitchPhase)) +
+			motion.curAnim.joints[12].pitchOffset;
+
+		yawOsc = 0.5 * motion.curAnim.joints[12].yaw *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[12].yawPhase)) +
+			motion.curAnim.joints[12].yawOffset;
+
+		rollOsc = 0.5 * motion.curAnim.joints[12].roll *
+			Math.sin(filter.degToRad((cycle * adjFreq) +
+			motion.curAnim.joints[12].rollPhase)) +
+			motion.curAnim.joints[12].rollOffset;
+	}
+
+	MyAvatar.setJointData("Head", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc));
+	MyAvatar.setJointData("Neck", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc));
 }
 
-// mouse event handlers
-var movingSliderOne = false;
-var movingSliderTwo = false;
-var movingSliderThree = false;
-var movingSliderFour = false;
-var movingSliderFive = false;
-var movingSliderSix = false;
-var movingSliderSeven = false;
-var movingSliderEight = false;
-var movingSliderNine = false;
-
-function mousePressEvent(event) {
-
-    var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
-
-    // check for a character joint control click
-    switch (clickedOverlay) {
-
-        case hideButton:
-            Overlays.editOverlay(hideButton, { visible: false } );
-            Overlays.editOverlay(hideButtonSelected, { visible: true } );
-            return;
-
-        case backButton:
-            Overlays.editOverlay(backButton, { visible: false } );
-            Overlays.editOverlay(backButtonSelected, { visible: true } );
-            return;
-
-        case controlsMinimisedTab:
-            // TODO: add visual user feedback for tab click
-            return;
-
-		case footstepsBigButton:
-			playFootStepSounds = true;
-			Overlays.editOverlay(footstepsBigButtonSelected, { visible: true } );
-			Overlays.editOverlay(footstepsBigButton, { visible: false } );
-			return;
-
-		case footstepsBigButtonSelected:
-			playFootStepSounds = false;
-			Overlays.editOverlay(footstepsBigButton, { visible: true } );
-			Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } );
-			return;
-
-		case femaleBigButton:
-		case maleBigButtonSelected:
-			avatarGender = FEMALE;
-			selectedWalk = femaleStrutWalk;
-			selectedStand = femaleStandOne;
-			selectedFlyUp  = femaleFlyingUp;
-			selectedFly = femaleFlying;
-			selectedFlyDown = femaleFlyingDown;
-			selectedSideStepLeft = femaleSideStepLeft;
-			selectedSideStepRight = femaleSideStepRight;
-			Overlays.editOverlay(femaleBigButtonSelected, { visible: true } );
-			Overlays.editOverlay(femaleBigButton, { visible: false } );
-			Overlays.editOverlay(maleBigButton, { visible: true } );
-			Overlays.editOverlay(maleBigButtonSelected, { visible: false } );
-			return;
-
-		case armsFreeBigButton:
-			armsFree = true;
-			Overlays.editOverlay(armsFreeBigButtonSelected, { visible: true } );
-			Overlays.editOverlay(armsFreeBigButton, { visible: false } );
-			return;
-
-		case armsFreeBigButtonSelected:
-			armsFree = false;
-			Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } );
-			Overlays.editOverlay(armsFreeBigButton, { visible: true } );
-			return;
-
-		case maleBigButton:
-		case femaleBigButtonSelected:
-			avatarGender = MALE;
-			selectedWalk = maleStrutWalk;
-			selectedStand = maleStandOne;
-			selectedFlyUp  = maleFlyingUp;
-			selectedFly = maleFlying;
-			selectedFlyDown = maleFlyingDown;
-			selectedSideStepLeft = maleSideStepLeft;
-			selectedSideStepRight = maleSideStepRight;
-			Overlays.editOverlay(femaleBigButton, { visible: true } );
-			Overlays.editOverlay(femaleBigButtonSelected, { visible: false } );
-			Overlays.editOverlay(maleBigButtonSelected, { visible: true } );
-			Overlays.editOverlay(maleBigButton, { visible: false } );
-			return;
-
-
-		case strutWalkBigButton:
-			if(avatarGender===FEMALE) selectedWalk = femaleStrutWalk;
-			else selectedWalk = maleStrutWalk;
-			currentAnimation = selectedWalk;
-			showWalkStyleButtons(true);
-			return;
-
-		case strutWalkBigButtonSelected:
-
-			// toggle forwards / backwards walk display
-			if(principleDirection===DIRECTION_FORWARDS) {
-				principleDirection=DIRECTION_BACKWARDS;
-			} else principleDirection=DIRECTION_FORWARDS;
-			return;
-
-        case sliderOne:
-            movingSliderOne = true;
-            return;
-
-        case sliderTwo:
-            movingSliderTwo = true;
-            return;
-
-        case sliderThree:
-            movingSliderThree = true;
-            return;
-
-        case sliderFour:
-            movingSliderFour = true;
-            return;
-
-        case sliderFive:
-            movingSliderFive = true;
-            return;
-
-        case sliderSix:
-            movingSliderSix = true;
-            return;
-
-        case sliderSeven:
-            movingSliderSeven = true;
-            return;
-
-        case sliderEight:
-            movingSliderEight = true;
-            return;
-
-        case sliderNine:
-            movingSliderNine = true;
-            return;
-    }
-
-    if( INTERNAL_STATE===CONFIG_WALK_JOINTS ||
-	    INTERNAL_STATE===CONFIG_STANDING ||
-	    INTERNAL_STATE===CONFIG_FLYING ||
-	    INTERNAL_STATE===CONFIG_FLYING_UP ||
-	    INTERNAL_STATE===CONFIG_FLYING_DOWN  ||
-	    INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-	    INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
-
-		// check for new joint selection and update display accordingly
-        var clickX = event.x - backgroundX - 75;
-        var clickY = event.y - backgroundY - 92;
-
-        if(clickX>60&&clickX<120&&clickY>123&&clickY<155) {
-            initialiseJointsEditingPanel(0);
-            return;
-        }
-        else if(clickX>63&&clickX<132&&clickY>156&&clickY<202) {
-            initialiseJointsEditingPanel(1);
-            return;
-        }
-        else if(clickX>58&&clickX<137&&clickY>203&&clickY<250) {
-            initialiseJointsEditingPanel(2);
-            return;
-        }
-        else if(clickX>58&&clickX<137&&clickY>250&&clickY<265) {
-            initialiseJointsEditingPanel(3);
-            return;
-        }
-        else if(clickX>58&&clickX<137&&clickY>265&&clickY<280) {
-            initialiseJointsEditingPanel(4);
-            return;
-        }
-        else if(clickX>78&&clickX<121&&clickY>111&&clickY<128) {
-            initialiseJointsEditingPanel(5);
-            return;
-        }
-        else if(clickX>78&&clickX<128&&clickY>89&&clickY<111) {
-            initialiseJointsEditingPanel(6);
-            return;
-        }
-        else if(clickX>85&&clickX<118&&clickY>77&&clickY<94) {
-            initialiseJointsEditingPanel(7);
-            return;
-        }
-        else if(clickX>64&&clickX<125&&clickY>55&&clickY<77) {
-            initialiseJointsEditingPanel(8);
-            return;
-        }
-        else if((clickX>44&&clickX<73&&clickY>71&&clickY<94)
-              ||(clickX>125&&clickX<144&&clickY>71&&clickY<94)) {
-            initialiseJointsEditingPanel(9);
-            return;
-        }
-        else if((clickX>28&&clickX<57&&clickY>94&&clickY<119)
-              ||(clickX>137&&clickX<170&&clickY>97&&clickY<114)) {
-            initialiseJointsEditingPanel(10);
-            return;
-        }
-        else if((clickX>18&&clickX<37&&clickY>115&&clickY<136)
-              ||(clickX>157&&clickX<182&&clickY>115&&clickY<136)) {
-            initialiseJointsEditingPanel(11);
-            return;
-        }
-        else if(clickX>81&&clickX<116&&clickY>12&&clickY<53) {
-            initialiseJointsEditingPanel(12);
-            return;
-        }
-    }
-}
-function mouseMoveEvent(event) {
-
-    // only need deal with slider changes
-    if(powerOn) {
-
-        if( INTERNAL_STATE===CONFIG_WALK_JOINTS ||
-            INTERNAL_STATE===CONFIG_STANDING ||
-            INTERNAL_STATE===CONFIG_FLYING ||
-            INTERNAL_STATE===CONFIG_FLYING_UP ||
-            INTERNAL_STATE===CONFIG_FLYING_DOWN ||
-            INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
-            INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
-
-            var thumbClickOffsetX = event.x - minSliderX;
-            var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
-            if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
-            if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
-            var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
-
-            if(movingSliderOne) { // currently selected joint pitch
-                Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
-                currentAnimation.joints[selectedJointIndex].pitch = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].pitchRange;
-            }
-            else if(movingSliderTwo) { // currently selected joint yaw
-                Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
-                currentAnimation.joints[selectedJointIndex].yaw = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].yawRange;
-            }
-            else if(movingSliderThree) { // currently selected joint roll
-                Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
-                currentAnimation.joints[selectedJointIndex].roll = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].rollRange;
-            }
-            else if(movingSliderFour) { // currently selected joint pitch phase
-                Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
-                var newPhase = 360 * thumbPositionNormalised - 180;
-                currentAnimation.joints[selectedJointIndex].pitchPhase = newPhase;
-            }
-            else if(movingSliderFive) { // currently selected joint yaw phase;
-                Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
-                var newPhase = 360 * thumbPositionNormalised - 180;
-                currentAnimation.joints[selectedJointIndex].yawPhase = newPhase;
-            }
-            else if(movingSliderSix) { // currently selected joint roll phase
-                Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
-                var newPhase = 360 * thumbPositionNormalised - 180;
-                currentAnimation.joints[selectedJointIndex].rollPhase = newPhase;
-            }
-            else if(movingSliderSeven) { // currently selected joint pitch offset
-                Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} ); // currently selected joint pitch offset
-                var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].pitchOffsetRange;
-                currentAnimation.joints[selectedJointIndex].pitchOffset = newOffset;
-            }
-            else if(movingSliderEight) { // currently selected joint yaw offset
-                Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} ); // currently selected joint yaw offset
-                var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].yawOffsetRange;
-                currentAnimation.joints[selectedJointIndex].yawOffset = newOffset;
-            }
-            else if(movingSliderNine) { // currently selected joint roll offset
-                Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} ); // currently selected joint roll offset
-                var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].rollOffsetRange;
-                currentAnimation.joints[selectedJointIndex].rollOffset = newOffset;
-            }
-        }
-        else if(INTERNAL_STATE===CONFIG_WALK_TWEAKS) {
-
-            var thumbClickOffsetX = event.x - minSliderX;
-            var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
-            if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
-            if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
-            var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
-
-            if(movingSliderOne) { // walk speed
-                paused = true; // avoid nasty jittering
-                Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
-                currentAnimation.settings.baseFrequency = thumbPositionNormalised * MAX_WALK_SPEED;
-            }
-            else if(movingSliderTwo) {  // take flight speed
-                Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
-                //currentAnimation.settings.takeFlightVelocity = thumbPositionNormalised * 300;
-            }
-            else if(movingSliderThree) { // hips sway
-                Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
-                currentAnimation.joints[0].sway = thumbPositionNormalised * sliderRanges.joints[0].swayRange;
-            }
-            else if(movingSliderFour) { // hips bob
-                Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
-                currentAnimation.joints[0].bob = thumbPositionNormalised * sliderRanges.joints[0].bobRange;
-            }
-            else if(movingSliderFive) { // hips thrust
-				Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
-                currentAnimation.joints[0].thrust = thumbPositionNormalised * sliderRanges.joints[0].thrustRange;
-            }
-            else if(movingSliderSix) { // legs separation
-                Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
-                currentAnimation.joints[1].rollOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[1].rollOffsetRange;
-            }
-            else if(movingSliderSeven) { // stride
-                Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} );
-                currentAnimation.joints[1].pitch = thumbPositionNormalised * sliderRanges.joints[1].pitchRange;
-            }
-            else if(movingSliderEight) { // arms swing = upper arms yaw
-                Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} );
-                currentAnimation.joints[9].yaw = thumbPositionNormalised * sliderRanges.joints[9].yawRange;
-            }
-            else if(movingSliderNine) { // arms out = upper arms pitch offset
-                Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} );
-                currentAnimation.joints[9].pitchOffset = (thumbPositionNormalised-0.5) * -2 * sliderRanges.joints[9].pitchOffsetRange;
-            }
-        }
-    }
-}
-function mouseReleaseEvent(event) {
-
-    var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
-
-    if(paused) paused = false;
-
-    if(clickedOverlay === offButton) {
-        powerOn = true;
-        Overlays.editOverlay(offButton, { visible: false } );
-        Overlays.editOverlay(onButton,  { visible: true } );
-        stand();
-    }
-    else if(clickedOverlay === hideButton || clickedOverlay === hideButtonSelected){
-        Overlays.editOverlay(hideButton, { visible: true } );
-        Overlays.editOverlay(hideButtonSelected, { visible: false } );
-        minimised = true;
-        minimiseDialog();
-    }
-    else if(clickedOverlay === controlsMinimisedTab) {
-        minimised = false;
-        minimiseDialog();
-    }
-    else if(powerOn) {
-
-        if(movingSliderOne) movingSliderOne = false;
-        else if(movingSliderTwo) movingSliderTwo = false;
-        else if(movingSliderThree) movingSliderThree = false;
-        else if(movingSliderFour) movingSliderFour = false;
-        else if(movingSliderFive) movingSliderFive = false;
-        else if(movingSliderSix) movingSliderSix = false;
-        else if(movingSliderSeven) movingSliderSeven = false;
-        else if(movingSliderEight) movingSliderEight = false;
-        else if(movingSliderNine) movingSliderNine = false;
-        else {
-
-            switch(clickedOverlay) {
-
-                case configWalkButtonSelected:
-                case configStandButtonSelected:
-                case configSideStepLeftButtonSelected:
-                case configSideStepRightButtonSelected:
-                case configFlyingButtonSelected:
-                case configFlyingUpButtonSelected:
-                case configFlyingDownButtonSelected:
-                case configWalkStylesButtonSelected:
-                case configWalkTweaksButtonSelected:
-                case configWalkJointsButtonSelected:
-                    setInternalState(STANDING);
-                    break;
-
-                case onButton:
-                    powerOn = false;
-                    setInternalState(STANDING);
-                    Overlays.editOverlay(offButton, { visible: true } );
-                    Overlays.editOverlay(onButton,  { visible: false } );
-                    resetJoints();
-                    break;
-
-                case backButton:
-                case backButtonSelected:
-                    Overlays.editOverlay(backButton, { visible: false } );
-                    Overlays.editOverlay(backButtonSelected, { visible: false } );
-                    setInternalState(STANDING);
-                    break;
-
-                case configWalkStylesButton:
-                    setInternalState(CONFIG_WALK_STYLES);
-                    break;
-
-                case configWalkTweaksButton:
-                    setInternalState(CONFIG_WALK_TWEAKS);
-                    break;
-
-                case configWalkJointsButton:
-                    setInternalState(CONFIG_WALK_JOINTS);
-                    break;
-
-                case configWalkButton:
-                    setInternalState(CONFIG_WALK_STYLES); //  set the default walk adjustment panel here (i.e. first panel shown when Walk button clicked)
-                    break;
-
-                case configStandButton:
-                    setInternalState(CONFIG_STANDING);
-                    break;
-
-                case configSideStepLeftButton:
-                    setInternalState(CONFIG_SIDESTEP_LEFT);
-                    break;
-
-                case configSideStepRightButton:
-                    setInternalState(CONFIG_SIDESTEP_RIGHT);
-                    break;
-
-                case configFlyingButton:
-                    setInternalState(CONFIG_FLYING);
-                    break;
-
-                case configFlyingUpButton:
-                    setInternalState(CONFIG_FLYING_UP);
-                    break;
-
-                case configFlyingDownButton:
-                    setInternalState(CONFIG_FLYING_DOWN);
-                    break;
-            }
-        }
-    }
-}
-Controller.mouseMoveEvent.connect(mouseMoveEvent);
-Controller.mousePressEvent.connect(mousePressEvent);
-Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
-
-// Script ending
-Script.scriptEnding.connect(function() {
-
-    // remove the background overlays
-    for(var i in backgroundOverlays) {
-        Overlays.deleteOverlay(backgroundOverlays[i]);
-    }
-    // remove the button overlays
-    for(var i in buttonOverlays) {
-        Overlays.deleteOverlay(buttonOverlays[i]);
-    }
-    // remove the slider thumb overlays
-    for(var i in sliderThumbOverlays) {
-        Overlays.deleteOverlay(sliderThumbOverlays[i]);
-    }
-    // remove the character joint control overlays
-    for(var i in bigButtonOverlays) {
-        Overlays.deleteOverlay(jointsControlOverlays[i]);
-    }
-    // remove the big button overlays
-    for(var i in bigButtonOverlays) {
-        Overlays.deleteOverlay(bigButtonOverlays[i]);
-    }
-    // remove the mimimised tab
-    Overlays.deleteOverlay(controlsMinimisedTab);
-
-    // remove the walk wheel overlays
-    Overlays.deleteOverlay(walkWheelYLine);
-    Overlays.deleteOverlay(walkWheelZLine);
-    Overlays.deleteOverlay(walkWheelStats);
-
-    // remove the debug stats overlays
-    Overlays.deleteOverlay(debugStats);
-    Overlays.deleteOverlay(debugStatsPeriodic);
-});
-
-var sideStep = 0.002; // i.e. 2mm increments whilst sidestepping - JS movement keys don't work well :-(
-function keyPressEvent(event) {
-
-	if (event.text == "q") {
-		// export currentAnimation as json string when q key is pressed. reformat result at http://www.freeformatter.com/json-formatter.html
-        print('\n');
-        print('walk.js dumping animation: '+currentAnimation.name+'\n');
-        print('\n');
-        print(JSON.stringify(currentAnimation), null, '\t');
-    }
-    else if (event.text == "t") {
-        statsOn = !statsOn;
-		Overlays.editOverlay(debugStats,         {visible: statsOn});
-		Overlays.editOverlay(debugStatsPeriodic, {visible: statsOn});
-		Overlays.editOverlay(walkWheelStats,     {visible: statsOn});
-		Overlays.editOverlay(walkWheelYLine,     {visible: statsOn});
-		Overlays.editOverlay(walkWheelZLine,     {visible: statsOn});
-    }
-}
-Controller.keyPressEvent.connect(keyPressEvent);
-
-// get the list of joint names
-var jointList = MyAvatar.getJointNames();
-
-// clear the joint data so can calculate hips to feet distance
-for(var i = 0 ; i < 5 ; i++) {
-    MyAvatar.setJointData(i, Quat.fromPitchYawRollDegrees(0,0,0));
-}
-// used to position the visual representation of the walkwheel only
-var hipsToFeetDistance = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightFoot").y;
-
-// This script is designed around the Finite State Machine (FSM) model, so to start things up we just select the STANDING state.
-setInternalState(STANDING);
\ No newline at end of file
+// Begin by setting an internal state
+state.setInternalState(state.STANDING);
\ No newline at end of file
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 623631d5e5..53454d3d3b 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -49,7 +49,7 @@ const float PITCH_SPEED = 100.0f; // degrees/sec
 const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions
 const float COLLISION_RADIUS_SCALE = 0.125f;
 
-const float MAX_WALKING_SPEED = 4.5f;
+const float MAX_WALKING_SPEED = 2.5f; // human walking speed
 const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed
 const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this
 

From 4460e23b680a610df28bd58602f993d6503d1cd5 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Wed, 5 Nov 2014 23:08:00 +0100
Subject: [PATCH 02/37] Suppress repeated Hash mismatch debug messages

---
 libraries/networking/src/LimitedNodeList.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 043f0621bb..0c18c82962 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -209,8 +209,15 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
             if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
                 return true;
             } else {
-                qDebug() << "Packet hash mismatch on" << checkType << "- Sender"
+                static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
+                
+                QUuid senderUUID = uuidFromPacketHeader(packet);
+                if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {
+                    qDebug() << "Packet hash mismatch on" << checkType << "- Sender"
                     << uuidFromPacketHeader(packet);
+                    
+                    hashDebugSuppressMap.insert(senderUUID, checkType);
+                }
             }
         } else {
             static QString repeatedMessage

From ff197a2f64bfc2da98d9f98523293cc89bf086f5 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 6 Nov 2014 12:46:37 +0100
Subject: [PATCH 03/37] Base implementation for own reverb

---
 interface/src/Audio.cpp | 140 +++++++++++++++++++++++++---------------
 interface/src/Audio.h   |   3 +
 2 files changed, 91 insertions(+), 52 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 7d039387bb..2b38782231 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -510,6 +510,33 @@ void Audio::initGverb() {
     gverb_set_taillevel(_gverb, DB_CO(_reverbOptions->getTailLevel()));
 }
 
+void Audio::updateGverbOptions() {
+    bool reverbChanged = false;
+    if (_receivedAudioStream.hasReverb()) {
+        
+        if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) {
+            _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
+            reverbChanged = true;
+        }
+        if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
+            _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
+            reverbChanged = true;
+        }
+        
+        if (_reverbOptions != &_zoneReverbOptions) {
+            _reverbOptions = &_zoneReverbOptions;
+            reverbChanged = true;
+        }
+    } else if (_reverbOptions != &_scriptReverbOptions) {
+        _reverbOptions = &_scriptReverbOptions;
+        reverbChanged = true;
+    }
+    
+    if (reverbChanged) {
+        initGverb();
+    }
+}
+
 void Audio::setReverbOptions(const AudioEffectOptions* options) {
     // Save the new options
     _scriptReverbOptions.setMaxRoomSize(options->getMaxRoomSize());
@@ -557,6 +584,62 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF
     }
 }
 
+void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
+    bool hasEcho = Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio);
+    bool hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) &&
+                          !Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio);
+    if (_muted || !_audioOutput || (!hasEcho && !hasLocalReverb)) {
+        //qDebug() << "No Echo/Reverb:" << hasEcho << _reverb << _receivedAudioStream.hasReverb();
+        return;
+    }
+    
+    // if this person wants local loopback add that to the locally injected audio
+    // if there is reverb apply it to local audio and substract the origin samples
+    
+    if (!_loopbackOutputDevice && _loopbackAudioOutput) {
+        // we didn't have the loopback output device going so set that up now
+        _loopbackOutputDevice = _loopbackAudioOutput->start();
+    }
+    
+    QByteArray loopBackByteArray(inputByteArray);
+    if (_inputFormat != _outputFormat) {
+        float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) *
+                                           (_outputFormat.channelCount() / _inputFormat.channelCount());
+        loopBackByteArray.resize(inputByteArray.size() * loopbackOutputToInputRatio);
+        loopBackByteArray.fill(0);
+        linearResampling((int16_t*)inputByteArray.data(), (int16_t*)loopBackByteArray.data(),
+                         inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t),
+                         _inputFormat, _outputFormat);
+    }
+    
+    if (hasLocalReverb) {
+        //qDebug() << "Has Reverb";
+        QByteArray loopbackCopy;
+        if (!hasEcho) {
+            loopbackCopy = loopBackByteArray;
+        }
+        
+        int16_t* loopbackSamples = (int16_t*) loopBackByteArray.data();
+        int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
+        updateGverbOptions();
+        addReverb(loopbackSamples, numLoopbackSamples, _outputFormat);
+        
+        if (!hasEcho) {
+            int16_t* loopbackCopySamples = (int16_t*) loopbackCopy.data();
+            
+            for (int i = 0; i < numLoopbackSamples; ++i) {
+                loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i],
+                                                -32768, 32767);
+            }
+        }
+    }
+    
+    if (_loopbackOutputDevice) {
+        //qDebug() << "Writing";
+        _loopbackOutputDevice->write(loopBackByteArray);
+    }
+}
+
 void Audio::handleAudioInput() {
     static char audioDataPacket[MAX_PACKET_SIZE];
 
@@ -601,34 +684,8 @@ void Audio::handleAudioInput() {
 
         _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
     }
-        
-    if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
-        // if this person wants local loopback add that to the locally injected audio
-
-        if (!_loopbackOutputDevice && _loopbackAudioOutput) {
-            // we didn't have the loopback output device going so set that up now
-            _loopbackOutputDevice = _loopbackAudioOutput->start();
-        }
-        
-        if (_inputFormat == _outputFormat) {
-            if (_loopbackOutputDevice) {
-                _loopbackOutputDevice->write(inputByteArray);
-            }
-        } else {
-            float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate())
-                * (_outputFormat.channelCount() / _inputFormat.channelCount());
-
-            QByteArray loopBackByteArray(inputByteArray.size() * loopbackOutputToInputRatio, 0);
-
-            linearResampling((int16_t*) inputByteArray.data(), (int16_t*) loopBackByteArray.data(),
-                             inputByteArray.size() / sizeof(int16_t),
-                             loopBackByteArray.size() / sizeof(int16_t), _inputFormat, _outputFormat);
-
-            if (_loopbackOutputDevice) {
-                _loopbackOutputDevice->write(loopBackByteArray);
-            }
-        }
-    }
+    
+    handleLocalEchoAndReverb(inputByteArray);
 
     _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size());
     
@@ -951,34 +1008,13 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou
         _desiredOutputFormat, _outputFormat);
     
     if(_reverb || _receivedAudioStream.hasReverb()) {
-        bool reverbChanged = false;
-        if (_receivedAudioStream.hasReverb()) {
-            
-            if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) {
-                _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
-                reverbChanged = true;
-            }
-            if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
-                _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
-                reverbChanged = true;
-            }
-            
-            if (_reverbOptions != &_zoneReverbOptions) {
-                _reverbOptions = &_zoneReverbOptions;
-                reverbChanged = true;
-            }
-        } else if (_reverbOptions != &_scriptReverbOptions) {
-            _reverbOptions = &_scriptReverbOptions;
-            reverbChanged = true;
-        }
-        
-        if (reverbChanged) {
-            initGverb();
-        }
+        updateGverbOptions();
         addReverb((int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat);
     }
 }
 
+
+
 void Audio::addReceivedAudioToStream(const QByteArray& audioByteArray) {
     if (_audioOutput) {
         // Audio output must exist and be correctly set up if we're going to process received audio
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index fcbfb12761..13c7930478 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -267,8 +267,11 @@ private:
 
     // Adds Reverb
     void initGverb();
+    void updateGverbOptions();
     void addReverb(int16_t* samples, int numSamples, QAudioFormat& format);
 
+    void handleLocalEchoAndReverb(QByteArray inputByteArray);
+    
     // Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
     void addProceduralSounds(int16_t* monoInput, int numSamples);
 

From efc86b9f75128dc26642689ba2d55b48c526de61 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 6 Nov 2014 17:42:27 +0100
Subject: [PATCH 04/37] Remove debug

---
 interface/src/Audio.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 2b38782231..7a1bfbb1c7 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -589,7 +589,6 @@ void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
     bool hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) &&
                           !Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio);
     if (_muted || !_audioOutput || (!hasEcho && !hasLocalReverb)) {
-        //qDebug() << "No Echo/Reverb:" << hasEcho << _reverb << _receivedAudioStream.hasReverb();
         return;
     }
     
@@ -613,7 +612,6 @@ void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
     }
     
     if (hasLocalReverb) {
-        //qDebug() << "Has Reverb";
         QByteArray loopbackCopy;
         if (!hasEcho) {
             loopbackCopy = loopBackByteArray;
@@ -626,7 +624,6 @@ void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
         
         if (!hasEcho) {
             int16_t* loopbackCopySamples = (int16_t*) loopbackCopy.data();
-            
             for (int i = 0; i < numLoopbackSamples; ++i) {
                 loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i],
                                                 -32768, 32767);
@@ -635,7 +632,6 @@ void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
     }
     
     if (_loopbackOutputDevice) {
-        //qDebug() << "Writing";
         _loopbackOutputDevice->write(loopBackByteArray);
     }
 }

From 44cb35778afa05c3a1ac6d27c6182ff95753aaf8 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 6 Nov 2014 17:42:57 +0100
Subject: [PATCH 05/37] Move AudioEnv packet send to own function

	Audio environment packet moved to own function and out of the
if/else
	So it is now sent all the time, now matter if there are other
people around you
---
 assignment-client/src/audio/AudioMixer.cpp | 113 +++++++++++----------
 assignment-client/src/audio/AudioMixer.h   |   3 +
 2 files changed, 63 insertions(+), 53 deletions(-)

diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 6ca93a7b11..e217061826 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -463,6 +463,63 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
     return streamsMixed;
 }
 
+void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
+    static char clientEnvBuffer[MAX_PACKET_SIZE];
+    
+    // Send stream properties
+    bool hasReverb = false;
+    float reverbTime, wetLevel;
+    // find reverb properties
+    for (int i = 0; i < _zoneReverbSettings.size(); ++i) {
+        AudioMixerClientData* data = static_cast<AudioMixerClientData*>(node->getLinkedData());
+        glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition();
+        if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) {
+            hasReverb = true;
+            reverbTime = _zoneReverbSettings[i].reverbTime;
+            wetLevel = _zoneReverbSettings[i].wetLevel;
+            break;
+        }
+    }
+    AudioMixerClientData* nodeData = static_cast<AudioMixerClientData*>(node->getLinkedData());
+    AvatarAudioStream* stream = nodeData->getAvatarAudioStream();
+    bool dataChanged = (stream->hasReverb() != hasReverb) ||
+    (stream->hasReverb() && (stream->getRevebTime() != reverbTime ||
+                             stream->getWetLevel() != wetLevel));
+    if (dataChanged) {
+        // Update stream
+        if (hasReverb) {
+            stream->setReverb(reverbTime, wetLevel);
+        } else {
+            stream->clearReverb();
+        }
+    }
+    
+    // Send at change or every so often
+    float CHANCE_OF_SEND = 0.01f;
+    bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND);
+    
+    if (sendData) {
+        int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
+        char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader;
+        
+        unsigned char bitset = 0;
+        if (hasReverb) {
+            setAtBit(bitset, HAS_REVERB_BIT);
+        }
+        
+        memcpy(envDataAt, &bitset, sizeof(unsigned char));
+        envDataAt += sizeof(unsigned char);
+        
+        if (hasReverb) {
+            memcpy(envDataAt, &reverbTime, sizeof(float));
+            envDataAt += sizeof(float);
+            memcpy(envDataAt, &wetLevel, sizeof(float));
+            envDataAt += sizeof(float);
+        }
+        NodeList::getInstance()->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
+    }
+}
+
 void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
     NodeList* nodeList = NodeList::getInstance();
     
@@ -640,7 +697,6 @@ void AudioMixer::run() {
     timer.start();
 
     char clientMixBuffer[MAX_PACKET_SIZE];
-    char clientEnvBuffer[MAX_PACKET_SIZE];
     
     int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
     
@@ -734,58 +790,6 @@ void AudioMixer::run() {
                         // pack mixed audio samples
                         memcpy(mixDataAt, _mixSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO);
                         mixDataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO;
-
-                        // Send stream properties
-                        bool hasReverb = false;
-                        float reverbTime, wetLevel;
-                        // find reverb properties
-                        for (int i = 0; i < _zoneReverbSettings.size(); ++i) {
-                            AudioMixerClientData* data = static_cast<AudioMixerClientData*>(node->getLinkedData());
-                            glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition();
-                            if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) {
-                                hasReverb = true;
-                                reverbTime = _zoneReverbSettings[i].reverbTime;
-                                wetLevel = _zoneReverbSettings[i].wetLevel;
-                                break;
-                            }
-                        }
-                        AvatarAudioStream* stream = nodeData->getAvatarAudioStream();
-                        bool dataChanged = (stream->hasReverb() != hasReverb) ||
-                                            (stream->hasReverb() && (stream->getRevebTime() != reverbTime ||
-                                                                     stream->getWetLevel() != wetLevel));
-                        if (dataChanged) {                            
-                            // Update stream
-                            if (hasReverb) {
-                                stream->setReverb(reverbTime, wetLevel);
-                            } else {
-                                stream->clearReverb();
-                            }
-                        }
-                        
-                        // Send at change or every so often
-                        float CHANCE_OF_SEND = 0.01f;
-                        bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND);
-                        
-                        if (sendData) {
-                            int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
-                            char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader;
-                            
-                            unsigned char bitset = 0;
-                            if (hasReverb) {
-                                setAtBit(bitset, HAS_REVERB_BIT);
-                            }
-                            
-                            memcpy(envDataAt, &bitset, sizeof(unsigned char));
-                            envDataAt += sizeof(unsigned char);
-                            
-                            if (hasReverb) {
-                                memcpy(envDataAt, &reverbTime, sizeof(float));
-                                envDataAt += sizeof(float);
-                                memcpy(envDataAt, &wetLevel, sizeof(float));
-                                envDataAt += sizeof(float);
-                            }
-                            nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
-                        }
                     } else {
                         // pack header
                         int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame);
@@ -801,6 +805,9 @@ void AudioMixer::run() {
                         memcpy(mixDataAt, &numSilentSamples, sizeof(quint16));
                         mixDataAt += sizeof(quint16);
                     }
+                    
+                    // Send audio environment
+                    sendAudioEnvironmentPacket(node);
 
                     // send mixed audio packet
                     nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node);
diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h
index ff976dec61..b4a9e2aa1c 100644
--- a/assignment-client/src/audio/AudioMixer.h
+++ b/assignment-client/src/audio/AudioMixer.h
@@ -49,6 +49,9 @@ private:
     
     /// prepares and sends a mix to one Node
     int prepareMixForListeningNode(Node* node);
+    
+    /// Send Audio Environment packet for a single node
+    void sendAudioEnvironmentPacket(SharedNodePointer node);
 
     // used on a per stream basis to run the filter on before mixing, large enough to handle the historical
     // data from a phase delay as well as an entire network buffer

From 3e109ee770c19fa4151f55aa6093adcd804360b0 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 6 Nov 2014 14:57:04 -0800
Subject: [PATCH 06/37] Add WebWindow widget for scripts

---
 interface/src/Application.cpp         |  3 ++
 interface/src/scripting/WebWindow.cpp | 68 +++++++++++++++++++++++++++
 interface/src/scripting/WebWindow.h   | 53 +++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 interface/src/scripting/WebWindow.cpp
 create mode 100644 interface/src/scripting/WebWindow.h

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index b773231b0f..98be839e25 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -91,6 +91,7 @@
 #include "scripting/MenuScriptingInterface.h"
 #include "scripting/SettingsScriptingInterface.h"
 #include "scripting/WindowScriptingInterface.h"
+#include "scripting/WebWindow.h"
 
 #include "ui/DataWebDialog.h"
 #include "ui/InfoView.h"
@@ -3871,6 +3872,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
     // register `location` on the global object.
     scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
                                        LocationScriptingInterface::locationSetter);
+
+    scriptEngine->registerFunction("WebWindow", WebWindow::constructor, 1);
     
     scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
     scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
diff --git a/interface/src/scripting/WebWindow.cpp b/interface/src/scripting/WebWindow.cpp
new file mode 100644
index 0000000000..dfd3104f4b
--- /dev/null
+++ b/interface/src/scripting/WebWindow.cpp
@@ -0,0 +1,68 @@
+//
+//  WebWindow.cpp
+//  interface/src/scripting
+//
+//  Created by Ryan Huffman on 11/06/14.
+//  Copyright 2014 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include <QVBoxLayout>
+#include <QApplication>
+#include <QWebFrame>
+#include <QWebView>
+
+#include "WindowScriptingInterface.h"
+#include "WebWindow.h"
+
+ScriptEventBridge::ScriptEventBridge(QObject* parent) : QObject(parent) {
+}
+
+void ScriptEventBridge::emitWebEvent(const QString& data) {
+    emit webEventReceived(data);
+}
+
+void ScriptEventBridge::emitScriptEvent(const QString& data) {
+    emit scriptEventReceived(data);
+}
+
+WebWindow::WebWindow(const QString& url, int width, int height)
+    : QObject(NULL),
+      _window(new QWidget(NULL, Qt::Tool)),
+      _eventBridge(new ScriptEventBridge(this)) {
+
+    QWebView* webView = new QWebView(_window);
+    webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
+    webView->setUrl(url);
+    QVBoxLayout* layout = new QVBoxLayout(_window);
+    _window->setLayout(layout);
+    layout->addWidget(webView);
+    layout->setSpacing(0);
+    layout->setContentsMargins(0, 0, 0, 0);
+    _window->setGeometry(0, 0, width, height);
+
+    connect(this, &WebWindow::destroyed, _window, &QWidget::deleteLater);
+}
+
+WebWindow::~WebWindow() {
+}
+
+void WebWindow::setVisible(bool visible) {
+    QMetaObject::invokeMethod(_window, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible));
+}
+
+QScriptValue WebWindow::constructor(QScriptContext* context, QScriptEngine* engine) {
+    WebWindow* retVal;
+    QString file = context->argument(0).toString();
+    QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection,
+            Q_RETURN_ARG(WebWindow*, retVal),
+            Q_ARG(const QString&, file),
+            Q_ARG(int, context->argument(1).toInteger()),
+            Q_ARG(int, context->argument(2).toInteger()));
+
+    connect(engine, &QScriptEngine::destroyed, retVal, &WebWindow::deleteLater);
+
+    return engine->newQObject(retVal);//, QScriptEngine::ScriptOwnership);
+}
diff --git a/interface/src/scripting/WebWindow.h b/interface/src/scripting/WebWindow.h
new file mode 100644
index 0000000000..698e43fd6c
--- /dev/null
+++ b/interface/src/scripting/WebWindow.h
@@ -0,0 +1,53 @@
+//
+//  WebWindow.h
+//  interface/src/scripting
+//
+//  Created by Ryan Huffman on 11/06/14.
+//  Copyright 2014 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_WebWindow_h
+#define hifi_WebWindow_h
+
+#include <QScriptContext>
+#include <QScriptEngine>
+
+// #include "ScriptWebView.h"
+
+class ScriptEventBridge : public QObject {
+    Q_OBJECT
+public:
+    ScriptEventBridge(QObject* parent = NULL);
+
+public slots:
+    void emitWebEvent(const QString& data);
+    void emitScriptEvent(const QString& data);
+
+signals:
+    void webEventReceived(const QString& data);
+    void scriptEventReceived(const QString& data);
+
+};
+
+class WebWindow : public QObject {
+    Q_OBJECT
+    Q_PROPERTY(QObject* eventBridge READ getEventBridge)
+public:
+    WebWindow(const QString& url, int width, int height);
+    ~WebWindow();
+
+    static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
+
+public slots:
+    void setVisible(bool visible);
+    ScriptEventBridge* getEventBridge() const { return _eventBridge; }
+
+private:
+    QWidget* _window;
+    ScriptEventBridge* _eventBridge;
+};
+
+#endif

From feaf5678bbaeb499ed4ae21bf9889c4df2fbd1f4 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 6 Nov 2014 15:00:39 -0800
Subject: [PATCH 07/37] Add grid tools to entity edit tools

---
 examples/libraries/entitySelectionTool.js    |  73 +++--
 examples/libraries/gridTool.js               | 266 +++++++++++++++++++
 examples/newEditEntities.js                  |  27 +-
 gridControls.html                            | 206 ++++++++++++++
 libraries/script-engine/src/ScriptEngine.cpp |   5 +
 libraries/script-engine/src/ScriptEngine.h   |   1 +
 6 files changed, 543 insertions(+), 35 deletions(-)
 create mode 100644 examples/libraries/gridTool.js
 create mode 100644 gridControls.html

diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index 29bf1bdd79..980689d625 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -394,8 +394,7 @@ SelectionDisplay = (function () {
     var baseOverlayAngles = { x: 0, y: 0, z: 0 };
     var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles);
     var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", {
-                    position: { x:0, y: 0, z: 0},
-                    size: 1,
+                    position: { x: 1, y: 0, z: 0},
                     color: { red: 51, green: 152, blue: 203 },
                     alpha: 0.5,
                     solid: true,
@@ -570,6 +569,7 @@ SelectionDisplay = (function () {
         xRailOverlay,
         yRailOverlay,
         zRailOverlay,
+        baseOfEntityProjectionOverlay,
     ].concat(stretchHandles);
 
     overlayNames[highlightBox] = "highlightBox";
@@ -878,20 +878,24 @@ SelectionDisplay = (function () {
             translateHandlesVisible = false;
         }
         
-        var rotation = SelectionManager.worldRotation;
-        var dimensions = SelectionManager.worldDimensions;
-        var position = SelectionManager.worldPosition;
+        var rotation = selectionManager.worldRotation;
+        var dimensions = selectionManager.worldDimensions;
+        var position = selectionManager.worldPosition;
 
         Overlays.editOverlay(baseOfEntityProjectionOverlay, 
                             { 
-                                visible: true,
-                                solid:true,
-                                lineWidth: 2.0,
-                                position: { x: position.x,
-                                            y: 0,
-                                            z: position.z },
-
-                                dimensions: { x: dimensions.x, y: 0, z: dimensions.z },
+                                visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL",
+                                solid: true,
+                                // lineWidth: 2.0,
+                                position: {
+                                    x: position.x,
+                                    y: grid.getOrigin().y,
+                                    z: position.z
+                                },
+                                dimensions: {
+                                    x: dimensions.x,
+                                    y: dimensions.z
+                                },
                                 rotation: rotation,
                             });
 
@@ -1098,6 +1102,7 @@ SelectionDisplay = (function () {
 
     var initialXZPick = null;
     var isConstrained = false;
+    var constrainMajorOnly = false;
     var startPosition = null;
     var duplicatedEntityIDs = null;
     var translateXZTool = {
@@ -1143,34 +1148,46 @@ SelectionDisplay = (function () {
 
             // If shifted, constrain to one axis
             if (event.isShifted) {
-                if (Math.abs(vector.x) > Math.abs(vector.z)) {
-                    vector.z = 0;
-                } else {
-                    vector.x = 0;
-                }
+                // if (Math.abs(vector.x) > Math.abs(vector.z)) {
+                //     vector.z = 0;
+                // } else {
+                //     vector.x = 0;
+                // }
                 if (!isConstrained) {
-                    Overlays.editOverlay(xRailOverlay, { visible: true });
-                    var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 });
-                    var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 });
-                    var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 });
-                    var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 });
-                    Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true });
-                    Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true });
+                    // Overlays.editOverlay(xRailOverlay, { visible: true });
+                    // var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 });
+                    // var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 });
+                    // var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 });
+                    // var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 });
+                    // Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true });
+                    // Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true });
                     isConstrained = true;
                 }
+                // constrainMajorOnly = event.isControl;
+                // vector = Vec3.subtract(
+                //     grid.snapToGrid(Vec3.sum(startPosition, vector), constrainMajorOnly),
+                //     startPosition);
             } else {
                 if (isConstrained) {
-                    Overlays.editOverlay(xRailOverlay, { visible: false });
-                    Overlays.editOverlay(zRailOverlay, { visible: false });
+                    // Overlays.editOverlay(xRailOverlay, { visible: false });
+                    // Overlays.editOverlay(zRailOverlay, { visible: false });
+                    isConstrained = false;
                 }
             }
 
+            constrainMajorOnly = event.isControl;
+            var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, selectionManager.worldDimensions));
+            vector = Vec3.subtract(
+                    grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly),
+                    cornerPosition);
+
             var wantDebug = false;
 
             for (var i = 0; i < SelectionManager.selections.length; i++) {
                 var properties = SelectionManager.savedProperties[SelectionManager.selections[i].id];
+                var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z });
                 Entities.editEntity(SelectionManager.selections[i], {
-                    position: Vec3.sum(properties.position, vector),
+                    position: newPosition,
                 });
 
                 if (wantDebug) {
diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js
new file mode 100644
index 0000000000..76c0581b2c
--- /dev/null
+++ b/examples/libraries/gridTool.js
@@ -0,0 +1,266 @@
+Grid = function(opts) {
+    var that = {};
+
+    var color = { red: 100, green: 152, blue: 203 };
+    var gridColor = { red: 100, green: 152, blue: 203 };
+    var gridAlpha = 0.9;
+    var origin = { x: 0, y: 0, z: 0 };
+    var majorGridEvery = 5;
+    var minorGridSpacing = 0.2;
+    var halfSize = 40;
+    var yOffset = 0.001;
+
+    var worldSize = 16384;
+
+    var minorGridWidth = 0.5;
+    var majorGridWidth = 1.5;
+
+    var gridOverlays = [];
+
+    var snapToGrid = true;
+
+    var gridPlane = Overlays.addOverlay("rectangle3d", {
+        position: origin,
+        color: color,
+        size: halfSize * 2 * minorGridSpacing * 1.05,
+        alpha: 0.2,
+        solid: true,
+        visible: false,
+        ignoreRayIntersection: true,
+    });
+
+    that.getMinorIncrement = function() { return minorGridSpacing; };
+    that.getMajorIncrement = function() { return minorGridSpacing * majorGridEvery; };
+
+    that.visible = false;
+
+    that.getOrigin = function() {
+        return origin;
+    }
+
+    that.getSnapToGrid = function() { return snapToGrid; };
+
+    that.setVisible = function(visible, noUpdate) {
+        that.visible = visible;
+        updateGrid();
+        // for (var i = 0; i < gridOverlays.length; i++) {
+        //     Overlays.editOverlay(gridOverlays[i], { visible: visible });
+        // }
+        // Overlays.editOverlay(gridPlane, { visible: visible });
+
+        if (!noUpdate) {
+            that.emitUpdate();
+        }
+    }
+
+    that.snapToGrid = function(position, majorOnly) {
+        if (!snapToGrid) {
+            return position;
+        }
+
+        var spacing = majorOnly ? (minorGridSpacing * majorGridEvery) : minorGridSpacing;
+
+        position = Vec3.subtract(position, origin);
+
+        position.x = Math.round(position.x / spacing) * spacing;
+        position.y = Math.round(position.y / spacing) * spacing;
+        position.z = Math.round(position.z / spacing) * spacing;
+
+        return Vec3.sum(position, origin);
+    }
+
+    that.setPosition = function(newPosition, noUpdate) {
+        origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 });
+        updateGrid();
+
+        print("updated grid");
+        if (!noUpdate) {
+            that.emitUpdate();
+        }
+    };
+
+    that.emitUpdate = function() {
+        if (that.onUpdate) {
+            that.onUpdate({
+                origin: origin,
+                minorGridSpacing: minorGridSpacing,
+                majorGridEvery: majorGridEvery,
+                gridSize: halfSize,
+                visible: that.visible,
+                snapToGrid: snapToGrid,
+                gridColor: gridColor,
+            });
+        }
+    };
+
+    that.update = function(data) {
+        print("Got update");
+        if (data.snapToGrid !== undefined) {
+            snapToGrid = data.snapToGrid;
+        }
+
+        if (data.origin) {
+            var pos = data.origin;
+            pos.x = pos.x === undefined ? origin.x : pos.x;
+            pos.y = pos.y === undefined ? origin.y : pos.y;
+            pos.z = pos.z === undefined ? origin.z : pos.z;
+            that.setPosition(pos, true);
+        }
+
+        if (data.minorGridSpacing) {
+            minorGridSpacing = data.minorGridSpacing;
+        }
+
+        if (data.majorGridEvery) {
+            majorGridEvery = data.majorGridEvery;
+        }
+
+        if (data.gridColor) {
+            gridColor = data.gridColor;
+        }
+
+        if (data.gridSize) {
+            halfSize = data.gridSize;
+        }
+
+        if (data.visible !== undefined) {
+            that.setVisible(data.visible, true);
+        }
+
+        updateGrid();
+    }
+
+    function updateGrid() {
+        // Delete overlays
+        var gridLinesRequired = (halfSize * 2 + 1) * 2;
+        if (gridLinesRequired > gridOverlays.length) {
+            for (var i = gridOverlays.length; i < gridLinesRequired; i++) {
+                gridOverlays.push(Overlays.addOverlay("line3d", {}));
+            }
+        } else if (gridLinesRequired < gridOverlays.length) {
+            var numberToRemove = gridOverlays.length - gridLinesRequired;
+            var removed = gridOverlays.splice(gridOverlays.length - numberToRemove, numberToRemove);
+            for (var i = 0; i < removed.length; i++) {
+                Overlays.deleteOverlay(removed[i]);
+            }
+        }
+
+        Overlays.editOverlay(gridPlane, {
+            position: origin,
+            size: halfSize * 2 * minorGridSpacing * 1.05,
+        });
+
+        var startX = {
+            x: origin.x - (halfSize * minorGridSpacing),
+            y: origin.y,
+            z: origin.z,
+        };
+        var endX = {
+            x: origin.x + (halfSize * minorGridSpacing),
+            y: origin.y,
+            z: origin.z,
+        };
+        var startZ = {
+            x: origin.x,
+            y: origin.y,
+            z: origin.z - (halfSize * minorGridSpacing)
+        };
+        var endZ = {
+            x: origin.x,
+            y: origin.y,
+            z: origin.z + (halfSize * minorGridSpacing)
+        };
+
+        var overlayIdx = 0;
+        for (var i = -halfSize; i <= halfSize; i++) {
+            // Offset for X-axis aligned grid line
+            var offsetX = { x: 0, y: 0, z: i * minorGridSpacing };
+
+            // Offset for Z-axis aligned grid line
+            var offsetZ = { x: i * minorGridSpacing, y: 0, z: 0 };
+
+            var position = Vec3.sum(origin, offsetX);
+            var size = i % majorGridEvery == 0 ? majorGridWidth : minorGridWidth;
+
+            var gridLineX = gridOverlays[overlayIdx++];
+            var gridLineZ = gridOverlays[overlayIdx++];
+
+            Overlays.editOverlay(gridLineX, {
+                start: Vec3.sum(startX, offsetX),
+                end: Vec3.sum(endX, offsetX),
+                lineWidth: size,
+                color: gridColor,
+                alpha: gridAlpha,
+                solid: true,
+                visible: that.visible,
+                ignoreRayIntersection: true,
+            });
+            Overlays.editOverlay(gridLineZ, {
+                start: Vec3.sum(startZ, offsetZ),
+                end: Vec3.sum(endZ, offsetZ),
+                lineWidth: size,
+                color: gridColor,
+                alpha: gridAlpha,
+                solid: true,
+                visible: that.visible,
+                ignoreRayIntersection: true,
+            });
+        }
+    }
+
+    function cleanup() {
+        Overlays.deleteOverlay(gridPlane);
+        for (var i = 0; i < gridOverlays.length; i++) {
+            Overlays.deleteOverlay(gridOverlays[i]);
+        }
+    }
+
+    that.addListener = function(callback) {
+        that.onUpdate = callback;
+    }
+
+    Script.scriptEnding.connect(cleanup);
+    updateGrid();
+
+    that.onUpdate = null;
+
+    return that;
+};
+
+GridTool = function(opts) {
+    var that = {};
+
+    var horizontalGrid = opts.horizontalGrid;
+    var verticalGrid = opts.verticalGrid;
+    var listeners = [];
+
+    // var webView = Window.createWebView('http://localhost:8000/gridControls.html', 200, 280);
+    var webView = new WebWindow('http://localhost:8000/gridControls.html', 200, 280);
+
+    horizontalGrid.addListener(function(data) {
+        webView.eventBridge.emitScriptEvent(JSON.stringify(data));
+    });
+
+    webView.eventBridge.webEventReceived.connect(function(data) {
+        print('got event: ' + data);
+        data = JSON.parse(data);
+        if (data.type == "init") {
+            horizontalGrid.emitUpdate(); 
+        } else if (data.type == "update") {
+            horizontalGrid.update(data);
+            for (var i = 0; i < listeners.length; i++) {
+                listeners[i](data);
+            }
+        }
+    });
+
+    that.addListener = function(callback) {
+        listeners.push(callback);
+    }
+
+    that.setVisible = function(visible) {
+        webView.setVisible(visible);
+    }
+
+    return that;
+};
diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js
index ea1f69e15f..b8e7a6174e 100644
--- a/examples/newEditEntities.js
+++ b/examples/newEditEntities.js
@@ -35,6 +35,12 @@ var entityPropertyDialogBox = EntityPropertyDialogBox;
 Script.include("libraries/entityCameraTool.js");
 var cameraManager = new CameraManager();
 
+Script.include("libraries/gridTool.js");
+var grid = Grid();
+gridTool = GridTool({ horizontalGrid: grid });
+gridTool.addListener(function(data) {
+});
+
 selectionManager.setEventListener(selectionDisplay.updateHandles);
 
 var windowDimensions = Controller.getViewportDimensions();
@@ -258,6 +264,7 @@ var toolBar = (function () {
 
         if (activeButton === toolBar.clicked(clickedOverlay)) {
             isActive = !isActive;
+            gridTool.setVisible(isActive);
             if (!isActive) {
                 selectionManager.clearSelections();
                 cameraManager.disable();
@@ -747,25 +754,32 @@ Controller.keyReleaseEvent.connect(function (event) {
         if (isActive) {
             cameraManager.enable();
         }
+    } else if (event.text == 'g') {
+        if (isActive && selectionManager.hasSelection()) {
+            var newPosition = selectionManager.worldPosition;
+            newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 });
+            grid.setPosition(newPosition);
+        }
     } else if (isActive) {
         var delta = null;
+        var increment = event.isShifted ? grid.getMajorIncrement() : grid.getMinorIncrement();
 
         if (event.text == 'UP') {
             if (event.isControl || event.isAlt) {
-                delta = { x: 0, y: 1, z: 0 };
+                delta = { x: 0, y: increment, z: 0 };
             } else {
-                delta = { x: 0, y: 0, z: -1 };
+                delta = { x: 0, y: 0, z: -increment };
             }
         } else if (event.text == 'DOWN') {
             if (event.isControl || event.isAlt) {
-                delta = { x: 0, y: -1, z: 0 };
+                delta = { x: 0, y: -increment, z: 0 };
             } else {
-                delta = { x: 0, y: 0, z: 1 };
+                delta = { x: 0, y: 0, z: increment };
             }
         } else if (event.text == 'LEFT') {
-            delta = { x: -1, y: 0, z: 0 };
+            delta = { x: -increment, y: 0, z: 0 };
         } else if (event.text == 'RIGHT') {
-            delta = { x: 1, y: 0, z: 0 };
+            delta = { x: increment, y: 0, z: 0 };
         }
 
         if (delta != null) {
@@ -820,7 +834,6 @@ function applyEntityProperties(data) {
         var properties = data.createEntities[i].properties;
         var newEntityID = Entities.addEntity(properties);
         DELETED_ENTITY_MAP[entityID.id] = newEntityID;
-        print(newEntityID.isKnownID);
         if (data.selectCreated) {
             selectedEntityIDs.push(newEntityID);
         }
diff --git a/gridControls.html b/gridControls.html
new file mode 100644
index 0000000000..84d437a60d
--- /dev/null
+++ b/gridControls.html
@@ -0,0 +1,206 @@
+<html>
+<head>
+    <script>
+        function loaded() {
+            function log(msg) {
+                var el = document.createElement('div');
+                el.innerHTML = msg;
+                document.body.appendChild(el);
+            }
+
+            var gridColor = { red: 0, green: 0, blue: 0 };
+            var gridColors = [
+                { red: 0, green: 0, blue: 0 },
+                { red: 128, green: 128, blue: 128 },
+                // { red: 255, green: 255, blue: 255 },
+                { red: 255, green: 128, blue: 128 },
+                { red: 128, green: 255, blue: 128 },
+                { red: 128, green: 128, blue: 255 },
+            ];
+
+            posY = document.getElementById("horiz-y");
+            minorSpacing = document.getElementById("minor-spacing");
+            majorSpacing = document.getElementById("major-spacing");
+            gridOn = document.getElementById("grid-on");
+            snapToGrid = document.getElementById("snap-to-grid");
+            hGridVisible = document.getElementById("horiz-grid-visible");
+
+
+            if (window.EventBridge !== undefined) {
+                EventBridge.scriptEventReceived.connect(function(data) {
+                    data = JSON.parse(data);
+                    if (data.origin) {
+
+                        var origin = data.origin;
+                        // posX.value = origin.x;
+                        posY.value = origin.y;
+                        // posZ.value = origin.z;
+                    }
+
+                    if (data.minorGridSpacing) {
+                        minorSpacing.value = data.minorGridSpacing;
+                    }
+
+                    if (data.majorGridEvery) {
+                        majorSpacing.value = data.majorGridEvery;
+                    }
+
+                    if (data.gridColor) {
+                        gridColor = data.gridColor;
+                    }
+
+                    if (data.snapToGrid !== undefined) {
+                        snapToGrid.checked = data.snapToGrid == true;
+                    }
+
+                    if (data.visible !== undefined) {
+                        hGridVisible.checked = data.visible == true;
+                    }
+                });
+
+                function emitUpdate() {
+                    EventBridge.emitWebEvent(JSON.stringify({
+                        type: "update",
+                        origin: {
+                            // x: posX.value,
+                            y: posY.value,
+                            // z: posZ.value,
+                        },
+                        minorGridSpacing: minorSpacing.value,
+                        majorGridEvery: majorSpacing.value,
+                        gridColor: gridColor,
+                        snapToGrid: snapToGrid.checked,
+                        visible: hGridVisible.checked,
+                    }));
+                }
+
+            }
+
+            document.addEventListener("input", emitUpdate);
+            hGridVisible.addEventListener("change", emitUpdate);
+            snapToGrid.addEventListener("change", emitUpdate);
+
+            var gridColorBox = document.getElementById('grid-color');
+
+            for (var i = 0; i < gridColors.length; i++) {
+                var colors = gridColors[i];
+                var box = document.createElement('div');
+                box.setAttribute('class', 'color-box');
+                box.style.background = 'rgb(' + colors.red + ', ' + colors.green + ', ' + colors.blue + ')';
+                document.getElementById("grid-colors").appendChild(box);
+                box.addEventListener("click", function(color) { 
+                    return function() {
+                        gridColor = color;
+                        // this.setAttribute('class', 'color-box highlight');
+
+                        emitUpdate();
+                    }
+                }({ red: colors.red, green: colors.green, blue: colors.blue }));
+            }
+
+            EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
+        }
+    </script>
+    <style>
+        * {
+        }
+
+        body {
+            margin: 0;
+            padding: 0;
+
+            background: #DDD;
+            font-family: Sans-Serif;
+            font-size: 12px;
+
+            -webkit-touch-callout: none;
+            -webkit-user-select: none;
+            -khtml-user-select: none;
+            -moz-user-select: none;
+            -ms-user-select: none;
+            user-select: none;
+        }
+
+        input {
+            line-height: 2;
+        }
+
+        .input-left {
+            display: inline-block;
+            width: 20px;
+        }
+
+        .color-box {
+            display: inline-block;
+            width: 20px;
+            height: 20px;
+            border: 1px solid black;
+            background: blue;
+            margin: 2px;
+        }
+
+        .color-box.highlight {
+            width: 18px;
+            height: 18px;
+            border: 2px solid black;
+        }
+
+        .section-header {
+            background: #AAA;
+            border-bottom: 1px solid #CCC;
+        }
+
+        .section-header label {
+            font-weight: bold;
+        }
+
+        .grid-section {
+            border-top: 1px solid #DDD;
+            padding: 4px 0px 4px 20px;
+            background: #DDD;
+        }
+    </style>
+</head>
+<body onload='loaded();'>
+    <div class="section-header">
+        <input type='checkbox' id="horiz-grid-visible">
+        <label>Horizontal Grid</label>
+    </div>
+    <div class="grid-section">
+
+        <label>Snap to grid</label>
+        <div>
+            <div class="input-left">
+            </div>
+            <input type='checkbox' id="snap-to-grid">
+        </div>
+
+        <label>Position (Y Axis)</label>
+        <div id="horizontal-position">
+            <div class="input-left">
+            </div>
+            <input type='number' id="horiz-y" class="number" value="0.0" step="0.1"></input>
+        </div>
+
+        <label>Minor Grid Size</label>
+        <div>
+            <div class="input-left">
+            </div>
+            <input type='number' id="minor-spacing" min="0" step="0.01", ></input>
+        </div>
+
+        <label>Major Grid Every</label>
+        <div>
+            <div class="input-left">
+            </div>
+            <input type='number' id="major-spacing" min="2" step="1", ></input>
+        </div>
+
+        <label>Grid Color</label>
+        <div id="grid-colors">
+            <div class="input-left">
+            </div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index fb98124fc9..8f176de04f 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -322,6 +322,11 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob
     return QScriptValue::NullValue;
 }
 
+void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {
+    QScriptValue scriptFun = newFunction(fun, numArguments);
+    globalObject().setProperty(name, scriptFun);
+}
+
 void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
                                         QScriptEngine::FunctionSignature setter, QScriptValue object) {
     QScriptValue setterFunction = newFunction(setter, 1);
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index d556475859..22617512a9 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -65,6 +65,7 @@ public:
     QScriptValue registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name
     void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
                               QScriptEngine::FunctionSignature setter, QScriptValue object = QScriptValue::NullValue);
+    void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
 
     Q_INVOKABLE void setIsAvatar(bool isAvatar);
     bool isAvatar() const { return _isAvatar; }

From 1e6cadc7c14da183069278632c2e31d8bec002d2 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 7 Nov 2014 00:06:57 +0100
Subject: [PATCH 08/37] Extra lines

---
 interface/src/Audio.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 7fc563411a..6129ee4ee0 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -1026,8 +1026,6 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou
     }
 }
 
-
-
 void Audio::addReceivedAudioToStream(const QByteArray& audioByteArray) {
     if (_audioOutput) {
         // Audio output must exist and be correctly set up if we're going to process received audio

From dcfeef471250e2dfb6d320a1a6646584c59d765b Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 7 Nov 2014 00:24:37 +0100
Subject: [PATCH 09/37] Reference and comments

---
 interface/src/Audio.cpp | 3 ++-
 interface/src/Audio.h   | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 6129ee4ee0..79ebcdd043 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -587,8 +587,9 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF
     }
 }
 
-void Audio::handleLocalEchoAndReverb(QByteArray inputByteArray) {
+void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
     bool hasEcho = Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio);
+    // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
     bool hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) &&
                           !Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio);
     if (_muted || !_audioOutput || (!hasEcho && !hasLocalReverb)) {
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index e58afa306f..be51511dcc 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -273,7 +273,7 @@ private:
     void updateGverbOptions();
     void addReverb(int16_t* samples, int numSamples, QAudioFormat& format);
 
-    void handleLocalEchoAndReverb(QByteArray inputByteArray);
+    void handleLocalEchoAndReverb(QByteArray& inputByteArray);
     
     // Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
     void addProceduralSounds(int16_t* monoInput, int numSamples);

From 011e5319710fe8cc50366700ad039ff77e2e110a Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 7 Nov 2014 14:41:59 +0100
Subject: [PATCH 10/37] Magic numbers and casts

---
 interface/src/Audio.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 79ebcdd043..2d7b117f51 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -574,11 +574,11 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF
         for (unsigned int j = sample; j < sample + audioFormat.channelCount(); j++) {
             if (j == sample) {
                 // left channel
-                int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), -32768, 32767);
+                int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
                 samplesData[j] = (int16_t)lResult;
             } else if (j == (sample + 1)) {
                 // right channel
-                int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), -32768, 32767);
+                int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
                 samplesData[j] = (int16_t)rResult;
             } else {
                 // ignore channels above 2
@@ -610,7 +610,8 @@ void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
                                            (_outputFormat.channelCount() / _inputFormat.channelCount());
         loopBackByteArray.resize(inputByteArray.size() * loopbackOutputToInputRatio);
         loopBackByteArray.fill(0);
-        linearResampling((int16_t*)inputByteArray.data(), (int16_t*)loopBackByteArray.data(),
+        linearResampling(reinterpret_cast<int16_t*>(inputByteArray.data()),
+                         reinterpret_cast<int16_t*>(loopBackByteArray.data()),
                          inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t),
                          _inputFormat, _outputFormat);
     }
@@ -621,16 +622,16 @@ void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
             loopbackCopy = loopBackByteArray;
         }
         
-        int16_t* loopbackSamples = (int16_t*) loopBackByteArray.data();
+        int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
         int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
         updateGverbOptions();
         addReverb(loopbackSamples, numLoopbackSamples, _outputFormat);
         
         if (!hasEcho) {
-            int16_t* loopbackCopySamples = (int16_t*) loopbackCopy.data();
+            int16_t* loopbackCopySamples = reinterpret_cast<int16_t*>(loopbackCopy.data());
             for (int i = 0; i < numLoopbackSamples; ++i) {
                 loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i],
-                                                -32768, 32767);
+                                                MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
             }
         }
     }

From a4db2441822a05ed5b7ff859830accc3642bfba9 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 13:57:20 -0800
Subject: [PATCH 11/37] Mirror/warning fixes.

---
 interface/src/Application.cpp        | 18 +++++++-----------
 interface/src/ui/RearMirrorTools.cpp |  7 +++++--
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index e58f79cb5e..0740e424a1 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -108,7 +108,7 @@ static unsigned STARFIELD_SEED = 1;
 
 static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
 
-const unsigned MAXIMUM_CACHE_SIZE = 10737418240;  // 10GB
+const qint64 MAXIMUM_CACHE_SIZE = 10737418240L;  // 10GB
 
 static QTimer* idleTimer = NULL;
 
@@ -724,11 +724,11 @@ void Application::paintGL() {
         displaySide(*whichCamera);
         glPopMatrix();
 
-        if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
-            renderRearViewMirror(_mirrorViewRect);
-
-        } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
+        if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
             _rearMirrorTools->render(true);
+        
+        } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
+            renderRearViewMirror(_mirrorViewRect);       
         }
 
         _glowEffect.render();
@@ -4314,8 +4314,6 @@ bool Application::isVSyncOn() const {
     if (wglewGetExtension("WGL_EXT_swap_control")) {
         int swapInterval = wglGetSwapIntervalEXT();
         return (swapInterval > 0);
-    } else {
-        return true;
     }
 #elif defined(Q_OS_LINUX)
     // TODO: write the poper code for linux
@@ -4326,10 +4324,9 @@ bool Application::isVSyncOn() const {
     } else {
         return true;
     }
-    */
-#else
-    return true;
+    */    
 #endif
+    return true;
 }
 
 bool Application::isVSyncEditable() const {
@@ -4344,7 +4341,6 @@ bool Application::isVSyncEditable() const {
         return true;
     }
     */
-#else
 #endif
     return false;
 }
diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp
index f4b0bb82b5..8423a9c432 100644
--- a/interface/src/ui/RearMirrorTools.cpp
+++ b/interface/src/ui/RearMirrorTools.cpp
@@ -40,7 +40,7 @@ RearMirrorTools::RearMirrorTools(QGLWidget* parent, QRect& bounds, QSettings* se
     _zoomHeadTextureId = _parent->bindTexture(QImage(Application::resourcesPath() + "images/plus.svg"));
     _zoomBodyTextureId = _parent->bindTexture(QImage(Application::resourcesPath() + "images/minus.svg"));
 
-    _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
+    _shrinkIconRect = QRect(ICON_PADDING, ICON_SIZE + ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
@@ -106,7 +106,7 @@ bool RearMirrorTools::mousePressEvent(int x, int y) {
     }
     
     if (_fullScreen) {
-        if (_shrinkIconRect.contains(x, y)) {
+        if (_shrinkIconRect.contains(x, 2 * _shrinkIconRect.top() - y)) {
             _fullScreen = false;
             emit shrinkView();
             return true;
@@ -156,6 +156,9 @@ void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, GLuint texture
     }
     glEnd();
     glPopMatrix();
+    
+    glMatrixMode(GL_MODELVIEW);
+    
     glBindTexture(GL_TEXTURE_2D, 0);
     glDisable(GL_TEXTURE_2D);
 }

From 61e0660d6542dd196b62f96bfb75c0ee0f065e6d Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 14:01:28 -0800
Subject: [PATCH 12/37] This actually seems to work differently on OS X versus
 Linux.

---
 interface/src/ui/RearMirrorTools.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp
index 8423a9c432..fb6ef63647 100644
--- a/interface/src/ui/RearMirrorTools.cpp
+++ b/interface/src/ui/RearMirrorTools.cpp
@@ -40,7 +40,7 @@ RearMirrorTools::RearMirrorTools(QGLWidget* parent, QRect& bounds, QSettings* se
     _zoomHeadTextureId = _parent->bindTexture(QImage(Application::resourcesPath() + "images/plus.svg"));
     _zoomBodyTextureId = _parent->bindTexture(QImage(Application::resourcesPath() + "images/minus.svg"));
 
-    _shrinkIconRect = QRect(ICON_PADDING, ICON_SIZE + ICON_PADDING, ICON_SIZE, ICON_SIZE);
+    _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
     _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
@@ -106,7 +106,7 @@ bool RearMirrorTools::mousePressEvent(int x, int y) {
     }
     
     if (_fullScreen) {
-        if (_shrinkIconRect.contains(x, 2 * _shrinkIconRect.top() - y)) {
+        if (_shrinkIconRect.contains(x, y)) {
             _fullScreen = false;
             emit shrinkView();
             return true;

From b421ee92432e87e25aa0835ac14855c8fd21dd6f Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 14:09:40 -0800
Subject: [PATCH 13/37] Fix for audio toolbox.

---
 interface/src/ui/ApplicationOverlay.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 4d2c710be7..e0aa1f3020 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -984,7 +984,9 @@ void ApplicationOverlay::renderAudioMeter() {
     const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP;
 
     int audioMeterY;
-    if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
+    bool boxed = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) &&
+        !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
+    if (boxed) {
         audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
     } else {
         audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
@@ -1022,9 +1024,7 @@ void ApplicationOverlay::renderAudioMeter() {
         renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f);
     }
 
-    audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP,
-                         audioMeterY,
-                         Menu::getInstance()->isOptionChecked(MenuOption::Mirror));
+    audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed);
 
     audio->renderScope(glWidget->width(), glWidget->height());
 

From 70080124928ceb8875eeade003ac423591e7eace Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 14:20:31 -0800
Subject: [PATCH 14/37] Trigger fixes.

---
 interface/src/Application.cpp | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 0740e424a1..a2cfc8f024 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2009,25 +2009,17 @@ void Application::init() {
 
 void Application::closeMirrorView() {
     if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
-        Menu::getInstance()->triggerOption(MenuOption::Mirror);;
+        Menu::getInstance()->triggerOption(MenuOption::Mirror);
     }
 }
 
 void Application::restoreMirrorView() {
-    if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
-        Menu::getInstance()->triggerOption(MenuOption::Mirror);;
-    }
-
     if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
         Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror);
     }
 }
 
 void Application::shrinkMirrorView() {
-    if (!Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
-        Menu::getInstance()->triggerOption(MenuOption::Mirror);;
-    }
-
     if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
         Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror);
     }

From cd110d60482feeddcf9a9b91ecdecba5ea354928 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 14:35:01 -0800
Subject: [PATCH 15/37] Fix for invalid projection matrix on startup.

---
 interface/src/Application.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a2cfc8f024..0afe011cd6 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -786,7 +786,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
     // Tell our viewFrustum about this change, using the application camera
     if (updateViewFrustum) {
         loadViewFrustum(camera, _viewFrustum);
-        computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
+        _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
 
         // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the
         // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum

From 6445b3aa5d46e4ff4d69211b6d00c924ddeb3197 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Fri, 7 Nov 2014 15:59:16 -0800
Subject: [PATCH 16/37] Linux warnings fixes.

---
 interface/src/Audio.cpp           |  2 +-
 interface/src/Menu.cpp            |  2 +-
 interface/src/gpu/Batch.cpp       |  2 +-
 interface/src/gpu/GLBackend.cpp   | 53 ++++++++++++++++---------------
 interface/src/gpu/Resource.cpp    |  2 ++
 interface/src/renderer/Model.cpp  |  4 ---
 interface/src/ui/TextRenderer.cpp |  6 ++--
 libraries/avatars/src/Player.cpp  |  6 ++--
 libraries/avatars/src/Player.h    |  6 ++--
 9 files changed, 41 insertions(+), 42 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 3039b7adfb..98ad76dcd2 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -544,7 +544,7 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF
         gverb_do(_gverb, value, &lValue, &rValue);
 
         // Mix, accounting for clipping, the left and right channels. Ignore the rest.
-        for (unsigned int j = sample; j < sample + audioFormat.channelCount(); j++) {
+        for (int j = sample; j < sample + audioFormat.channelCount(); j++) {
             if (j == sample) {
                 // left channel
                 int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), -32768, 32767);
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 21c1cded88..cd074805b6 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -386,7 +386,7 @@ Menu::Menu() :
 
 #if defined(Q_OS_MAC)
 #else
-        QAction* vsyncAction = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, this, SLOT(changeVSync()));
+        addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, this, SLOT(changeVSync()));
 #endif
     }
 
diff --git a/interface/src/gpu/Batch.cpp b/interface/src/gpu/Batch.cpp
index e02a0a551e..e176e1641a 100644
--- a/interface/src/gpu/Batch.cpp
+++ b/interface/src/gpu/Batch.cpp
@@ -106,7 +106,7 @@ void Batch::setInputStream(Slot startChannel, const BufferStream& stream) {
         const Buffers& buffers = stream.getBuffers();
         const Offsets& offsets = stream.getOffsets();
         const Offsets& strides = stream.getStrides();
-        for (int i = 0; i < buffers.size(); i++) {
+        for (unsigned int i = 0; i < buffers.size(); i++) {
             setInputBuffer(startChannel + i, buffers[i], offsets[i], strides[i]);
         }
     }
diff --git a/interface/src/gpu/GLBackend.cpp b/interface/src/gpu/GLBackend.cpp
index 5e13d03ff4..5c2451d81f 100644
--- a/interface/src/gpu/GLBackend.cpp
+++ b/interface/src/gpu/GLBackend.cpp
@@ -31,15 +31,15 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
 
     (&::gpu::GLBackend::do_glEnable),
     (&::gpu::GLBackend::do_glDisable),
-
+
     (&::gpu::GLBackend::do_glEnableClientState),
     (&::gpu::GLBackend::do_glDisableClientState),
 
     (&::gpu::GLBackend::do_glCullFace),
     (&::gpu::GLBackend::do_glAlphaFunc),
 
-    (&::gpu::GLBackend::do_glDepthFunc),
-    (&::gpu::GLBackend::do_glDepthMask),
+    (&::gpu::GLBackend::do_glDepthFunc),
+    (&::gpu::GLBackend::do_glDepthMask),
     (&::gpu::GLBackend::do_glDepthRange),
 
     (&::gpu::GLBackend::do_glBindBuffer),
@@ -57,18 +57,18 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
     (&::gpu::GLBackend::do_glPushMatrix),
     (&::gpu::GLBackend::do_glPopMatrix),
     (&::gpu::GLBackend::do_glMultMatrixf),
-    (&::gpu::GLBackend::do_glLoadMatrixf),
-    (&::gpu::GLBackend::do_glLoadIdentity),
-    (&::gpu::GLBackend::do_glRotatef),
-    (&::gpu::GLBackend::do_glScalef),
-    (&::gpu::GLBackend::do_glTranslatef),
+    (&::gpu::GLBackend::do_glLoadMatrixf),
+    (&::gpu::GLBackend::do_glLoadIdentity),
+    (&::gpu::GLBackend::do_glRotatef),
+    (&::gpu::GLBackend::do_glScalef),
+    (&::gpu::GLBackend::do_glTranslatef),
 
-    (&::gpu::GLBackend::do_glDrawArrays),
+    (&::gpu::GLBackend::do_glDrawArrays),
     (&::gpu::GLBackend::do_glDrawRangeElements),
-
-    (&::gpu::GLBackend::do_glColorPointer),
-    (&::gpu::GLBackend::do_glNormalPointer),
-    (&::gpu::GLBackend::do_glTexCoordPointer),
+
+    (&::gpu::GLBackend::do_glColorPointer),
+    (&::gpu::GLBackend::do_glNormalPointer),
+    (&::gpu::GLBackend::do_glTexCoordPointer),
     (&::gpu::GLBackend::do_glVertexPointer),
 
     (&::gpu::GLBackend::do_glVertexAttribPointer),
@@ -77,7 +77,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
 
     (&::gpu::GLBackend::do_glColor4f),
 
-    (&::gpu::GLBackend::do_glMaterialf),
+    (&::gpu::GLBackend::do_glMaterialf),
     (&::gpu::GLBackend::do_glMaterialfv),
 };
 
@@ -112,9 +112,8 @@ static const GLenum _elementTypeToGLType[NUM_TYPES]= {
 
 GLBackend::GLBackend() :
 
-    _inputFormat(0),
-    _inputAttributeActivation(0),
     _needInputFormatUpdate(true),
+    _inputFormat(0),
 
     _inputBuffersState(0),
     _inputBuffers(_inputBuffersState.size(), BufferPointer(0)),
@@ -122,7 +121,8 @@ GLBackend::GLBackend() :
     _inputBufferStrides(_inputBuffersState.size(), 0),
 
     _indexBuffer(0),
-    _indexBufferOffset(0)
+    _indexBufferOffset(0),
+    _inputAttributeActivation(0)
 {
 
 }
@@ -138,7 +138,7 @@ void GLBackend::renderBatch(Batch& batch) {
 
     GLBackend backend;
 
-    for (int i = 0; i < numCommands; i++) {
+    for (unsigned int i = 0; i < numCommands; i++) {
         CommandCall call = _commandCalls[(*command)];
         (backend.*(call))(batch, *offset);
         command++;
@@ -203,7 +203,7 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) {
 
     GLenum glType = _elementTypeToGLType[_indexBufferType];
 
-    glDrawElements(mode, numIndices, glType, (GLvoid*)(startIndex + _indexBufferOffset));
+    glDrawElements(mode, numIndices, glType, reinterpret_cast<GLvoid*>(startIndex + _indexBufferOffset));
     CHECK_GL_ERROR();
 }
 
@@ -265,7 +265,7 @@ void GLBackend::updateInput() {
             }
 
             // Manage Activation what was and what is expected now
-            for (int i = 0; i < newActivation.size(); i++) {
+            for (unsigned int i = 0; i < newActivation.size(); i++) {
                 bool newState = newActivation[i];
                 if (newState != _inputAttributeActivation[i]) {
 #if defined(SUPPORT_LEGACY_OPENGL)
@@ -314,7 +314,7 @@ void GLBackend::updateInput() {
                         CHECK_GL_ERROR();
                         _inputBuffersState[bufferNum] = false;
 
-                        for (int i = 0; i < channel._slots.size(); i++) {
+                        for (unsigned int i = 0; i < channel._slots.size(); i++) {
                             const Stream::Attribute& attrib = attributes.at(channel._slots[i]);
                             GLuint slot = attrib._slot;
                             GLuint count = attrib._element.getDimensionCount();
@@ -325,16 +325,16 @@ void GLBackend::updateInput() {
                             if (slot < NUM_CLASSIC_ATTRIBS) {
                                 switch (slot) {
                                 case Stream::POSITION:
-                                    glVertexPointer(count, type, stride, (GLvoid*)pointer);
+                                    glVertexPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
                                     break;
                                 case Stream::NORMAL:
-                                    glNormalPointer(type, stride, (GLvoid*)pointer);
+                                    glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
                                     break;
                                 case Stream::COLOR:
-                                    glColorPointer(count, type, stride, (GLvoid*)pointer);
+                                    glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
                                     break;
                                 case Stream::TEXCOORD:
-                                    glTexCoordPointer(count, type, stride, (GLvoid*)pointer); 
+                                    glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer)); 
                                     break;
                                 };
                             } else {
@@ -342,7 +342,8 @@ void GLBackend::updateInput() {
                             {
                             #endif
                                 GLboolean isNormalized = attrib._element.isNormalized();
-                                glVertexAttribPointer(slot, count, type, isNormalized, stride, (GLvoid*)pointer);
+                                glVertexAttribPointer(slot, count, type, isNormalized, stride,
+                                    reinterpret_cast<GLvoid*>(pointer));
                             }
                             CHECK_GL_ERROR();
                         }
diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp
index cd1b4ef84b..3a6e47a7ba 100644
--- a/interface/src/gpu/Resource.cpp
+++ b/interface/src/gpu/Resource.cpp
@@ -8,6 +8,8 @@
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
+
+#include "Context.h"
 #include "Resource.h"
 
 #include <QDebug>
diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp
index 38562df696..3137ab8369 100644
--- a/interface/src/renderer/Model.cpp
+++ b/interface/src/renderer/Model.cpp
@@ -479,9 +479,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
         float bestDistance = std::numeric_limits<float>::max();
         float distanceToSubMesh;
         BoxFace subMeshFace;
-        BoxFace bestSubMeshFace;
         int subMeshIndex = 0;
-        int bestSubMeshIndex = -1;
     
         // If we hit the models box, then consider the submeshes...
         foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
@@ -489,9 +487,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
 
             if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) {
                 if (distanceToSubMesh < bestDistance) {
-                    bestSubMeshIndex = subMeshIndex;
                     bestDistance = distanceToSubMesh;
-                    bestSubMeshFace = subMeshFace;
                     intersectedSomething = true;
                     extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
                 }
diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp
index 69df609ba9..fed041d6ea 100644
--- a/interface/src/ui/TextRenderer.cpp
+++ b/interface/src/ui/TextRenderer.cpp
@@ -129,7 +129,7 @@ int TextRenderer::draw(int x, int y, const char* str) {
                                                         leftBottom.x, rightTop.y, ls, tt, };
 
         const int NUM_COLOR_SCALARS_PER_GLYPH = 4;
-        unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor };
+        int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor };
 
         gpu::Buffer::Size offset = sizeof(vertexBuffer) * _numGlyphsBatched;
         gpu::Buffer::Size colorOffset = sizeof(colorBuffer) * _numGlyphsBatched;
@@ -181,9 +181,9 @@ TextRenderer::TextRenderer(const Properties& properties) :
     _color(properties.color),
     _glyphsBuffer(new gpu::Buffer()),
     _glyphsColorBuffer(new gpu::Buffer()),
-    _numGlyphsBatched(0),
     _glyphsStreamFormat(new gpu::Stream::Format()),
-    _glyphsStream(new gpu::BufferStream())
+    _glyphsStream(new gpu::BufferStream()),
+    _numGlyphsBatched(0)
 {
     _glyphsStreamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::POS_XYZ), 0);
     const int NUM_POS_COORDS = 2;
diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp
index 47d1b04421..56f820ab97 100644
--- a/libraries/avatars/src/Player.cpp
+++ b/libraries/avatars/src/Player.cpp
@@ -297,7 +297,7 @@ void Player::play() {
     _injector->setOptions(_options);
 }
 
-void Player::setCurrentFrame(unsigned int currentFrame) {
+void Player::setCurrentFrame(int currentFrame) {
     if (_recording && currentFrame >= _recording->getFrameNumber()) {
         stopPlaying();
         return;
@@ -314,7 +314,7 @@ void Player::setCurrentFrame(unsigned int currentFrame) {
     }
 }
 
-void Player::setCurrentTime(unsigned int currentTime) {
+void Player::setCurrentTime(int currentTime) {
     if (currentTime >= _recording->getLength()) {
         stopPlaying();
         return;
@@ -393,7 +393,7 @@ bool Player::computeCurrentFrame() {
         _currentFrame = 0;
     }
     
-    quint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength());
+    qint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength());
     while(_currentFrame >= 0 &&
           _recording->getFrameTimestamp(_currentFrame) > elapsed) {
         --_currentFrame;
diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h
index 043fcc4a25..7ed76fa495 100644
--- a/libraries/avatars/src/Player.h
+++ b/libraries/avatars/src/Player.h
@@ -44,8 +44,8 @@ public slots:
     void loadRecording(RecordingPointer recording);
     void play();
     
-    void setCurrentFrame(unsigned int currentFrame);
-    void setCurrentTime(unsigned int currentTime);
+    void setCurrentFrame(int currentFrame);
+    void setCurrentTime(int currentTime);
 
     void setVolume(float volume);
     void setAudioOffset(int audioOffset);
@@ -87,4 +87,4 @@ private:
     bool _useSkeletonURL;
 };
 
-#endif // hifi_Player_h
\ No newline at end of file
+#endif // hifi_Player_h

From ef325db052aa4ec933c7e40c245753298ece25cb Mon Sep 17 00:00:00 2001
From: DaveDubUK <DaveDubUK@gmail.com>
Date: Sat, 8 Nov 2014 11:47:57 +0000
Subject: [PATCH 17/37] Formatting changes to walk.js 1.1

Minor formatting changes for HiFi coding standard compliance
---
 examples/libraries/walkApi.js       |  51 ++--
 examples/libraries/walkFilters.js   |  48 ++--
 examples/libraries/walkInterface.js | 156 ++++++------
 examples/walk.js                    | 358 ++++++++++++++++------------
 4 files changed, 321 insertions(+), 292 deletions(-)

diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js
index 90b9dad623..8f9bbfc36c 100644
--- a/examples/libraries/walkApi.js
+++ b/examples/libraries/walkApi.js
@@ -90,25 +90,22 @@ Motion = function() {
 
         for (var i = 0; i < this.avatarJointNames.length; i++) {
 
-            if (i > 17 || i < 34)
+            if (i > 17 || i < 34) {
                 // left hand fingers
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
-
-            else if (i > 33 || i < 38)
+			} else if (i > 33 || i < 38) {
                 // left hand thumb
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
-
-            else if (i > 41 || i < 58)
+			} else if (i > 41 || i < 58) {
                 // right hand fingers
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
-
-            else if (i > 57 || i < 62)
+			} else if (i > 57 || i < 62) {
                 // right hand thumb
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
-
-            else
+            } else {
                 // zero out the remaining joints
                 MyAvatar.clearJointData(this.avatarJointNames[i]);
+			}
         }
     }
 
@@ -123,15 +120,16 @@ Motion = function() {
     this.walkWheelPos = 0;
 
     this.advanceWalkWheel = function(angle){
-
         this.walkWheelPos += angle;
-        if (motion.walkWheelPos >= 360)
+        if (motion.walkWheelPos >= 360) {
             this.walkWheelPos = this.walkWheelPos % 360;
+		}
     }
 
     // last frame history
     this.lastDirection = 0;
     this.lastVelocity = 0;
+    this.lastStrideLength = 0; // kept for use during transitions
 
 };  // end Motion constructor
 
@@ -159,7 +157,7 @@ state = (function () {
         // status vars
         powerOn: true,
         minimised: true,
-        editing:false,
+        editing: false,
         editingTranslation: false,
 
         setInternalState: function(newInternalState) {
@@ -284,10 +282,11 @@ state = (function () {
                     if (motion.strideLength === 0) {
 
                         motion.setGender(MALE);
-                        if (motion.direction === BACKWARDS)
+                        if (motion.direction === BACKWARDS) {
                             motion.strideLength = motion.selWalk.calibration.strideLengthBackwards;
-                        else
+						} else {
                             motion.strideLength = motion.selWalk.calibration.strideLengthForwards;
+						}
                     }
                     return;
             }
@@ -299,26 +298,30 @@ state = (function () {
 Transition = function(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) {
 
     this.lastAnim = lastAnimation; // name of last animation
+    this.nextAnimation = nextAnimation; // name of next animation
     if (lastAnimation === motion.selWalk ||
         nextAnimation === motion.selSideStepLeft ||
-        nextAnimation === motion.selSideStepRight)
-        this.walkingAtStart = true; // boolean - is the last animation a walking animation?
-    else
-        this.walkingAtStart = false; // boolean - is the last animation a walking animation?
-    this.nextAnimation = nextAnimation; // name of next animation
+        nextAnimation === motion.selSideStepRight) {
+		// boolean - is the last animation a walking animation?
+        this.walkingAtStart = true;
+	} else {
+        this.walkingAtStart = false;
+	}
     if (nextAnimation === motion.selWalk ||
         nextAnimation === motion.selSideStepLeft ||
-        nextAnimation === motion.selSideStepRight)
-        this.walkingAtEnd = true; // boolean - is the next animation a walking animation?
-    else
-        this.walkingAtEnd = false; // boolean - is the next animation a walking animation?
+        nextAnimation === motion.selSideStepRight) {
+		// boolean - is the next animation a walking animation?
+        this.walkingAtEnd = true;
+	} else {
+        this.walkingAtEnd = false;
+	}
     this.reachPoses = reachPoses; // placeholder / stub: array of reach poses for squash and stretch techniques
     this.transitionDuration = transitionDuration; // length of transition (seconds)
     this.easingLower = easingLower; // Bezier curve handle (normalised)
     this.easingUpper = easingUpper; // Bezier curve handle (normalised)
     this.startTime = new Date().getTime(); // Starting timestamp (seconds)
     this.progress = 0; // how far are we through the transition?
-    this.walkWheelIncrement = 6; // how much to turn the walkwheel each frame when transitioning to / from walking
+    this.walkWheelIncrement = 3; // how much to turn the walkwheel each frame when transitioning to / from walking
     this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition
     this.walkStopAngle = 0; // what angle should we stop the walk cycle?
 
diff --git a/examples/libraries/walkFilters.js b/examples/libraries/walkFilters.js
index 608618e580..d587aea447 100644
--- a/examples/libraries/walkFilters.js
+++ b/examples/libraries/walkFilters.js
@@ -17,7 +17,6 @@ AveragingFilter = function(length) {
     this.pastValues = [];
 
     for(var i = 0; i < length; i++) {
-
         this.pastValues.push(0);
     }
 
@@ -26,17 +25,14 @@ AveragingFilter = function(length) {
 
         if (this.pastValues.length === 0 && arguments[0]) {
             return arguments[0];
-        }
-        else if (arguments[0]) {
-
+        } else if (arguments[0]) {
             // apply quick and simple LP filtering
             this.pastValues.push(arguments[0]);
             this.pastValues.shift();
             var nextOutputValue = 0;
             for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea];
             return nextOutputValue / this.pastValues.length;
-        }
-        else return 0;
+        } else return 0;
     };
 };
 
@@ -44,15 +40,10 @@ AveragingFilter = function(length) {
 // provides LP filtering with a more stable frequency / phase response
 ButterworthFilter = function(cutOff) {
 
-    switch(cutOff) {
-
-        case 5:
-
-            this.gain = 20.20612010;
-            this.coeffOne = -0.4775922501;
-            this.coeffTwo = 1.2796324250;
-            break;
-    }
+	// cut off frequency = 5Hz
+	this.gain = 20.20612010;
+	this.coeffOne = -0.4775922501;
+	this.coeffTwo = 1.2796324250;
 
     // initialise the arrays
     this.xv = [];
@@ -95,39 +86,42 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
         var harmonics = 0;
         var multiplier = 0;
         var iterations = this.numHarmonics * 2 + 2;
-        if (this.waveShape === TRIANGLE) iterations++;
+        if (this.waveShape === TRIANGLE) {
+			iterations++;
+		}
 
         for(var n = 2; n < iterations; n++) {
 
             switch(this.waveShape) {
 
-                case SAWTOOTH:
+                case SAWTOOTH: {
 
                     multiplier = 1 / n;
                     harmonics += multiplier * Math.sin(n * frequency);
                     break;
+				}
 
-                case TRIANGLE:
+                case TRIANGLE: {
 
                     if (n % 2 === 1) {
-
                         var mulitplier = 1 / (n * n);
-
-                        // multiply every (4n-1)th harmonic by -1
-                        if (n === 3 || n === 7 || n === 11 || n === 15)
+                        // multiply (4n-1)th harmonics by -1
+                        if (n === 3 || n === 7 || n === 11 || n === 15) {
                             mulitplier *= -1;
+						}
                         harmonics += mulitplier * Math.sin(n * frequency);
                     }
                     break;
+				}
 
-                case SQUARE:
+                case SQUARE: {
 
                     if (n % 2 === 1) {
-
                         multiplier = 1 / n;
                         harmonics += multiplier * Math.sin(n * frequency);
                     }
                     break;
+				}
             }
         }
 
@@ -216,13 +210,13 @@ filter = (function() {
             return pos;
         },
 
-        // simple clipping filter (faster way to make square waveforms)
+        // simple clipping filter (clips bottom of wave only, special case for hips y-axis skeleton offset)
         clipTrough: function(inputValue, peak, strength) {
 
             var outputValue = inputValue * strength;
-            if (outputValue < -peak)
+            if (outputValue < -peak) {
                 outputValue = -peak;
-
+			}
             return outputValue;
         }
     }
diff --git a/examples/libraries/walkInterface.js b/examples/libraries/walkInterface.js
index 71e4462933..76b28498d8 100644
--- a/examples/libraries/walkInterface.js
+++ b/examples/libraries/walkInterface.js
@@ -1363,22 +1363,20 @@ walkInterface = (function() {
 
     function setBackground(backgroundID) {
         for (var i in _backgroundOverlays) {
-            if (_backgroundOverlays[i] === backgroundID)
+            if (_backgroundOverlays[i] === backgroundID) {
                 Overlays.editOverlay(_backgroundOverlays[i], {
                     visible: true
                 });
-            else Overlays.editOverlay(_backgroundOverlays[i], {
-                visible: false
-            });
+			} else {
+				Overlays.editOverlay(_backgroundOverlays[i], { visible: false });
+			}
         }
     };
 
     // top row menu type buttons (on | walk | stand | fly | hide)
     function hideMenuButtons() {
         for (var i in _buttonOverlays) {
-            Overlays.editOverlay(_buttonOverlays[i], {
-                visible: false
-            });
+            Overlays.editOverlay(_buttonOverlays[i], { visible: false });
         }
     };
 
@@ -1401,9 +1399,7 @@ walkInterface = (function() {
     function setButtonOverlayVisible(buttonOverlayName) {
         for (var i in _buttonOverlays) {
             if (_buttonOverlays[i] === buttonOverlayName) {
-                Overlays.editOverlay(buttonOverlayName, {
-                    visible: true
-                });
+                Overlays.editOverlay(buttonOverlayName, { visible: true });
             }
         }
     };
@@ -1484,13 +1480,13 @@ walkInterface = (function() {
             });
         }
 
-        if (!showButtons) return;
+        if (!showButtons) {
+			return;
+		}
 
         // set all the non-selected ones to showing
         for (var i = 8; i < _bigbuttonOverlays.length; i += 2) {
-            Overlays.editOverlay(_bigbuttonOverlays[i], {
-                visible: true
-            });
+            Overlays.editOverlay(_bigbuttonOverlays[i], { visible: true });
         }
 
         // set the currently selected one
@@ -1877,6 +1873,7 @@ walkInterface = (function() {
                 _motion.curJointIndex = 4;
                 initialiseJointsEditingPanel();
                 return;
+
             } else if (clickX > 78 && clickX < 121 && clickY > 111 && clickY < 128) {
                 _motion.curJointIndex = 5;
                 initialiseJointsEditingPanel();
@@ -1962,7 +1959,7 @@ walkInterface = (function() {
                 return;
 
             case _hideButton:
-                _hideButtonSelected:
+            case _hideButtonSelected:
 
                     Overlays.editOverlay(_hideButton, {
                         visible: false
@@ -2084,8 +2081,11 @@ walkInterface = (function() {
 
             case _standardWalkBigButton:
 
-                if (_motion.avatarGender === FEMALE) _motion.selWalk = _motion.femaleStandardWalk;
-                else _motion.selWalk = _motion.maleStandardWalk;
+                if (_motion.avatarGender === FEMALE) {
+					_motion.selWalk = _motion.femaleStandardWalk;
+				} else {
+					_motion.selWalk = _motion.maleStandardWalk;
+				}
                 _motion.curAnim = _motion.selWalk;
                 initialiseWalkStylesPanel(true);
                 return;
@@ -2095,7 +2095,9 @@ walkInterface = (function() {
                 // toggle forwards / backwards walk display
                 if (_motion.direction === FORWARDS) {
                     _motion.direction = BACKWARDS;
-                } else _motion.direction = FORWARDS;
+                } else {
+					_motion.direction = FORWARDS;
+				}
                 return;
 
             case _sliderOne:
@@ -2240,7 +2242,9 @@ walkInterface = (function() {
 
         // workaround for bug (https://worklist.net/20160)
         if ((event.x > 310 && event.x < 318 && event.y > 1350 && event.y < 1355) ||
-           (event.x > 423 && event.x < 428 && event.y > 1505 && event.y < 1508 )) return;
+           (event.x > 423 && event.x < 428 && event.y > 1505 && event.y < 1508 )) {
+			   return;
+		}
 
         if (_state.currentState === _state.EDIT_WALK_JOINTS ||
             _state.currentState === _state.EDIT_STANDING ||
@@ -2252,8 +2256,11 @@ walkInterface = (function() {
 
             var thumbClickOffsetX = event.x - _minSliderX;
             var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX;
-            if (thumbPositionNormalised < 0) thumbPositionNormalised = 0;
-            if (thumbPositionNormalised > 1) thumbPositionNormalised = 1;
+            if (thumbPositionNormalised < 0) {
+				thumbPositionNormalised = 0;
+			} else if (thumbPositionNormalised > 1) {
+				thumbPositionNormalised = 1;
+			}
             var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range
 
             if (_movingSliderOne) {
@@ -2263,12 +2270,9 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].sway =
                         thumbPositionNormalised * _sliderRanges.joints[0].swayRange;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].pitch =
                         thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].pitchRange;
                 }
@@ -2280,12 +2284,9 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].bob =
                         thumbPositionNormalised * _sliderRanges.joints[0].bobRange;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].yaw =
                         thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].yawRange;
                 }
@@ -2297,12 +2298,9 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].thrust =
                         thumbPositionNormalised * _sliderRanges.joints[0].thrustRange;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].roll =
                         thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].rollRange;
                 }
@@ -2317,11 +2315,8 @@ walkInterface = (function() {
                 var newPhase = 360 * thumbPositionNormalised - 180;
 
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].swayPhase = newPhase;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].pitchPhase = newPhase;
                 }
 
@@ -2335,11 +2330,8 @@ walkInterface = (function() {
                 var newPhase = 360 * thumbPositionNormalised - 180;
 
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].bobPhase = newPhase;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].yawPhase = newPhase;
                 }
 
@@ -2353,11 +2345,8 @@ walkInterface = (function() {
                 var newPhase = 360 * thumbPositionNormalised - 180;
 
                 if (_state.editingTranslation) {
-
                     _motion.curAnim.joints[0].thrustPhase = newPhase;
-
                 } else {
-
                     _motion.curAnim.joints[_motion.curJointIndex].rollPhase = newPhase;
                 }
 
@@ -2368,13 +2357,10 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 * _sliderRanges.joints[0].swayOffsetRange;
                     _motion.curAnim.joints[0].swayOffset = newOffset;
-
                 } else {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 * _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].pitchOffset = newOffset;
@@ -2387,13 +2373,10 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 *_sliderRanges.joints[0].bobOffsetRange;
                     _motion.curAnim.joints[0].bobOffset = newOffset;
-
                 } else {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 * _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].yawOffset = newOffset;
@@ -2406,21 +2389,19 @@ walkInterface = (function() {
                     x: sliderX + _minSliderX
                 });
                 if (_state.editingTranslation) {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 * _sliderRanges.joints[0].thrustOffsetRange;
                     _motion.curAnim.joints[0].thrustOffset = newOffset;
-
                 } else {
-
                     var newOffset = (thumbPositionNormalised - 0.5) *
                                      2 * _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].rollOffset = newOffset;
                 }
             }
 
-        } // end if editing joints
-        else if (_state.currentState === _state.EDIT_WALK_TWEAKS) {
+		// end if editing joints
+
+        } else if (_state.currentState === _state.EDIT_WALK_TWEAKS) {
 
             // sliders for commonly required walk adjustments
             var thumbClickOffsetX = event.x - _minSliderX;
@@ -2430,50 +2411,39 @@ walkInterface = (function() {
             var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range
 
             if (_movingSliderOne) {
-
                 // walk speed
                 Overlays.editOverlay(_sliderOne, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.calibration.frequency = thumbPositionNormalised * MAX_WALK_SPEED;
-
             } else if (_movingSliderTwo) {
-
                 // lean (hips pitch offset)
                 Overlays.editOverlay(_sliderTwo, {
                     x: sliderX + _minSliderX
                 });
                 var newOffset = (thumbPositionNormalised - 0.5) * 2 * _sliderRanges.joints[0].pitchOffsetRange;
                 _motion.curAnim.joints[0].pitchOffset = newOffset;
-
             } else if (_movingSliderThree) {
-
                 // stride (upper legs pitch)
                 Overlays.editOverlay(_sliderThree, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.joints[1].pitch = thumbPositionNormalised * _sliderRanges.joints[1].pitchRange;
-
             } else if (_movingSliderFour) {
-
                 // legs separation (upper legs roll)
                 Overlays.editOverlay(_sliderFour, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.joints[1].rollOffset = (thumbPositionNormalised - 0.5) *
                     2 * _sliderRanges.joints[1].rollOffsetRange;
-
             } else if (_movingSliderFive) {
-
                 // legs forward (lower legs pitch offset)
                 Overlays.editOverlay(_sliderFive, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.joints[1].pitchOffset = (thumbPositionNormalised - 0.5) *
                     2 * _sliderRanges.joints[1].pitchOffsetRange;
-
             } else if (_movingSliderSix) {
-
                 // lower legs splay (lower legs roll offset)
                 Overlays.editOverlay(_sliderSix, {
                     x: sliderX + _minSliderX
@@ -2482,25 +2452,20 @@ walkInterface = (function() {
                     2 * _sliderRanges.joints[2].rollOffsetRange;
 
             } else if (_movingSliderSeven) {
-
                 // arms forward (upper arms yaw offset)
                 Overlays.editOverlay(_sliderSeven, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.joints[9].yawOffset = (thumbPositionNormalised - 0.5) *
                     2 * _sliderRanges.joints[9].yawOffsetRange;
-
             } else if (_movingSliderEight) {
-
                 // arms out (upper arm pitch offset)
                 Overlays.editOverlay(_sliderEight, {
                     x: sliderX + _minSliderX
                 });
                 _motion.curAnim.joints[9].pitchOffset = (thumbPositionNormalised - 0.5) *
                     -2 * _sliderRanges.joints[9].pitchOffsetRange;
-
             } else if (_movingSliderNine) {
-
                 // lower arms splay (lower arm pitch offset)
                 Overlays.editOverlay(_sliderNine, {
                     x: sliderX + _minSliderX
@@ -2508,21 +2473,30 @@ walkInterface = (function() {
                 _motion.curAnim.joints[10].pitchOffset = (thumbPositionNormalised - 0.5) *
                     -2 * _sliderRanges.joints[10].pitchOffsetRange;
             }
-
         } // if tweaking
     };
 
     function mouseReleaseEvent(event) {
 
-        if (_movingSliderOne) _movingSliderOne = false;
-        else if (_movingSliderTwo) _movingSliderTwo = false;
-        else if (_movingSliderThree) _movingSliderThree = false;
-        else if (_movingSliderFour) _movingSliderFour = false;
-        else if (_movingSliderFive) _movingSliderFive = false;
-        else if (_movingSliderSix) _movingSliderSix = false;
-        else if (_movingSliderSeven) _movingSliderSeven = false;
-        else if (_movingSliderEight) _movingSliderEight = false;
-        else if (_movingSliderNine) _movingSliderNine = false;
+        if (_movingSliderOne) {
+			_movingSliderOne = false;
+		} else if (_movingSliderTwo) {
+			_movingSliderTwo = false;
+		} else if (_movingSliderThree) {
+			_movingSliderThree = false;
+		} else if (_movingSliderFour) {
+			_movingSliderFour = false;
+		} else if (_movingSliderFive) {
+			_movingSliderFive = false;
+		} else if (_movingSliderSix) {
+			_movingSliderSix = false;
+		} else if (_movingSliderSeven) {
+			_movingSliderSeven = false;
+		} else if (_movingSliderEight) {
+			_movingSliderEight = false;
+		} else if (_movingSliderNine) {
+			_movingSliderNine = false;
+		}
     };
 
     Controller.mousePressEvent.connect(mousePressEvent);
@@ -2575,7 +2549,7 @@ walkInterface = (function() {
 
                     case _state.EDIT_WALK_STYLES:
                     case _state.EDIT_WALK_TWEAKS:
-                    case _state.EDIT_WALK_JOINTS:
+                    case _state.EDIT_WALK_JOINTS: {
 
                         hideMenuButtons();
                         initialiseFrontPanel(false);
@@ -2604,10 +2578,11 @@ walkInterface = (function() {
 
                         } else if (_state.currentState === _state.EDIT_WALK_JOINTS) {
 
-                            if (_state.editingTranslation)
+                            if (_state.editingTranslation) {
                                 setBackground(_controlsBackgroundWalkEditHipTrans);
-                            else
+							} else {
                                 setBackground(_controlsBackgroundWalkEditJoints);
+							}
 
                             initialiseWalkStylesPanel(false);
                             setSliderThumbsVisible(true);
@@ -2619,15 +2594,17 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
+					}
 
                     case _state.EDIT_STANDING:
                     case _state.EDIT_SIDESTEP_LEFT:
-                    case _state.EDIT_SIDESTEP_RIGHT:
+                    case _state.EDIT_SIDESTEP_RIGHT: {
 
-                        if (_state.editingTranslation)
+                        if (_state.editingTranslation) {
                             setBackground(_controlsBackgroundWalkEditHipTrans);
-                        else
+						} else {
                             setBackground(_controlsBackgroundWalkEditJoints);
+						}
                         hideMenuButtons();
                         initialiseWalkStylesPanel(false);
                         initialiseFrontPanel(false);
@@ -2654,10 +2631,11 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
+					}
 
                     case _state.EDIT_FLYING:
                     case _state.EDIT_FLYING_UP:
-                    case _state.EDIT_FLYING_DOWN:
+                    case _state.EDIT_FLYING_DOWN: {
 
                         setBackground(_controlsBackgroundWalkEditJoints);
                         hideMenuButtons();
@@ -2685,18 +2663,22 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
+					}
 
                     case _state.STANDING:
                     case _state.WALKING:
                     case _state.FLYING:
                     case _state.SIDE_STEP:
-                    default:
+                    default: {
 
                         hideMenuButtons();
                         hideJointSelectors();
                         setBackground(_controlsBackground);
-                        if (_state.powerOn) setButtonOverlayVisible(_onButton);
-                        else setButtonOverlayVisible(_offButton);
+                        if (_state.powerOn) {
+							setButtonOverlayVisible(_onButton);
+						} else {
+							setButtonOverlayVisible(_offButton);
+						}
                         setButtonOverlayVisible(_configWalkButton);
                         setButtonOverlayVisible(_configStandButton);
                         setButtonOverlayVisible(_configFlyingButton);
@@ -2705,9 +2687,9 @@ walkInterface = (function() {
                         initialiseFrontPanel(true);
                         initialiseWalkStylesPanel(false);
                         return;
+					}
                 }
             }
         }
     }; // end public methods (return)
-
 })();
\ No newline at end of file
diff --git a/examples/walk.js b/examples/walk.js
index 1e1cd7748b..279516aa2a 100644
--- a/examples/walk.js
+++ b/examples/walk.js
@@ -14,7 +14,7 @@
 // constants
 var MALE = 1;
 var FEMALE = 2;
-var MAX_WALK_SPEED = 2.5;
+var MAX_WALK_SPEED = 2.5;//3.919;
 var TAKE_FLIGHT_SPEED = 4.55;
 var TOP_SPEED = 300;
 var UP = 1;
@@ -24,7 +24,7 @@ var RIGHT = 8;
 var FORWARDS = 16;
 var BACKWARDS = 32;
 
-// ovelay images location
+// location of animation files and overlay images
 var pathToAssets = 'http://s3.amazonaws.com/hifi-public/WalkScript/';
 
 // load the UI
@@ -51,7 +51,7 @@ var SAWTOOTH = 1;
 var TRIANGLE = 2;
 var SQUARE = 4;
 
-// filters for synthesising more complex, natural waveforms
+// various filters for synthesising more complex, natural waveforms
 var leanPitchFilter = filter.createAveragingFilter(15);
 var leanRollFilter = filter.createAveragingFilter(15);
 var hipsYawShaper = filter.createWaveSynth(TRIANGLE, 3, 2);
@@ -71,56 +71,65 @@ Script.update.connect(function(deltaTime) {
 		// check for editing modes first, as these require no positioning calculations
 		switch (state.currentState) {
 
-			case state.EDIT_WALK_STYLES:
+			case state.EDIT_WALK_STYLES: {
 				motion.curAnim = motion.selWalk;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_WALK_TWEAKS:
+			case state.EDIT_WALK_TWEAKS: {
 				motion.curAnim = motion.selWalk;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_WALK_JOINTS:
+			case state.EDIT_WALK_JOINTS: {
 				motion.curAnim = motion.selWalk;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_STANDING:
+			case state.EDIT_STANDING: {
 				motion.curAnim = motion.selStand;
 				motion.direction = FORWARDS;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_SIDESTEP_LEFT:
+			case state.EDIT_SIDESTEP_LEFT: {
 				motion.curAnim = motion.selSideStepLeft;
 				motion.direction = LEFT;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_SIDESTEP_RIGHT:
+			case state.EDIT_SIDESTEP_RIGHT: {
 				motion.curAnim = motion.selSideStepRight;
 				motion.direction = RIGHT;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_FLYING:
+			case state.EDIT_FLYING: {
 				motion.curAnim = motion.selFly;
 				motion.direction = FORWARDS;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_FLYING_UP:
+			case state.EDIT_FLYING_UP: {
 				motion.curAnim = motion.selFlyUp;
 				motion.direction = UP;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
-			case state.EDIT_FLYING_DOWN:
+			case state.EDIT_FLYING_DOWN: {
 				motion.curAnim = motion.selFlyDown;
 				motion.direction = DOWN;
 				animateAvatar(deltaTime, speed);
 				break;
+			}
 
 			default:
 				break;
@@ -137,60 +146,78 @@ Script.update.connect(function(deltaTime) {
 			return;
 		}
 		var localVelocity = {x: 0, y: 0, z: 0};
-		if (speed > 0)
+		if (speed > 0) {
 			localVelocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), velocity);
+		}
 
 		if (!state.editing) {
 
 			// determine the candidate animation state
 			var actionToTake = undefined;
-			if (speed < 0.05) actionToTake = state.STANDING; // as per MIN_AVATAR_SPEED, MyAvatar.cpp
-			else if (speed < TAKE_FLIGHT_SPEED) actionToTake = state.WALKING;
-			else if (speed >= TAKE_FLIGHT_SPEED) actionToTake = state.FLYING;
+			if (speed < 0.05) {
+				actionToTake = state.STANDING;
+			} else if (speed < TAKE_FLIGHT_SPEED) {
+				actionToTake = state.WALKING;
+			} else if (speed >= TAKE_FLIGHT_SPEED) {
+				actionToTake = state.FLYING;
+			}
 
 			// determine the principle direction
 			if (Math.abs(localVelocity.x) > Math.abs(localVelocity.y) &&
 				Math.abs(localVelocity.x) > Math.abs(localVelocity.z)) {
 
-				if (localVelocity.x < 0) motion.direction = LEFT;
-				else motion.direction = RIGHT;
+				if (localVelocity.x < 0) {
+					motion.direction = LEFT;
+				} else {
+					motion.direction = RIGHT;
+				}
 
 			} else if (Math.abs(localVelocity.y) > Math.abs(localVelocity.x) &&
 					   Math.abs(localVelocity.y) > Math.abs(localVelocity.z)) {
 
-				if (localVelocity.y > 0) motion.direction = UP;
-				else motion.direction = DOWN;
+				if (localVelocity.y > 0) {
+					motion.direction = UP;
+				} else {
+					motion.direction = DOWN;
+				}
 
 			} else if (Math.abs(localVelocity.z) > Math.abs(localVelocity.x) &&
 					   Math.abs(localVelocity.z) > Math.abs(localVelocity.y)) {
 
-				if (localVelocity.z < 0) motion.direction = FORWARDS;
-				else motion.direction = BACKWARDS;
+				if (localVelocity.z < 0) {
+					motion.direction = FORWARDS;
+				} else {
+					motion.direction = BACKWARDS;
+				}
 			}
 
 			// maybe at walking speed, but sideways?
 			if (actionToTake === state.WALKING &&
-				(motion.direction === LEFT ||
-					motion.direction === RIGHT))
-						actionToTake = state.SIDE_STEP;
+			   (motion.direction === LEFT ||
+				motion.direction === RIGHT)) {
+					actionToTake = state.SIDE_STEP;
+			}
 
 			// maybe at walking speed, but flying up or down?
 			if (actionToTake === state.WALKING &&
-				(motion.direction === UP))// ||
-					//motion.direction === DOWN))
-						actionToTake = state.FLYING;
+			   (motion.direction === UP ||
+			    motion.direction === DOWN)) {
+					actionToTake = state.FLYING;
+			}
 
 			// select appropriate animation and initiate Transition if required
+			// note: The transitions are not compete, and are the most likely
+			// candidate for the next worklist item
 			switch (actionToTake) {
 
-				case state.STANDING:
+				case state.STANDING: {
 
 					// do we need to change state?
 					if (state.currentState !== state.STANDING) {
 
 						switch (motion.curAnim) {
 
-							case motion.selWalk:
+							case motion.selWalk: {
 
 								// Walking to standing
 								motion.curTransition = new Transition(
@@ -200,57 +227,60 @@ Script.update.connect(function(deltaTime) {
 									{x: 0.1, y: 0.5},
 									{x: -0.25, y: 1.22});
 								break;
+							}
 
-							case motion.selSideStepLeft:
-							case motion.selSideStepRight:
+							case motion.selFly:
+							case motion.selFlyUp:
+							case motion.selFlyDown: {
 
-								break;
-
-							default:
-
-								// flying to standing
+								// Flying to Standing
 								motion.curTransition = new Transition(
 									motion.curAnim,
 									motion.selStand,
-									[], 0.25,
+									[], 0.5,
 									{x: 0.5, y: 0.08},
 									{x: 0.28, y: 1});
 								break;
+							}
+
+							default:
+
+								break;
 						}
 						state.setInternalState(state.STANDING);
 						motion.curAnim = motion.selStand;
 					}
 					animateAvatar(deltaTime, speed);
 					break;
+				}
 
-				case state.WALKING:
+				case state.WALKING: {
 
 					if (state.currentState !== state.WALKING) {
 
-						if (motion.direction === BACKWARDS)
-								 motion.walkWheelPos = motion.selWalk.calibration.startAngleBackwards;
-
-						else motion.walkWheelPos = motion.selWalk.calibration.startAngleForwards;
+						if (motion.direction === BACKWARDS) {
+							motion.walkWheelPos = motion.selWalk.calibration.startAngleBackwards;
+						} else {
+							motion.walkWheelPos = motion.selWalk.calibration.startAngleForwards;
+						}
 
 						switch (motion.curAnim) {
 
-							case motion.selStand:
+							case motion.selStand: {
 
 								// Standing to Walking
 								motion.curTransition = new Transition(
 									motion.curAnim,
 									motion.selWalk,
-									[], 0.1,
-									{x: 0.5, y: 0.1},
-									{x: 0.5, y: 0.9});
+									[], 0.25,
+									{x: 0.5, y: 0.5},
+									{x: 0.5, y: 0.5});
 								break;
+							}
 
-							case motion.selSideStepLeft:
-							case motion.selSideStepRight:
-
-								break;
-
-							default:
+							case motion.selFly:
+							case motion.selFlyUp:
+							case motion.selFlyDown: {
 
 								// Flying to Walking
 								motion.curTransition = new Transition(
@@ -260,69 +290,56 @@ Script.update.connect(function(deltaTime) {
 									{x: 0.24, y: 0.03},
 									{x: 0.42, y: 1.0});
 								break;
+							}
+
+							default:
+
+								break;
 						}
 						state.setInternalState(state.WALKING);
 					}
 					motion.curAnim = motion.selWalk;
 					animateAvatar(deltaTime, speed);
 					break;
+				}
 
-				case state.SIDE_STEP:
+				case state.SIDE_STEP: {
 
 					var selSideStep = 0;
 					if (motion.direction === LEFT) {
 
-						if (motion.lastDirection !== LEFT)
+						if (motion.lastDirection !== LEFT) {
 							motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart;
+						}
 						selSideStep = motion.selSideStepLeft;
 
 					} else {
 
-						if (motion.lastDirection !== RIGHT)
+						if (motion.lastDirection !== RIGHT) {
 							motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart;
+						}
 						selSideStep = motion.selSideStepRight;
 					}
 
 					if (state.currentState !== state.SIDE_STEP) {
 
 						if (motion.direction === LEFT) {
-
 							motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart;
-							switch (motion.curAnim) {
-
-								case motion.selStand:
-
-									break;
-
-								default:
-
-									break;
-							}
-
 						} else {
-
 							motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart;
-							switch (motion.curAnim) {
-
-								case motion.selStand:
-
-									break;
-
-								default:
-
-									break;
-							}
 						}
 						state.setInternalState(state.SIDE_STEP);
 					}
 					motion.curAnim = selSideStep;
 					animateAvatar(deltaTime, speed);
 					break;
+				}
 
-				case state.FLYING:
+				case state.FLYING: {
 
-					if (state.currentState !== state.FLYING)
+					if (state.currentState !== state.FLYING) {
 						state.setInternalState(state.FLYING);
+					}
 
 					// change animation for flying directly up or down
 					if (motion.direction === UP) {
@@ -332,23 +349,21 @@ Script.update.connect(function(deltaTime) {
 							switch (motion.curAnim) {
 
 								case motion.selStand:
-								case motion.selWalk:
+								case motion.selWalk: {
 
 									// standing | walking to flying up
 									motion.curTransition = new Transition(
 										motion.curAnim,
 										motion.selFlyUp,
-										[], 0.25,
+										[], 0.35,
 										{x: 0.5, y: 0.08},
 										{x: 0.28, y: 1});
 									break;
+								}
 
-								case motion.selSideStepLeft:
-								case motion.selSideStepRight:
-
-									break;
-
-								default:
+								case motion.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
 
 									motion.curTransition = new Transition(
 										motion.curAnim,
@@ -357,6 +372,11 @@ Script.update.connect(function(deltaTime) {
 										{x: 0.5, y: 0.08},
 										{x: 0.28, y: 1});
 									break;
+								}
+
+								default:
+
+									break;
 							}
 							motion.curAnim = motion.selFlyUp;
 						}
@@ -368,7 +388,7 @@ Script.update.connect(function(deltaTime) {
 							switch (motion.curAnim) {
 
 								case motion.selStand:
-								case motion.selWalk:
+								case motion.selWalk: {
 
 									motion.curTransition = new Transition(
 										motion.curAnim,
@@ -377,21 +397,24 @@ Script.update.connect(function(deltaTime) {
 										{x: 0.5, y: 0.08},
 										{x: 0.28, y: 1});
 									break;
+								}
 
-								case motion.selSideStepLeft:
-								case motion.selSideStepRight:
-
-									break;
-
-								default:
+								case motion.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
 
 									motion.curTransition = new Transition(
 										motion.curAnim,
 										motion.selFlyDown,
-										[], 0.5,
+										[], 0.45,
 										{x: 0.5, y: 0.08},
 										{x: 0.28, y: 1});
 									break;
+								}
+
+								default:
+
+									break;
 							}
 							motion.curAnim = motion.selFlyDown;
 						}
@@ -403,7 +426,7 @@ Script.update.connect(function(deltaTime) {
 							switch (motion.curAnim) {
 
 								case motion.selStand:
-								case motion.selWalk:
+								case motion.selWalk: {
 
 									motion.curTransition = new Transition(
 										motion.curAnim,
@@ -412,13 +435,11 @@ Script.update.connect(function(deltaTime) {
 										{x: 1.44, y:0.24},
 										{x: 0.61, y:0.92});
 									break;
+								}
 
-								case motion.selSideStepLeft:
-								case motion.selSideStepRight:
-
-									break;
-
-								default:
+								case motion.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
 
 									motion.curTransition = new Transition(
 										motion.curAnim,
@@ -427,12 +448,18 @@ Script.update.connect(function(deltaTime) {
 										{x: 0.5, y: 0.08},
 										{x: 0.28, y: 1});
 									break;
+								}
+
+								default:
+
+									break;
 							}
 							motion.curAnim = motion.selFly;
 						}
 					}
 					animateAvatar(deltaTime, speed);
 					break;
+				}// end case state.FLYING
 
 			} // end switch(actionToTake)
 
@@ -447,17 +474,20 @@ Script.update.connect(function(deltaTime) {
 // the faster we go, the further we lean forward. the angle is calcualted here
 function getLeanPitch(speed) {
 
-	if (speed > TOP_SPEED) speed = TOP_SPEED;
+	if (speed > TOP_SPEED) {
+		speed = TOP_SPEED;
+	}
 	var leanProgress = speed / TOP_SPEED;
 
 	if (motion.direction === LEFT ||
-	   motion.direction === RIGHT)
+	   motion.direction === RIGHT) {
 		leanProgress = 0;
-
-	else {
+	} else {
 
 		var responseSharpness = 1.5;
-		if (motion.direction == BACKWARDS) responseSharpness = 3.0;
+		if (motion.direction == BACKWARDS) {
+			responseSharpness = 3.0;
+		}
 
 		leanProgress = filter.bezier((1 - leanProgress),
 										{x: 0, y: 0.0},
@@ -466,10 +496,11 @@ function getLeanPitch(speed) {
 										{x: 1, y: 1}).y;
 
 		// determine final pitch and adjust for direction of momentum
-		if (motion.direction === BACKWARDS)
+		if (motion.direction === BACKWARDS) {
 			leanProgress = -motion.motionPitchMax * leanProgress;
-		else
+		} else {
 			leanProgress = motion.motionPitchMax * leanProgress;
+		}
 	}
 
 	// return the smoothed response
@@ -480,19 +511,26 @@ function getLeanPitch(speed) {
 function getLeanRoll(deltaTime, speed) {
 
 	var leanRollProgress = 0;
-	if (speed > TOP_SPEED) speed = TOP_SPEED;
+	if (speed > TOP_SPEED) {
+		speed = TOP_SPEED;
+	}
 
 	// what's our our anglular velocity?
 	var angularVelocityMax = 70; // from observation
 	var angularVelocity = filter.radToDeg(MyAvatar.getAngularVelocity().y);
-	if (angularVelocity > angularVelocityMax) angularVelocity = angularVelocityMax;
-	if (angularVelocity < -angularVelocityMax) angularVelocity = -angularVelocityMax;
+	if (angularVelocity > angularVelocityMax) {
+		angularVelocity = angularVelocityMax;
+	}
+	if (angularVelocity < -angularVelocityMax) {
+		angularVelocity = -angularVelocityMax;
+	}
 
 	leanRollProgress = speed / TOP_SPEED;
 
 	if (motion.direction !== LEFT &&
-	   motion.direction !== RIGHT)
+	    motion.direction !== RIGHT) {
 			leanRollProgress *= (Math.abs(angularVelocity) / angularVelocityMax);
+	}
 
 	// apply our response curve
 	leanRollProgress = filter.bezier((1 - leanRollProgress),
@@ -502,14 +540,17 @@ function getLeanRoll(deltaTime, speed) {
 											   {x: 1, y: 1}).y;
 	// which way to lean?
 	var turnSign = -1;
-	if (angularVelocity < 0.001) turnSign = 1;
+	if (angularVelocity < 0.001) {
+		turnSign = 1;
+	}
 	if (motion.direction === BACKWARDS ||
-	   motion.direction === LEFT)
+	    motion.direction === LEFT) {
 		turnSign *= -1;
-
+	}
 	if (motion.direction === LEFT ||
-	   motion.direction === RIGHT)
+	    motion.direction === RIGHT) {
 	   		leanRollProgress *= 2;
+	}
 
 	// add damping with simple averaging filter
 	leanRollProgress = leanRollFilter.process(turnSign * leanRollProgress);
@@ -520,12 +561,13 @@ function playFootstep(side) {
 
 	var options = new AudioInjectionOptions();
 	options.position = Camera.getPosition();
-	options.volume = 0.2;
+	options.volume = 0.3;
 	var soundNumber = 2; // 0 to 2
-	if (side === RIGHT && motion.makesFootStepSounds)
+	if (side === RIGHT && motion.makesFootStepSounds) {
 		Audio.playSound(walkAssets.footsteps[soundNumber + 1], options);
-	else if (side === LEFT && motion.makesFootStepSounds)
+	} else if (side === LEFT && motion.makesFootStepSounds) {
 		Audio.playSound(walkAssets.footsteps[soundNumber], options);
+	}
 }
 
 // animate the avatar using sine wave generators. inspired by Victorian clockwork dolls
@@ -545,13 +587,15 @@ function animateAvatar(deltaTime, speed) {
 
 	// don't lean into the direction of travel if going up
 	var leanMod = 1;
-	if (motion.direction === UP)
+	if (motion.direction === UP) {
 		leanMod = 0;
+	}
 
 	// adjust leaning direction for flying
 	var flyingModifier = 1;
-	if (state.currentState.FLYING)
+	if (state.currentState.FLYING) {
 		flyingModifier = -1;
+	}
 
 	if (motion.curTransition !== nullTransition) {
 
@@ -573,20 +617,22 @@ function animateAvatar(deltaTime, speed) {
 				// find the closest stop point from the walk wheel's angle
 				var angleToLeftStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - leftStop) - 180);
 				var angleToRightStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - rightStop) - 180);
-				if (motion.walkWheelPos > angleToLeftStop) angleToLeftStop = 360 - angleToLeftStop;
-				if (motion.walkWheelPos > angleToRightStop) angleToRightStop = 360 - angleToRightStop;
-
+				if (motion.walkWheelPos > angleToLeftStop) {
+					angleToLeftStop = 360 - angleToLeftStop;
+				}
+				if (motion.walkWheelPos > angleToRightStop) {
+					angleToRightStop = 360 - angleToRightStop;
+				}
 
 				motion.curTransition.walkWheelIncrement = 3;
 
 				// keep the walkwheel turning by setting the walkWheelIncrement
 				// until our feet are tucked nicely underneath us.
-				if (angleToLeftStop < angleToRightStop)
-
+				if (angleToLeftStop < angleToRightStop) {
 					motion.curTransition.walkStopAngle = leftStop;
-
-				else
+				} else {
 					motion.curTransition.walkStopAngle = rightStop;
+				}
 
 			} else {
 
@@ -622,11 +668,9 @@ function animateAvatar(deltaTime, speed) {
 						motion.curTransition.walkWheelIncrement = 0;
 					}
 					// keep turning walk wheel until both feet are below the avi
-
 					motion.advanceWalkWheel(motion.curTransition.walkWheelIncrement);
-					//motion.curTransition.walkWheelAdvance += motion.curTransition.walkWheelIncrement;
-				}
-				else motion.curTransition.walkWheelIncrement = 0; // sidestep
+
+				} else motion.curTransition.walkWheelIncrement = 0; // sidestep
 			}
 		} } // end motion.curTransition !== nullTransition
 
@@ -636,8 +680,9 @@ function animateAvatar(deltaTime, speed) {
 
 		// if the timing's right, take a snapshot of the stride max and recalibrate
 		var strideMaxAt = motion.curAnim.calibration.forwardStrideMaxAt;
-		if (motion.direction === BACKWARDS)
+		if (motion.direction === BACKWARDS) {
 			strideMaxAt = motion.curAnim.calibration.backwardsStrideMaxAt;
+		}
 
 		var tolerance = 1.0;
 		if (motion.walkWheelPos < (strideMaxAt + tolerance) &&
@@ -648,18 +693,20 @@ function animateAvatar(deltaTime, speed) {
 			var footLPos = MyAvatar.getJointPosition("LeftFoot");
 			motion.strideLength = Vec3.distance(footRPos, footLPos);
 
-			if (motion.direction === FORWARDS)
+			if (motion.direction === FORWARDS) {
 				motion.curAnim.calibration.strideLengthForwards = motion.strideLength;
-			else if (motion.direction === BACKWARDS)
+			} else if (motion.direction === BACKWARDS) {
 				motion.curAnim.calibration.strideLengthBackwards = motion.strideLength;
+			}
 
 		} else {
 
 			// use the saved value for stride length
-			if (motion.direction === FORWARDS)
+			if (motion.direction === FORWARDS) {
 				motion.strideLength = motion.curAnim.calibration.strideLengthForwards;
-			else if (motion.direction === BACKWARDS)
+			} else if (motion.direction === BACKWARDS) {
 				motion.strideLength = motion.curAnim.calibration.strideLengthBackwards;
+			}
 		}
 	} // end get walk stride length
 
@@ -714,8 +761,9 @@ function animateAvatar(deltaTime, speed) {
 
 		// if we are in an edit mode, we will need fake time to turn the wheel
 		if (state.currentState !== state.WALKING &&
-			state.currentState !== state.SIDE_STEP)
+			state.currentState !== state.SIDE_STEP) {
 			degreesTurnedSinceLastFrame = motion.curAnim.calibration.frequency / 70;
+		}
 
 		// advance the walk wheel the appropriate amount
 		motion.advanceWalkWheel(degreesTurnedSinceLastFrame);
@@ -758,12 +806,6 @@ function animateAvatar(deltaTime, speed) {
 	var sideStepFootPitchModifier = 1;
 	var sideStepHandPitchSign = 1;
 
-	// The below code should probably be optimised into some sort of loop, where
-	// the joints are iterated through. However, this has not been done yet, as there
-	// are still some quite fundamental changes to be made (e.g. turning on the spot
-	// animation and sidestepping transitions) so it's been left as is for ease of
-	// understanding and editing.
-
 	// calculate hips translation
 	if (motion.curTransition !== nullTransition) {
 
@@ -774,7 +816,9 @@ function animateAvatar(deltaTime, speed) {
 					  motion.curAnim.joints[0].swayPhase)) + motion.curAnim.joints[0].swayOffset;
 
 			var bobPhase = motion.curAnim.joints[0].bobPhase;
-			if (motion.direction === motion.BACKWARDS) bobPhase += 90;
+			if (motion.direction === motion.BACKWARDS) {
+				bobPhase += 90;
+			}
 			bobOsc = motion.curAnim.joints[0].bob *
 					 Math.sin(filter.degToRad(motion.cumulativeTime *
 					 motion.curAnim.calibration.frequency + bobPhase)) +
@@ -792,7 +836,9 @@ function animateAvatar(deltaTime, speed) {
 						  motion.curTransition.lastAnim.joints[0].swayOffset;
 
 			var bobPhaseLast = motion.curTransition.lastAnim.joints[0].bobPhase;
-			if (motion.direction === motion.BACKWARDS) bobPhaseLast +=90;
+			if (motion.direction === motion.BACKWARDS) {
+				bobPhaseLast +=90;
+			}
 			bobOscLast = motion.curTransition.lastAnim.joints[0].bob *
 						 Math.sin(filter.degToRad(motion.walkWheelPos + bobPhaseLast));
 			bobOscLast = filter.clipTrough(bobOscLast, motion.curTransition.lastAnim.joints[0].bob , 2);
@@ -804,15 +850,18 @@ function animateAvatar(deltaTime, speed) {
 							motion.curTransition.lastAnim.joints[0].thrustPhase)) +
 							motion.curTransition.lastAnim.joints[0].thrustOffset;
 
-		} // end if walking at start of transition
-		else {
+		// end if walking at start of transition
+
+		} else {
 
 			swayOsc = motion.curAnim.joints[0].sway *
 					  Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) +
 					  motion.curAnim.joints[0].swayOffset;
 
 			var bobPhase = motion.curAnim.joints[0].bobPhase;
-			if (motion.direction === motion.BACKWARDS) bobPhase += 90;
+			if (motion.direction === motion.BACKWARDS) {
+				bobPhase += 90;
+			}
 			bobOsc = motion.curAnim.joints[0].bob *
 					 Math.sin(filter.degToRad(cycle * adjFreq * 2 + bobPhase));
 			if (state.currentState === state.WALKING ||
@@ -854,8 +903,9 @@ function animateAvatar(deltaTime, speed) {
 		bobOsc = (transProgress * bobOsc) + ((1 - transProgress) * bobOscLast);
 		thrustOsc = (transProgress * thrustOsc) + ((1 - transProgress) * thrustOscLast);
 
-	}// if current transition active
-	else {
+		// end if walking at start of transition
+
+		} else {
 
 		swayOsc = motion.curAnim.joints[0].sway *
 				  Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) +

From 0c1ea784b9263f31726b79371476649e25aaabf7 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Mon, 10 Nov 2014 11:49:02 -0800
Subject: [PATCH 18/37] Set face.

---
 interface/src/renderer/Model.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp
index 3137ab8369..8d426d067b 100644
--- a/interface/src/renderer/Model.cpp
+++ b/interface/src/renderer/Model.cpp
@@ -489,6 +489,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
                 if (distanceToSubMesh < bestDistance) {
                     bestDistance = distanceToSubMesh;
                     intersectedSomething = true;
+                    face = subMeshFace;
                     extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
                 }
             }

From 5f89b6e00ea8f48f9b0814d07929a66dc99d61d3 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka <drzej.k@gmail.com>
Date: Mon, 10 Nov 2014 11:58:52 -0800
Subject: [PATCH 19/37] The spec actually says that the 'L' (should have been
 'LL' anyway) isn't necessary; the compiler will know to read the constant as
 a long long.

---
 interface/src/Application.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 0afe011cd6..d54a638239 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -108,7 +108,7 @@ static unsigned STARFIELD_SEED = 1;
 
 static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
 
-const qint64 MAXIMUM_CACHE_SIZE = 10737418240L;  // 10GB
+const qint64 MAXIMUM_CACHE_SIZE = 10737418240;  // 10GB
 
 static QTimer* idleTimer = NULL;
 

From 38a142eeb875c26e4a8ca0d23d0948d04adf4283 Mon Sep 17 00:00:00 2001
From: Philip Rosedale <philip@highfidelity.io>
Date: Mon, 10 Nov 2014 15:23:13 -0800
Subject: [PATCH 20/37] return value by reference

---
 libraries/entities/src/EntityItem.cpp | 1 +
 libraries/entities/src/EntityItem.h   | 6 +++---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index ef02aafbc8..14229575a1 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -491,6 +491,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
         READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation);
         READ_ENTITY_PROPERTY(PROP_MASS, float, _mass);
         READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity);
+        qDebug() << "    readEntityDataFromBuffer() _velocity:" << _velocity << "line:" << __LINE__;
         READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity);
         READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
         READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime);
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index ae44e3931b..adf72198be 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -145,14 +145,14 @@ public:
     float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
 
     /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
-    void setDimensions(const glm::vec3& value) { _dimensions = value; ; recalculateCollisionShape(); }
+    void setDimensions(const glm::vec3& value) { _dimensions = value; recalculateCollisionShape(); }
 
     /// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
     void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
 
     static const glm::quat DEFAULT_ROTATION;
     const glm::quat& getRotation() const { return _rotation; }
-    void setRotation(const glm::quat& rotation) { _rotation = rotation; ; recalculateCollisionShape(); }
+    void setRotation(const glm::quat& rotation) { _rotation = rotation; recalculateCollisionShape(); }
 
     static const float DEFAULT_GLOW_LEVEL;
     float getGlowLevel() const { return _glowLevel; }
@@ -169,7 +169,7 @@ public:
     static const glm::vec3 DEFAULT_VELOCITY;
     static const glm::vec3 NO_VELOCITY;
     static const float EPSILON_VELOCITY_LENGTH;
-    const glm::vec3 getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
+    const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
     glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters
     void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second
     void setVelocityInMeters(const glm::vec3& value) { _velocity = value / (float) TREE_SCALE; } /// velocity in meters

From dabc2b23a395bda91e4114df65072e0b98269e35 Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Mon, 10 Nov 2014 15:51:56 -0800
Subject: [PATCH 21/37] add a guard in case local audio cannot be handled

---
 interface/src/Audio.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 4a488bb296..43ad14aca7 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -1342,8 +1342,11 @@ void Audio::handleAudioByteArray(const QByteArray& audioByteArray, const AudioIn
         QAudioOutput* localSoundOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), localFormat, this);
         
         QIODevice* localIODevice = localSoundOutput->start();
-        qDebug() << "Writing" << audioByteArray.size() << "to" << localIODevice;
-        localIODevice->write(audioByteArray);
+        if (localIODevice) {
+            localIODevice->write(audioByteArray);
+        } else {
+            qDebug() << "Unable to handle audio byte array. Error:" << localSoundOutput->error();
+        }
     } else {
         qDebug() << "Audio::handleAudioByteArray called with an empty byte array. Sound is likely still downloading.";
     }

From 05b361affc1d89bb65558cc66d4db99d93f99390 Mon Sep 17 00:00:00 2001
From: DaveDubUK <DaveDubUK@gmail.com>
Date: Tue, 11 Nov 2014 07:34:18 +0000
Subject: [PATCH 22/37] Formatting changes

---
 examples/libraries/walkApi.js       |  28 +++---
 examples/libraries/walkFilters.js   |  33 +++----
 examples/libraries/walkInterface.js | 139 ++++++++++++++--------------
 3 files changed, 100 insertions(+), 100 deletions(-)

diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js
index 8f9bbfc36c..0175c56e18 100644
--- a/examples/libraries/walkApi.js
+++ b/examples/libraries/walkApi.js
@@ -1,7 +1,7 @@
 //
 //  walkObjects.js
 //
-//  version 1.000
+//  version 1.001
 //
 //  Created by David Wooldridge, Autumn 2014
 //
@@ -93,19 +93,19 @@ Motion = function() {
             if (i > 17 || i < 34) {
                 // left hand fingers
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
-			} else if (i > 33 || i < 38) {
+            } else if (i > 33 || i < 38) {
                 // left hand thumb
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
-			} else if (i > 41 || i < 58) {
+            } else if (i > 41 || i < 58) {
                 // right hand fingers
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0));
-			} else if (i > 57 || i < 62) {
+            } else if (i > 57 || i < 62) {
                 // right hand thumb
                 MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
             } else {
                 // zero out the remaining joints
                 MyAvatar.clearJointData(this.avatarJointNames[i]);
-			}
+            }
         }
     }
 
@@ -123,7 +123,7 @@ Motion = function() {
         this.walkWheelPos += angle;
         if (motion.walkWheelPos >= 360) {
             this.walkWheelPos = this.walkWheelPos % 360;
-		}
+        }
     }
 
     // last frame history
@@ -284,9 +284,9 @@ state = (function () {
                         motion.setGender(MALE);
                         if (motion.direction === BACKWARDS) {
                             motion.strideLength = motion.selWalk.calibration.strideLengthBackwards;
-						} else {
+                        } else {
                             motion.strideLength = motion.selWalk.calibration.strideLengthForwards;
-						}
+                        }
                     }
                     return;
             }
@@ -302,19 +302,19 @@ Transition = function(lastAnimation, nextAnimation, reachPoses, transitionDurati
     if (lastAnimation === motion.selWalk ||
         nextAnimation === motion.selSideStepLeft ||
         nextAnimation === motion.selSideStepRight) {
-		// boolean - is the last animation a walking animation?
+        // boolean - is the last animation a walking animation?
         this.walkingAtStart = true;
-	} else {
+    } else {
         this.walkingAtStart = false;
-	}
+    }
     if (nextAnimation === motion.selWalk ||
         nextAnimation === motion.selSideStepLeft ||
         nextAnimation === motion.selSideStepRight) {
-		// boolean - is the next animation a walking animation?
+        // boolean - is the next animation a walking animation?
         this.walkingAtEnd = true;
-	} else {
+    } else {
         this.walkingAtEnd = false;
-	}
+    }
     this.reachPoses = reachPoses; // placeholder / stub: array of reach poses for squash and stretch techniques
     this.transitionDuration = transitionDuration; // length of transition (seconds)
     this.easingLower = easingLower; // Bezier curve handle (normalised)
diff --git a/examples/libraries/walkFilters.js b/examples/libraries/walkFilters.js
index d587aea447..98a7562bb4 100644
--- a/examples/libraries/walkFilters.js
+++ b/examples/libraries/walkFilters.js
@@ -1,7 +1,7 @@
 //
 //  walkFilters.js
 //
-//  version 1.000
+//  version 1.001
 //
 //  Created by David Wooldridge, Autumn 2014
 //
@@ -32,7 +32,9 @@ AveragingFilter = function(length) {
             var nextOutputValue = 0;
             for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea];
             return nextOutputValue / this.pastValues.length;
-        } else return 0;
+        } else {
+            return 0;
+        }
     };
 };
 
@@ -40,10 +42,10 @@ AveragingFilter = function(length) {
 // provides LP filtering with a more stable frequency / phase response
 ButterworthFilter = function(cutOff) {
 
-	// cut off frequency = 5Hz
-	this.gain = 20.20612010;
-	this.coeffOne = -0.4775922501;
-	this.coeffTwo = 1.2796324250;
+    // cut off frequency = 5Hz
+    this.gain = 20.20612010;
+    this.coeffOne = -0.4775922501;
+    this.coeffTwo = 1.2796324250;
 
     // initialise the arrays
     this.xv = [];
@@ -87,8 +89,8 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
         var multiplier = 0;
         var iterations = this.numHarmonics * 2 + 2;
         if (this.waveShape === TRIANGLE) {
-			iterations++;
-		}
+            iterations++;
+        }
 
         for(var n = 2; n < iterations; n++) {
 
@@ -99,7 +101,7 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
                     multiplier = 1 / n;
                     harmonics += multiplier * Math.sin(n * frequency);
                     break;
-				}
+                }
 
                 case TRIANGLE: {
 
@@ -108,11 +110,11 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
                         // multiply (4n-1)th harmonics by -1
                         if (n === 3 || n === 7 || n === 11 || n === 15) {
                             mulitplier *= -1;
-						}
+                        }
                         harmonics += mulitplier * Math.sin(n * frequency);
                     }
                     break;
-				}
+                }
 
                 case SQUARE: {
 
@@ -121,7 +123,7 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
                         harmonics += multiplier * Math.sin(n * frequency);
                     }
                     break;
-				}
+                }
             }
         }
 
@@ -141,10 +143,9 @@ HarmonicsFilter = function(magnitudes, phaseAngles) {
         var harmonics = 0;
         var numHarmonics = magnitudes.length;
 
-        for(var n = 0; n < numHarmonics; n++)
-
+        for(var n = 0; n < numHarmonics; n++) {
             harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]);
-
+        }
         return harmonics;
     };
 };
@@ -216,7 +217,7 @@ filter = (function() {
             var outputValue = inputValue * strength;
             if (outputValue < -peak) {
                 outputValue = -peak;
-			}
+            }
             return outputValue;
         }
     }
diff --git a/examples/libraries/walkInterface.js b/examples/libraries/walkInterface.js
index 76b28498d8..7b7a23a584 100644
--- a/examples/libraries/walkInterface.js
+++ b/examples/libraries/walkInterface.js
@@ -1,7 +1,7 @@
 //
 //  walkInterface.js
 //
-//  version 1.000
+//  version 1.001
 //
 //  Created by David Wooldridge, Autumn 2014
 //
@@ -1367,9 +1367,9 @@ walkInterface = (function() {
                 Overlays.editOverlay(_backgroundOverlays[i], {
                     visible: true
                 });
-			} else {
-				Overlays.editOverlay(_backgroundOverlays[i], { visible: false });
-			}
+            } else {
+                Overlays.editOverlay(_backgroundOverlays[i], { visible: false });
+            }
         }
     };
 
@@ -1481,8 +1481,8 @@ walkInterface = (function() {
         }
 
         if (!showButtons) {
-			return;
-		}
+            return;
+        }
 
         // set all the non-selected ones to showing
         for (var i = 8; i < _bigbuttonOverlays.length; i += 2) {
@@ -2082,10 +2082,10 @@ walkInterface = (function() {
             case _standardWalkBigButton:
 
                 if (_motion.avatarGender === FEMALE) {
-					_motion.selWalk = _motion.femaleStandardWalk;
-				} else {
-					_motion.selWalk = _motion.maleStandardWalk;
-				}
+                    _motion.selWalk = _motion.femaleStandardWalk;
+                } else {
+                    _motion.selWalk = _motion.maleStandardWalk;
+                }
                 _motion.curAnim = _motion.selWalk;
                 initialiseWalkStylesPanel(true);
                 return;
@@ -2096,8 +2096,8 @@ walkInterface = (function() {
                 if (_motion.direction === FORWARDS) {
                     _motion.direction = BACKWARDS;
                 } else {
-					_motion.direction = FORWARDS;
-				}
+                    _motion.direction = FORWARDS;
+                }
                 return;
 
             case _sliderOne:
@@ -2159,7 +2159,7 @@ walkInterface = (function() {
                 // exit edit modes
                 _motion.curAnim = _motion.selStand;
                 _state.setInternalState(_state.STANDING);
-                break;
+                return;
 
             case _onButton:
 
@@ -2171,8 +2171,7 @@ walkInterface = (function() {
                 Overlays.editOverlay(_onButton, {
                     visible: false
                 });
-                //resetJoints();
-                break;
+                return;
 
             case _backButton:
             case _backButtonSelected:
@@ -2184,57 +2183,57 @@ walkInterface = (function() {
                     visible: false
                 });
                 _state.setInternalState(_state.STANDING);
-                break;
+                return;
 
             case _configWalkStylesButton:
 
                 _state.setInternalState(_state.EDIT_WALK_STYLES);
-                break;
+                return;
 
             case _configWalkTweaksButton:
 
                 _state.setInternalState(_state.EDIT_WALK_TWEAKS);
-                break;
+                return;
 
             case _configWalkJointsButton:
 
                 _state.setInternalState(_state.EDIT_WALK_JOINTS);
-                break;
+                return;
 
             case _configWalkButton:
 
                 _state.setInternalState(_state.EDIT_WALK_STYLES);
-                break;
+                return;
 
             case _configStandButton:
 
                 _state.setInternalState(_state.EDIT_STANDING);
-                break;
+                return;
 
             case _configSideStepLeftButton:
-                _state.setInternalState(_state.EDIT_SIDESTEP_LEFT);
 
-                break;
+                _state.setInternalState(_state.EDIT_SIDESTEP_LEFT);
+                return;
 
             case _configSideStepRightButton:
 
                 _state.setInternalState(_state.EDIT_SIDESTEP_RIGHT);
-                break;
+                return;
 
             case _configFlyingButton:
 
                 _state.setInternalState(_state.EDIT_FLYING);
-                break;
+                return;
 
             case _configFlyingUpButton:
 
                 _state.setInternalState(_state.EDIT_FLYING_UP);
-                break;
+                return;
 
             case _configFlyingDownButton:
 
                 _state.setInternalState(_state.EDIT_FLYING_DOWN);
-                break;
+                return;
         }
     };
 
@@ -2243,8 +2242,8 @@ walkInterface = (function() {
         // workaround for bug (https://worklist.net/20160)
         if ((event.x > 310 && event.x < 318 && event.y > 1350 && event.y < 1355) ||
            (event.x > 423 && event.x < 428 && event.y > 1505 && event.y < 1508 )) {
-			   return;
-		}
+               return;
+        }
 
         if (_state.currentState === _state.EDIT_WALK_JOINTS ||
             _state.currentState === _state.EDIT_STANDING ||
@@ -2257,10 +2256,10 @@ walkInterface = (function() {
             var thumbClickOffsetX = event.x - _minSliderX;
             var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX;
             if (thumbPositionNormalised < 0) {
-				thumbPositionNormalised = 0;
-			} else if (thumbPositionNormalised > 1) {
-				thumbPositionNormalised = 1;
-			}
+                thumbPositionNormalised = 0;
+            } else if (thumbPositionNormalised > 1) {
+                thumbPositionNormalised = 1;
+            }
             var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range
 
             if (_movingSliderOne) {
@@ -2358,11 +2357,11 @@ walkInterface = (function() {
                 });
                 if (_state.editingTranslation) {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 * _sliderRanges.joints[0].swayOffsetRange;
+                        2 * _sliderRanges.joints[0].swayOffsetRange;
                     _motion.curAnim.joints[0].swayOffset = newOffset;
                 } else {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 * _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange;
+                        2 * _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].pitchOffset = newOffset;
                 }
 
@@ -2374,11 +2373,11 @@ walkInterface = (function() {
                 });
                 if (_state.editingTranslation) {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 *_sliderRanges.joints[0].bobOffsetRange;
+                        2 *_sliderRanges.joints[0].bobOffsetRange;
                     _motion.curAnim.joints[0].bobOffset = newOffset;
                 } else {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 * _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange;
+                        2 * _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].yawOffset = newOffset;
                 }
 
@@ -2390,16 +2389,16 @@ walkInterface = (function() {
                 });
                 if (_state.editingTranslation) {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 * _sliderRanges.joints[0].thrustOffsetRange;
+                        2 * _sliderRanges.joints[0].thrustOffsetRange;
                     _motion.curAnim.joints[0].thrustOffset = newOffset;
                 } else {
                     var newOffset = (thumbPositionNormalised - 0.5) *
-                                     2 * _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange;
+                        2 * _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange;
                     _motion.curAnim.joints[_motion.curJointIndex].rollOffset = newOffset;
                 }
             }
 
-		// end if editing joints
+        // end if editing joints
 
         } else if (_state.currentState === _state.EDIT_WALK_TWEAKS) {
 
@@ -2479,24 +2478,24 @@ walkInterface = (function() {
     function mouseReleaseEvent(event) {
 
         if (_movingSliderOne) {
-			_movingSliderOne = false;
-		} else if (_movingSliderTwo) {
-			_movingSliderTwo = false;
-		} else if (_movingSliderThree) {
-			_movingSliderThree = false;
-		} else if (_movingSliderFour) {
-			_movingSliderFour = false;
-		} else if (_movingSliderFive) {
-			_movingSliderFive = false;
-		} else if (_movingSliderSix) {
-			_movingSliderSix = false;
-		} else if (_movingSliderSeven) {
-			_movingSliderSeven = false;
-		} else if (_movingSliderEight) {
-			_movingSliderEight = false;
-		} else if (_movingSliderNine) {
-			_movingSliderNine = false;
-		}
+            _movingSliderOne = false;
+        } else if (_movingSliderTwo) {
+            _movingSliderTwo = false;
+        } else if (_movingSliderThree) {
+            _movingSliderThree = false;
+        } else if (_movingSliderFour) {
+            _movingSliderFour = false;
+        } else if (_movingSliderFive) {
+            _movingSliderFive = false;
+        } else if (_movingSliderSix) {
+            _movingSliderSix = false;
+        } else if (_movingSliderSeven) {
+            _movingSliderSeven = false;
+        } else if (_movingSliderEight) {
+            _movingSliderEight = false;
+        } else if (_movingSliderNine) {
+            _movingSliderNine = false;
+        }
     };
 
     Controller.mousePressEvent.connect(mousePressEvent);
@@ -2580,9 +2579,9 @@ walkInterface = (function() {
 
                             if (_state.editingTranslation) {
                                 setBackground(_controlsBackgroundWalkEditHipTrans);
-							} else {
+                            } else {
                                 setBackground(_controlsBackgroundWalkEditJoints);
-							}
+                            }
 
                             initialiseWalkStylesPanel(false);
                             setSliderThumbsVisible(true);
@@ -2594,7 +2593,7 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
-					}
+                    }
 
                     case _state.EDIT_STANDING:
                     case _state.EDIT_SIDESTEP_LEFT:
@@ -2602,9 +2601,9 @@ walkInterface = (function() {
 
                         if (_state.editingTranslation) {
                             setBackground(_controlsBackgroundWalkEditHipTrans);
-						} else {
+                        } else {
                             setBackground(_controlsBackgroundWalkEditJoints);
-						}
+                        }
                         hideMenuButtons();
                         initialiseWalkStylesPanel(false);
                         initialiseFrontPanel(false);
@@ -2631,7 +2630,7 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
-					}
+                    }
 
                     case _state.EDIT_FLYING:
                     case _state.EDIT_FLYING_UP:
@@ -2663,7 +2662,7 @@ walkInterface = (function() {
                         setButtonOverlayVisible(_onButton);
                         setButtonOverlayVisible(_backButton);
                         return;
-					}
+                    }
 
                     case _state.STANDING:
                     case _state.WALKING:
@@ -2675,10 +2674,10 @@ walkInterface = (function() {
                         hideJointSelectors();
                         setBackground(_controlsBackground);
                         if (_state.powerOn) {
-							setButtonOverlayVisible(_onButton);
-						} else {
-							setButtonOverlayVisible(_offButton);
-						}
+                            setButtonOverlayVisible(_onButton);
+                        } else {
+                            setButtonOverlayVisible(_offButton);
+                        }
                         setButtonOverlayVisible(_configWalkButton);
                         setButtonOverlayVisible(_configStandButton);
                         setButtonOverlayVisible(_configFlyingButton);
@@ -2687,7 +2686,7 @@ walkInterface = (function() {
                         initialiseFrontPanel(true);
                         initialiseWalkStylesPanel(false);
                         return;
-					}
+                    }
                 }
             }
         }

From 3f1897d6b2e7142b7feafa74a155cee88b374f32 Mon Sep 17 00:00:00 2001
From: DaveDubUK <DaveDubUK@gmail.com>
Date: Tue, 11 Nov 2014 13:21:37 +0000
Subject: [PATCH 23/37] Minor formatting change

---
 examples/libraries/walkInterface.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/examples/libraries/walkInterface.js b/examples/libraries/walkInterface.js
index 7b7a23a584..aa0b533101 100644
--- a/examples/libraries/walkInterface.js
+++ b/examples/libraries/walkInterface.js
@@ -1961,12 +1961,8 @@ walkInterface = (function() {
             case _hideButton:
             case _hideButtonSelected:
 
-                    Overlays.editOverlay(_hideButton, {
-                        visible: false
-                    });
-                Overlays.editOverlay(_hideButtonSelected, {
-                    visible: true
-                });
+				Overlays.editOverlay(_hideButton, {visible: false});
+                Overlays.editOverlay(_hideButtonSelected, {visible: true});
                 _state.minimised = true;
                 momentaryButtonTimer = Script.setInterval(function() {
                     minimiseDialog(_state.minimised);

From 2ebca0a659c6ceb2ed6a3046cec8defb14d2b0eb Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 09:50:25 -0800
Subject: [PATCH 24/37] Add Grid3DOverlay

---
 interface/src/ui/overlays/Grid3DOverlay.cpp | 118 ++++++++++++++++++++
 interface/src/ui/overlays/Grid3DOverlay.h   |  44 ++++++++
 2 files changed, 162 insertions(+)
 create mode 100644 interface/src/ui/overlays/Grid3DOverlay.cpp
 create mode 100644 interface/src/ui/overlays/Grid3DOverlay.h

diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp
new file mode 100644
index 0000000000..c628199fe3
--- /dev/null
+++ b/interface/src/ui/overlays/Grid3DOverlay.cpp
@@ -0,0 +1,118 @@
+//
+//  Grid3DOverlay.cpp
+//  interface/src/ui/overlays
+//
+//  Created by Ryan Huffman on 11/06/14.
+//  Copyright 2014 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "Grid3DOverlay.h"
+
+#include "Application.h"
+
+ProgramObject Grid3DOverlay::_gridProgram;
+
+Grid3DOverlay::Grid3DOverlay() : Base3DOverlay(),
+    _minorGridWidth(1.0),
+    _majorGridEvery(5) {
+}
+
+Grid3DOverlay::~Grid3DOverlay() {
+}
+
+void Grid3DOverlay::render(RenderArgs* args) {
+    if (!_visible) {
+        return; // do nothing if we're not visible
+    }
+
+    if (!_gridProgram.isLinked()) {
+        if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/grid.frag")) {
+            qDebug() << "Failed to compile: " + _gridProgram.log();
+            return;
+        }
+        if (!_gridProgram.link()) {
+            qDebug() << "Failed to link: " + _gridProgram.log();
+            return;
+        }
+    }
+
+    // Render code largely taken from MetavoxelEditor::render()
+    glDisable(GL_LIGHTING);
+
+    glDepthMask(GL_FALSE);
+
+    glPushMatrix();
+
+    glm::quat rotation = getRotation();
+
+    glm::vec3 axis = glm::axis(rotation);
+
+    glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
+
+    glLineWidth(1.5f);
+
+    // center the grid around the camera position on the plane
+    glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition();
+    float spacing = _minorGridWidth;
+
+    float alpha = getAlpha();
+    xColor color = getColor();
+    glm::vec3 position = getPosition();
+
+    const int GRID_DIVISIONS = 300;
+    const float MAX_COLOR = 255.0f;
+    float scale = GRID_DIVISIONS * spacing;
+
+    glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
+
+    _gridProgram.bind();
+
+    // Minor grid
+    glPushMatrix();
+    {
+        glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
+            spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z);
+
+        glScalef(scale, scale, scale);
+
+        Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
+    }
+    glPopMatrix();
+
+    // Major grid
+    glPushMatrix();
+    {
+        glLineWidth(4.0f);
+        spacing *= _majorGridEvery;
+        glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
+            spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z);
+
+        scale *= _majorGridEvery;
+        glScalef(scale, scale, scale);
+
+        Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
+    }
+    glPopMatrix();
+
+    _gridProgram.release();
+
+    glPopMatrix();
+
+    glEnable(GL_LIGHTING);
+    glDepthMask(GL_TRUE);
+}
+
+void Grid3DOverlay::setProperties(const QScriptValue& properties) {
+    Base3DOverlay::setProperties(properties);
+
+    if (properties.property("minorGridWidth").isValid()) {
+        _minorGridWidth = properties.property("minorGridWidth").toVariant().toFloat();
+    }
+
+    if (properties.property("majorGridEvery").isValid()) {
+        _majorGridEvery = properties.property("majorGridEvery").toVariant().toInt();
+    }
+}
diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h
new file mode 100644
index 0000000000..b1675f15d7
--- /dev/null
+++ b/interface/src/ui/overlays/Grid3DOverlay.h
@@ -0,0 +1,44 @@
+//
+//  Grid3DOverlay.h
+//  interface/src/ui/overlays
+//
+//  Created by Ryan Huffman on 11/06/14.
+//  Copyright 2014 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_Grid3DOverlay_h
+#define hifi_Grid3DOverlay_h
+
+// include this before QGLWidget, which includes an earlier version of OpenGL
+#include "InterfaceConfig.h"
+
+#include <glm/glm.hpp>
+
+#include <QGLWidget>
+#include <SharedUtil.h>
+
+#include "Base3DOverlay.h"
+
+#include "renderer/ProgramObject.h"
+
+class Grid3DOverlay : public Base3DOverlay {
+    Q_OBJECT
+
+public:
+    Grid3DOverlay();
+    ~Grid3DOverlay();
+
+    virtual void render(RenderArgs* args);
+    virtual void setProperties(const QScriptValue& properties);
+
+private:
+    float _minorGridWidth;
+    int _majorGridEvery;
+
+    static ProgramObject _gridProgram;
+};
+
+#endif // hifi_Grid3DOverlay_h

From b04fd89e4f6c7f3241a6570966c16b65aa89d780 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 09:55:30 -0800
Subject: [PATCH 25/37] Update grid tool html location

---
 .../html/gridControls.html                    |  19 +--
 examples/libraries/gridTool.js                | 121 ++++--------------
 libraries/script-engine/src/ScriptEngine.cpp  |   6 +-
 libraries/script-engine/src/ScriptEngine.h    |   2 +-
 4 files changed, 29 insertions(+), 119 deletions(-)
 rename gridControls.html => examples/html/gridControls.html (89%)

diff --git a/gridControls.html b/examples/html/gridControls.html
similarity index 89%
rename from gridControls.html
rename to examples/html/gridControls.html
index 84d437a60d..e7bf1cdf8c 100644
--- a/gridControls.html
+++ b/examples/html/gridControls.html
@@ -2,20 +2,13 @@
 <head>
     <script>
         function loaded() {
-            function log(msg) {
-                var el = document.createElement('div');
-                el.innerHTML = msg;
-                document.body.appendChild(el);
-            }
-
             var gridColor = { red: 0, green: 0, blue: 0 };
             var gridColors = [
                 { red: 0, green: 0, blue: 0 },
                 { red: 128, green: 128, blue: 128 },
-                // { red: 255, green: 255, blue: 255 },
-                { red: 255, green: 128, blue: 128 },
-                { red: 128, green: 255, blue: 128 },
-                { red: 128, green: 128, blue: 255 },
+                { red: 255, green: 0, blue: 0 },
+                { red: 0, green: 255, blue: 0},
+                { red: 0, green: 0, blue: 255 },
             ];
 
             posY = document.getElementById("horiz-y");
@@ -32,9 +25,7 @@
                     if (data.origin) {
 
                         var origin = data.origin;
-                        // posX.value = origin.x;
                         posY.value = origin.y;
-                        // posZ.value = origin.z;
                     }
 
                     if (data.minorGridSpacing) {
@@ -62,9 +53,7 @@
                     EventBridge.emitWebEvent(JSON.stringify({
                         type: "update",
                         origin: {
-                            // x: posX.value,
                             y: posY.value,
-                            // z: posZ.value,
                         },
                         minorGridSpacing: minorSpacing.value,
                         majorGridEvery: majorSpacing.value,
@@ -91,8 +80,6 @@
                 box.addEventListener("click", function(color) { 
                     return function() {
                         gridColor = color;
-                        // this.setAttribute('class', 'color-box highlight');
-
                         emitUpdate();
                     }
                 }({ red: colors.red, green: colors.green, blue: colors.blue }));
diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js
index 76c0581b2c..34e25d6733 100644
--- a/examples/libraries/gridTool.js
+++ b/examples/libraries/gridTool.js
@@ -3,7 +3,7 @@ Grid = function(opts) {
 
     var color = { red: 100, green: 152, blue: 203 };
     var gridColor = { red: 100, green: 152, blue: 203 };
-    var gridAlpha = 0.9;
+    var gridAlpha = 1.0;
     var origin = { x: 0, y: 0, z: 0 };
     var majorGridEvery = 5;
     var minorGridSpacing = 0.2;
@@ -15,18 +15,16 @@ Grid = function(opts) {
     var minorGridWidth = 0.5;
     var majorGridWidth = 1.5;
 
-    var gridOverlays = [];
-
     var snapToGrid = true;
 
-    var gridPlane = Overlays.addOverlay("rectangle3d", {
-        position: origin,
-        color: color,
-        size: halfSize * 2 * minorGridSpacing * 1.05,
-        alpha: 0.2,
-        solid: true,
-        visible: false,
-        ignoreRayIntersection: true,
+    var gridOverlay = Overlays.addOverlay("grid", {
+        position: { x: 0 , y: 0, z: 0 },
+        visible: true,
+        color: { red: 0, green: 0, blue: 128 },
+        alpha: 1.0,
+        rotation: Quat.fromPitchYawRollDegrees(90, 0, 0),
+        minorGridWidth: 0.1,
+        majorGridEvery: 2,
     });
 
     that.getMinorIncrement = function() { return minorGridSpacing; };
@@ -43,10 +41,6 @@ Grid = function(opts) {
     that.setVisible = function(visible, noUpdate) {
         that.visible = visible;
         updateGrid();
-        // for (var i = 0; i < gridOverlays.length; i++) {
-        //     Overlays.editOverlay(gridOverlays[i], { visible: visible });
-        // }
-        // Overlays.editOverlay(gridPlane, { visible: visible });
 
         if (!noUpdate) {
             that.emitUpdate();
@@ -71,9 +65,10 @@ Grid = function(opts) {
 
     that.setPosition = function(newPosition, noUpdate) {
         origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 });
+        origin.x = 0;
+        origin.z = 0;
         updateGrid();
 
-        print("updated grid");
         if (!noUpdate) {
             that.emitUpdate();
         }
@@ -94,7 +89,6 @@ Grid = function(opts) {
     };
 
     that.update = function(data) {
-        print("Got update");
         if (data.snapToGrid !== undefined) {
             snapToGrid = data.snapToGrid;
         }
@@ -131,88 +125,18 @@ Grid = function(opts) {
     }
 
     function updateGrid() {
-        // Delete overlays
-        var gridLinesRequired = (halfSize * 2 + 1) * 2;
-        if (gridLinesRequired > gridOverlays.length) {
-            for (var i = gridOverlays.length; i < gridLinesRequired; i++) {
-                gridOverlays.push(Overlays.addOverlay("line3d", {}));
-            }
-        } else if (gridLinesRequired < gridOverlays.length) {
-            var numberToRemove = gridOverlays.length - gridLinesRequired;
-            var removed = gridOverlays.splice(gridOverlays.length - numberToRemove, numberToRemove);
-            for (var i = 0; i < removed.length; i++) {
-                Overlays.deleteOverlay(removed[i]);
-            }
-        }
-
-        Overlays.editOverlay(gridPlane, {
-            position: origin,
-            size: halfSize * 2 * minorGridSpacing * 1.05,
+        Overlays.editOverlay(gridOverlay, {
+            position: { x: origin.y, y: origin.y, z: -origin.y },
+            visible: that.visible,
+            minorGridWidth: minorGridSpacing,
+            majorGridEvery: majorGridEvery,
+                color: gridColor,
+                alpha: gridAlpha,
         });
-
-        var startX = {
-            x: origin.x - (halfSize * minorGridSpacing),
-            y: origin.y,
-            z: origin.z,
-        };
-        var endX = {
-            x: origin.x + (halfSize * minorGridSpacing),
-            y: origin.y,
-            z: origin.z,
-        };
-        var startZ = {
-            x: origin.x,
-            y: origin.y,
-            z: origin.z - (halfSize * minorGridSpacing)
-        };
-        var endZ = {
-            x: origin.x,
-            y: origin.y,
-            z: origin.z + (halfSize * minorGridSpacing)
-        };
-
-        var overlayIdx = 0;
-        for (var i = -halfSize; i <= halfSize; i++) {
-            // Offset for X-axis aligned grid line
-            var offsetX = { x: 0, y: 0, z: i * minorGridSpacing };
-
-            // Offset for Z-axis aligned grid line
-            var offsetZ = { x: i * minorGridSpacing, y: 0, z: 0 };
-
-            var position = Vec3.sum(origin, offsetX);
-            var size = i % majorGridEvery == 0 ? majorGridWidth : minorGridWidth;
-
-            var gridLineX = gridOverlays[overlayIdx++];
-            var gridLineZ = gridOverlays[overlayIdx++];
-
-            Overlays.editOverlay(gridLineX, {
-                start: Vec3.sum(startX, offsetX),
-                end: Vec3.sum(endX, offsetX),
-                lineWidth: size,
-                color: gridColor,
-                alpha: gridAlpha,
-                solid: true,
-                visible: that.visible,
-                ignoreRayIntersection: true,
-            });
-            Overlays.editOverlay(gridLineZ, {
-                start: Vec3.sum(startZ, offsetZ),
-                end: Vec3.sum(endZ, offsetZ),
-                lineWidth: size,
-                color: gridColor,
-                alpha: gridAlpha,
-                solid: true,
-                visible: that.visible,
-                ignoreRayIntersection: true,
-            });
-        }
     }
 
     function cleanup() {
-        Overlays.deleteOverlay(gridPlane);
-        for (var i = 0; i < gridOverlays.length; i++) {
-            Overlays.deleteOverlay(gridOverlays[i]);
-        }
+        Overlays.deleteOverlay(gridOverlay);
     }
 
     that.addListener = function(callback) {
@@ -234,18 +158,17 @@ GridTool = function(opts) {
     var verticalGrid = opts.verticalGrid;
     var listeners = [];
 
-    // var webView = Window.createWebView('http://localhost:8000/gridControls.html', 200, 280);
-    var webView = new WebWindow('http://localhost:8000/gridControls.html', 200, 280);
+    var url = Script.resolvePath('html/gridControls.html');
+    var webView = new WebWindow(url, 200, 280);
 
     horizontalGrid.addListener(function(data) {
         webView.eventBridge.emitScriptEvent(JSON.stringify(data));
     });
 
     webView.eventBridge.webEventReceived.connect(function(data) {
-        print('got event: ' + data);
         data = JSON.parse(data);
         if (data.type == "init") {
-            horizontalGrid.emitUpdate(); 
+            horizontalGrid.emitUpdate();
         } else if (data.type == "update") {
             horizontalGrid.update(data);
             for (var i = 0; i < listeners.length; i++) {
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 8f176de04f..8ea048dbfe 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -641,7 +641,7 @@ void ScriptEngine::stopTimer(QTimer *timer) {
     }
 }
 
-QUrl ScriptEngine::resolveInclude(const QString& include) const {
+QUrl ScriptEngine::resolvePath(const QString& include) const {
     // first lets check to see if it's already a full URL
     QUrl url(include);
     if (!url.scheme().isEmpty()) {
@@ -667,7 +667,7 @@ void ScriptEngine::print(const QString& message) {
 }
 
 void ScriptEngine::include(const QString& includeFile) {
-    QUrl url = resolveInclude(includeFile);
+    QUrl url = resolvePath(includeFile);
     QString includeContents;
 
     if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") {
@@ -705,7 +705,7 @@ void ScriptEngine::include(const QString& includeFile) {
 }
 
 void ScriptEngine::load(const QString& loadFile) {
-    QUrl url = resolveInclude(loadFile);
+    QUrl url = resolvePath(loadFile);
     emit loadScript(url.toString(), false);
 }
 
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 22617512a9..ce48fe6cd8 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -104,6 +104,7 @@ public slots:
     void include(const QString& includeFile);
     void load(const QString& loadfile);
     void print(const QString& message);
+    QUrl resolvePath(const QString& path) const;
 
     void nodeKilled(SharedNodePointer node);
 
@@ -132,7 +133,6 @@ protected:
     int _numAvatarSoundSentBytes;
 
 private:
-    QUrl resolveInclude(const QString& include) const;
     void sendAvatarIdentityPacket();
     void sendAvatarBillboardPacket();
 

From d537c5efdba71aa5891c92f41be1a0c4cecf5c8b Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 09:56:02 -0800
Subject: [PATCH 26/37] Add grid tool menu option

---
 examples/libraries/entitySelectionTool.js |  1 +
 examples/newEditEntities.js               | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index 980689d625..7ae4068e12 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -399,6 +399,7 @@ SelectionDisplay = (function () {
                     alpha: 0.5,
                     solid: true,
                     visible: false,
+                    width: 300, height: 200,
                     rotation: baseOverlayRotation,
                     ignoreRayIntersection: true, // always ignore this
                 });
diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js
index b8e7a6174e..039771a752 100644
--- a/examples/newEditEntities.js
+++ b/examples/newEditEntities.js
@@ -38,8 +38,6 @@ var cameraManager = new CameraManager();
 Script.include("libraries/gridTool.js");
 var grid = Grid();
 gridTool = GridTool({ horizontalGrid: grid });
-gridTool.addListener(function(data) {
-});
 
 selectionManager.setEventListener(selectionDisplay.updateHandles);
 
@@ -57,9 +55,12 @@ var wantEntityGlow = false;
 var SPAWN_DISTANCE = 1;
 var DEFAULT_DIMENSION = 0.20;
 
+var MENU_GRID_TOOL_ENABLED = 'Grid Tool';
 var MENU_INSPECT_TOOL_ENABLED = 'Inspect Tool';
 var MENU_EASE_ON_FOCUS = 'Ease Orientation on Focus';
 
+var SETTING_GRID_TOOL_ENABLED = 'GridToolEnabled';
+
 var modelURLs = [
         HIFI_PUBLIC_BUCKET + "meshes/Feisar_Ship.FBX",
         HIFI_PUBLIC_BUCKET + "meshes/birarda/birarda_head.fbx",
@@ -264,12 +265,13 @@ var toolBar = (function () {
 
         if (activeButton === toolBar.clicked(clickedOverlay)) {
             isActive = !isActive;
-            gridTool.setVisible(isActive);
             if (!isActive) {
+                gridTool.setVisible(false);
                 selectionManager.clearSelections();
                 cameraManager.disable();
             } else {
                 cameraManager.enable();
+                gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED));
             }
             return true;
         }
@@ -597,7 +599,9 @@ function setupModelMenus() {
     Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
     Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true });
 
-    Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: "Edit Entities Help...", isCheckable: true });
+    Menu.addMenuItem({ menuName: "View", menuItemName: MENU_GRID_TOOL_ENABLED, afterItem: "Edit Entities Help...", isCheckable: true,
+                       isChecked: Settings.getValue(SETTING_GRID_TOOL_ENABLED) == 'true'});
+    Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: MENU_GRID_TOOL_ENABLED, isCheckable: true });
     Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, isCheckable: true });
 }
 
@@ -621,6 +625,8 @@ function cleanupModelMenus() {
     Menu.removeMenuItem("File", "Import Models");
     Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems");
 
+    Settings.setValue(SETTING_GRID_TOOL_ENABLED, Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED));
+    Menu.removeMenuItem("View", MENU_GRID_TOOL_ENABLED);
     Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
     Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
 }
@@ -729,6 +735,10 @@ function handeMenuEvent(menuItem) {
         }
     } else if (menuItem == "Import Models") {
         modelImporter.doImport();
+    } else if (menuItem == MENU_GRID_TOOL_ENABLED) {
+        if (isActive) {
+            gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED));
+        }
     }
     tooltip.show(false);
 }

From f577cdd2f46426bd7f7699bfa16f19d2919d4f3a Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 09:56:47 -0800
Subject: [PATCH 27/37] Add Grid3DOverlay to Overlays

---
 interface/src/ui/overlays/Overlays.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index df7a5fbcea..0192f9c216 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -23,6 +23,7 @@
 #include "Overlays.h"
 #include "Rectangle3DOverlay.h"
 #include "Sphere3DOverlay.h"
+#include "Grid3DOverlay.h"
 #include "TextOverlay.h"
 #include "Text3DOverlay.h"
 
@@ -155,6 +156,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
         thisOverlay = new Rectangle3DOverlay();
     } else if (type == "line3d") {
         thisOverlay = new Line3DOverlay();
+    } else if (type == "grid") {
+        thisOverlay = new Grid3DOverlay();
     } else if (type == "localvoxels") {
         thisOverlay = new LocalVoxelsOverlay();
     } else if (type == "localmodels") {

From bd61eba664fc99e2216be4dd6f670c8d9c0f5bf5 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 10:00:28 -0800
Subject: [PATCH 28/37] Remove unused include

---
 interface/src/scripting/WebWindow.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/interface/src/scripting/WebWindow.h b/interface/src/scripting/WebWindow.h
index 698e43fd6c..abc3f61e60 100644
--- a/interface/src/scripting/WebWindow.h
+++ b/interface/src/scripting/WebWindow.h
@@ -15,8 +15,6 @@
 #include <QScriptContext>
 #include <QScriptEngine>
 
-// #include "ScriptWebView.h"
-
 class ScriptEventBridge : public QObject {
     Q_OBJECT
 public:

From a1d771a0bed3bc89921d10848a520228afcf16c5 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 10:03:14 -0800
Subject: [PATCH 29/37] Rename WebWindow to WebWindowClass

---
 interface/src/Application.cpp                 |  4 ++--
 .../{WebWindow.cpp => WebWindowClass.cpp}     | 22 +++++++++----------
 .../{WebWindow.h => WebWindowClass.h}         | 12 +++++-----
 3 files changed, 19 insertions(+), 19 deletions(-)
 rename interface/src/scripting/{WebWindow.cpp => WebWindowClass.cpp} (75%)
 rename interface/src/scripting/{WebWindow.h => WebWindowClass.h} (83%)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 98be839e25..329634eb33 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -91,7 +91,7 @@
 #include "scripting/MenuScriptingInterface.h"
 #include "scripting/SettingsScriptingInterface.h"
 #include "scripting/WindowScriptingInterface.h"
-#include "scripting/WebWindow.h"
+#include "scripting/WebWindowClass.h"
 
 #include "ui/DataWebDialog.h"
 #include "ui/InfoView.h"
@@ -3873,7 +3873,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
     scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
                                        LocationScriptingInterface::locationSetter);
 
-    scriptEngine->registerFunction("WebWindow", WebWindow::constructor, 1);
+    scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
     
     scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
     scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
diff --git a/interface/src/scripting/WebWindow.cpp b/interface/src/scripting/WebWindowClass.cpp
similarity index 75%
rename from interface/src/scripting/WebWindow.cpp
rename to interface/src/scripting/WebWindowClass.cpp
index dfd3104f4b..c5ad20b286 100644
--- a/interface/src/scripting/WebWindow.cpp
+++ b/interface/src/scripting/WebWindowClass.cpp
@@ -1,5 +1,5 @@
 //
-//  WebWindow.cpp
+//  WebWindowClass.cpp
 //  interface/src/scripting
 //
 //  Created by Ryan Huffman on 11/06/14.
@@ -15,7 +15,7 @@
 #include <QWebView>
 
 #include "WindowScriptingInterface.h"
-#include "WebWindow.h"
+#include "WebWindowClass.h"
 
 ScriptEventBridge::ScriptEventBridge(QObject* parent) : QObject(parent) {
 }
@@ -28,7 +28,7 @@ void ScriptEventBridge::emitScriptEvent(const QString& data) {
     emit scriptEventReceived(data);
 }
 
-WebWindow::WebWindow(const QString& url, int width, int height)
+WebWindowClass::WebWindowClass(const QString& url, int width, int height)
     : QObject(NULL),
       _window(new QWidget(NULL, Qt::Tool)),
       _eventBridge(new ScriptEventBridge(this)) {
@@ -43,26 +43,26 @@ WebWindow::WebWindow(const QString& url, int width, int height)
     layout->setContentsMargins(0, 0, 0, 0);
     _window->setGeometry(0, 0, width, height);
 
-    connect(this, &WebWindow::destroyed, _window, &QWidget::deleteLater);
+    connect(this, &WebWindowClass::destroyed, _window, &QWidget::deleteLater);
 }
 
-WebWindow::~WebWindow() {
+WebWindowClass::~WebWindowClass() {
 }
 
-void WebWindow::setVisible(bool visible) {
+void WebWindowClass::setVisible(bool visible) {
     QMetaObject::invokeMethod(_window, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible));
 }
 
-QScriptValue WebWindow::constructor(QScriptContext* context, QScriptEngine* engine) {
-    WebWindow* retVal;
+QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
+    WebWindowClass* retVal;
     QString file = context->argument(0).toString();
-    QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection,
-            Q_RETURN_ARG(WebWindow*, retVal),
+    QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindowClass", Qt::BlockingQueuedConnection,
+            Q_RETURN_ARG(WebWindowClass*, retVal),
             Q_ARG(const QString&, file),
             Q_ARG(int, context->argument(1).toInteger()),
             Q_ARG(int, context->argument(2).toInteger()));
 
-    connect(engine, &QScriptEngine::destroyed, retVal, &WebWindow::deleteLater);
+    connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater);
 
     return engine->newQObject(retVal);//, QScriptEngine::ScriptOwnership);
 }
diff --git a/interface/src/scripting/WebWindow.h b/interface/src/scripting/WebWindowClass.h
similarity index 83%
rename from interface/src/scripting/WebWindow.h
rename to interface/src/scripting/WebWindowClass.h
index abc3f61e60..7b77299774 100644
--- a/interface/src/scripting/WebWindow.h
+++ b/interface/src/scripting/WebWindowClass.h
@@ -1,5 +1,5 @@
 //
-//  WebWindow.h
+//  WebWindowClass.h
 //  interface/src/scripting
 //
 //  Created by Ryan Huffman on 11/06/14.
@@ -9,8 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#ifndef hifi_WebWindow_h
-#define hifi_WebWindow_h
+#ifndef hifi_WebWindowClass_h
+#define hifi_WebWindowClass_h
 
 #include <QScriptContext>
 #include <QScriptEngine>
@@ -30,12 +30,12 @@ signals:
 
 };
 
-class WebWindow : public QObject {
+class WebWindowClass : public QObject {
     Q_OBJECT
     Q_PROPERTY(QObject* eventBridge READ getEventBridge)
 public:
-    WebWindow(const QString& url, int width, int height);
-    ~WebWindow();
+    WebWindowClass(const QString& url, int width, int height);
+    ~WebWindowClass();
 
     static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
 

From 3ec28f8e301f7d49f4d6e03b2ab35a1aea3c76ab Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 10:15:16 -0800
Subject: [PATCH 30/37] Add WindowScriptingInterface::doCreateWebWindow

---
 interface/src/scripting/WebWindowClass.cpp           | 4 ++--
 interface/src/scripting/WindowScriptingInterface.cpp | 4 ++++
 interface/src/scripting/WindowScriptingInterface.h   | 7 +++++++
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp
index c5ad20b286..d280d8eecf 100644
--- a/interface/src/scripting/WebWindowClass.cpp
+++ b/interface/src/scripting/WebWindowClass.cpp
@@ -56,7 +56,7 @@ void WebWindowClass::setVisible(bool visible) {
 QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
     WebWindowClass* retVal;
     QString file = context->argument(0).toString();
-    QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindowClass", Qt::BlockingQueuedConnection,
+    QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection,
             Q_RETURN_ARG(WebWindowClass*, retVal),
             Q_ARG(const QString&, file),
             Q_ARG(int, context->argument(1).toInteger()),
@@ -64,5 +64,5 @@ QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine*
 
     connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater);
 
-    return engine->newQObject(retVal);//, QScriptEngine::ScriptOwnership);
+    return engine->newQObject(retVal);
 }
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index ed6f0cf600..8c2066f253 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -34,6 +34,10 @@ WindowScriptingInterface::WindowScriptingInterface() :
 {
 }
 
+WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& url, int width, int height) {
+    return new WebWindowClass(url, width, height);
+}
+
 QScriptValue WindowScriptingInterface::hasFocus() {
     return Application::getInstance()->getGLWidget()->hasFocus();
 }
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 24c21765b5..5529d31efd 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -15,6 +15,11 @@
 #include <QObject>
 #include <QScriptValue>
 #include <QString>
+#include <QFileDialog>
+#include <QComboBox>
+#include <QLineEdit>
+
+#include "WebWindowClass.h"
 
 class WindowScriptingInterface : public QObject {
     Q_OBJECT
@@ -72,6 +77,8 @@ private slots:
 
     void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); }
     void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); }
+
+    WebWindowClass* doCreateWebWindow(const QString& url, int width, int height);
     
 private:
     WindowScriptingInterface();

From db9a5f22820b9476802338d1b104ba101a71c778 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Tue, 11 Nov 2014 11:43:27 -0800
Subject: [PATCH 31/37] Add back XZ constraining to entity edit tools

---
 examples/libraries/entitySelectionTool.js | 32 ++++++++++-------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index 7ae4068e12..22f75cb187 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -1149,29 +1149,25 @@ SelectionDisplay = (function () {
 
             // If shifted, constrain to one axis
             if (event.isShifted) {
-                // if (Math.abs(vector.x) > Math.abs(vector.z)) {
-                //     vector.z = 0;
-                // } else {
-                //     vector.x = 0;
-                // }
+                if (Math.abs(vector.x) > Math.abs(vector.z)) {
+                    vector.z = 0;
+                } else {
+                    vector.x = 0;
+                }
                 if (!isConstrained) {
-                    // Overlays.editOverlay(xRailOverlay, { visible: true });
-                    // var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 });
-                    // var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 });
-                    // var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 });
-                    // var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 });
-                    // Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true });
-                    // Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true });
+                    Overlays.editOverlay(xRailOverlay, { visible: true });
+                    var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 });
+                    var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 });
+                    var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 });
+                    var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 });
+                    Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true });
+                    Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true });
                     isConstrained = true;
                 }
-                // constrainMajorOnly = event.isControl;
-                // vector = Vec3.subtract(
-                //     grid.snapToGrid(Vec3.sum(startPosition, vector), constrainMajorOnly),
-                //     startPosition);
             } else {
                 if (isConstrained) {
-                    // Overlays.editOverlay(xRailOverlay, { visible: false });
-                    // Overlays.editOverlay(zRailOverlay, { visible: false });
+                    Overlays.editOverlay(xRailOverlay, { visible: false });
+                    Overlays.editOverlay(zRailOverlay, { visible: false });
                     isConstrained = false;
                 }
             }

From 2bc7896dee2d94f85707958a0c76f44448a29363 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 11 Nov 2014 13:46:48 -0800
Subject: [PATCH 32/37] first cut at preload script behavior

---
 examples/entityScripts/playSoundOnClick.js    | 22 ++++++++++++++-----
 .../entityScripts/playSoundOnEnterOrLeave.js  |  9 ++++++--
 interface/src/entities/EntityTreeRenderer.cpp | 18 +++++++++++++++
 interface/src/entities/EntityTreeRenderer.h   |  2 ++
 libraries/entities/src/EntityTree.cpp         | 15 ++++++++++++-
 libraries/entities/src/EntityTree.h           |  6 +++++
 libraries/entities/src/EntityTreeElement.cpp  |  3 ++-
 7 files changed, 65 insertions(+), 10 deletions(-)

diff --git a/examples/entityScripts/playSoundOnClick.js b/examples/entityScripts/playSoundOnClick.js
index b261bb269a..4bc523a7aa 100644
--- a/examples/entityScripts/playSoundOnClick.js
+++ b/examples/entityScripts/playSoundOnClick.js
@@ -12,13 +12,23 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 (function(){ 
-    var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
+    var bird;
+
+    function playSound(entityID) { 
+        var options = new AudioInjectionOptions();
+        var position = MyAvatar.position; 
+        options.position = position;
+        options.volume = 0.5;
+        Audio.playSound(bird, options);
+    }; 
+
+    this.preload = function(entityID) { 
+        print("preload("+entityID.id+")");
+        bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
+    }; 
+
     this.clickDownOnEntity = function(entityID, mouseEvent) { 
         print("clickDownOnEntity()...");
-		var options = new AudioInjectionOptions();
-		var position = MyAvatar.position; 
-		options.position = position;
-		options.volume = 0.5;
-		Audio.playSound(bird, options);
+		playSound();
     }; 
 })
diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js
index 228a8a36d0..98702dcfdd 100644
--- a/examples/entityScripts/playSoundOnEnterOrLeave.js
+++ b/examples/entityScripts/playSoundOnEnterOrLeave.js
@@ -12,9 +12,9 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 (function(){ 
-    var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
+    var bird;
 
-    function playSound(entityID) { 
+    function playSound() { 
         var options = new AudioInjectionOptions();
         var position = MyAvatar.position; 
         options.position = position;
@@ -22,6 +22,11 @@
         Audio.playSound(bird, options);
     }; 
 
+    this.preload = function(entityID) { 
+        print("preload("+entityID.id+")");
+        bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
+    }; 
+
     this.enterEntity = function(entityID) { 
         playSound();
     }; 
diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp
index 2f8ddb1095..ae94b5f973 100644
--- a/interface/src/entities/EntityTreeRenderer.cpp
+++ b/interface/src/entities/EntityTreeRenderer.cpp
@@ -81,6 +81,8 @@ void EntityTreeRenderer::init() {
     _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f);
     
     connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
+    connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity);
+    connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
 }
 
 QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
@@ -770,3 +772,19 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
     _entityScripts.remove(entityID);
 }
 
+void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
+    qDebug() << "addingEntity() entityID:" << entityID;
+    
+    // load the entity script if needed...
+    QScriptValue entityScript = loadEntityScript(entityID);
+    if (entityScript.property("preload").isValid()) {
+        QScriptValueList entityArgs = createEntityArgs(entityID);
+        entityScript.property("preload").call(entityScript, entityArgs);
+    }
+    
+}
+
+void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID) {
+    qDebug() << "changingEntityID() oldEntityID:" << oldEntityID << "newEntityID:" << newEntityID;
+}
+
diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h
index ff9066dd6d..c7068fac86 100644
--- a/interface/src/entities/EntityTreeRenderer.h
+++ b/interface/src/entities/EntityTreeRenderer.h
@@ -104,6 +104,8 @@ signals:
 
 public slots:
     void deletingEntity(const EntityItemID& entityID);
+    void addingEntity(const EntityItemID& entityID);
+    void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
     
 private:
     QList<Model*> _releasedModels;
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 199bd92030..aa7405164e 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -168,6 +168,7 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
     if (result) {
         // this does the actual adding of the entity
         addEntityItem(result);
+        emitAddingEntity(entityID);
     }
     return result;
 }
@@ -184,6 +185,14 @@ void EntityTree::trackDeletedEntity(const EntityItemID& entityID) {
     }
 }
 
+void EntityTree::emitAddingEntity(const EntityItemID& entityItemID) {
+    emit addingEntity(entityItemID);
+}
+
+void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
+    emit entityScriptChanging(entityItemID);
+}
+
 void EntityTree::deleteEntity(const EntityItemID& entityID) {
     emit deletingEntity(entityID);
 
@@ -290,6 +299,7 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) {
     EntityItemID  creatorTokenVersion = searchEntityID.convertToCreatorTokenVersion();
     EntityItemID  knownIDVersion = searchEntityID.convertToKnownIDVersion();
 
+
     // First look for and find the "viewed version" of this entity... it's possible we got
     // the known ID version sent to us between us creating our local version, and getting this
     // remapping message. If this happened, we actually want to find and delete that version of
@@ -310,6 +320,10 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) {
             creatorTokenContainingElement->updateEntityItemID(creatorTokenVersion, knownIDVersion);
             setContainingElement(creatorTokenVersion, NULL);
             setContainingElement(knownIDVersion, creatorTokenContainingElement);
+            
+            // because the ID of the entity is switching, we need to emit these signals for any 
+            // listeners who care about the changing of IDs
+            emit changingEntityID(creatorTokenVersion, knownIDVersion);
         }
     }
     unlock();
@@ -981,7 +995,6 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
     return processedBytes;
 }
 
-
 EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID)  /*const*/ {
     // TODO: do we need to make this thread safe? Or is it acceptable as is
     if (_entityToElementMap.contains(entityItemID)) {
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 8d1acc0d01..6fe2d256c2 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -140,10 +140,16 @@ public:
 
     void trackDeletedEntity(const EntityItemID& entityID);
 
+    void emitAddingEntity(const EntityItemID& entityItemID);
+    void emitEntityScriptChanging(const EntityItemID& entityItemID);
+
     QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
     
 signals:
     void deletingEntity(const EntityItemID& entityID);
+    void addingEntity(const EntityItemID& entityID);
+    void entityScriptChanging(const EntityItemID& entityItemID);
+    void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
 
 private:
 
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index 079fb1bba7..e419187cb6 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -726,7 +726,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
                     entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead);
                     entityItem = _myTree->findEntityByEntityItemID(entityItemID);
                 }
-                
+
                 // If the item already exists in our tree, we want do the following...
                 // 1) allow the existing item to read from the databuffer
                 // 2) check to see if after reading the item, the containing element is still correct, fix it if needed
@@ -762,6 +762,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
                         addEntityItem(entityItem); // add this new entity to this elements entities
                         entityItemID = entityItem->getEntityItemID();
                         _myTree->setContainingElement(entityItemID, this);
+                        _myTree->emitAddingEntity(entityItemID); // we just added an entity
                         EntityItem::SimulationState newState = entityItem->getSimulationState();
                         _myTree->changeEntityState(entityItem, EntityItem::Static, newState);
                     }

From 8c35d4e3dce7c8a59caf56f94f35e3c0c0583480 Mon Sep 17 00:00:00 2001
From: Philip Rosedale <philip@highfidelity.io>
Date: Tue, 11 Nov 2014 14:08:59 -0800
Subject: [PATCH 33/37] remove debug line

---
 libraries/entities/src/EntityItem.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 14229575a1..ef02aafbc8 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -491,7 +491,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
         READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation);
         READ_ENTITY_PROPERTY(PROP_MASS, float, _mass);
         READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity);
-        qDebug() << "    readEntityDataFromBuffer() _velocity:" << _velocity << "line:" << __LINE__;
         READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity);
         READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
         READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime);

From 17d0f247d601566222ab3c10f33e779834ebab84 Mon Sep 17 00:00:00 2001
From: Philip Rosedale <philip@highfidelity.io>
Date: Tue, 11 Nov 2014 14:10:44 -0800
Subject: [PATCH 34/37] Switch muting threshold to 0.003 to be a bit less
 aggressive

---
 assignment-client/src/audio/AudioMixer.cpp     | 2 +-
 domain-server/resources/describe-settings.json | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 4d0e1bf19c..4173cacfc7 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -61,7 +61,7 @@
 
 const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
 const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18;
-const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.001f;
+const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f;
 const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
 const QString AUDIO_ENV_GROUP_KEY = "audio_env";
 const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index a49e1072eb..ba4cfe8dfd 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -93,8 +93,8 @@
         "name": "noise_muting_threshold",
         "label": "Noise Muting Threshold",
         "help": "Loudness value for noise background between 0 and 1.0 (0: mute everyone, 1.0: never mute)",
-        "placeholder": "0.001",
-        "default": "0.001",
+        "placeholder": "0.003",
+        "default": "0.003",
         "advanced": false
       },
       {

From 8ec3f8ce991efa9c685ce9a5f91a2695f989eb49 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 11 Nov 2014 14:21:06 -0800
Subject: [PATCH 35/37] implement support for calling preload when the script
 changes

---
 examples/entityScripts/changeColorOnHover.js  |  8 ++++++++
 interface/src/entities/EntityTreeRenderer.cpp | 13 ++++++++-----
 interface/src/entities/EntityTreeRenderer.h   |  2 +-
 libraries/entities/src/EntityTree.cpp         |  7 +++++++
 libraries/entities/src/EntityTreeElement.cpp  |  9 +++++++++
 5 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/examples/entityScripts/changeColorOnHover.js b/examples/entityScripts/changeColorOnHover.js
index de3f5f3784..638c1bece4 100644
--- a/examples/entityScripts/changeColorOnHover.js
+++ b/examples/entityScripts/changeColorOnHover.js
@@ -22,13 +22,21 @@
     this.oldColorKnown = true;
     print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
   };
+  
+  this.preload = function(entityID) {
+    print("preload");
+    this.storeOldColor(entityID);
+  };
+  
   this.hoverEnterEntity = function(entityID, mouseEvent) { 
+    print("hoverEnterEntity");
     if (!this.oldColorKnown) {
         this.storeOldColor(entityID);
     }
     Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
   }; 
   this.hoverLeaveEntity = function(entityID, mouseEvent) { 
+    print("hoverLeaveEntity");
     if (this.oldColorKnown) {
         print("leave restoring old color... this.oldColor=" 
                     + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp
index ae94b5f973..c371c015ba 100644
--- a/interface/src/entities/EntityTreeRenderer.cpp
+++ b/interface/src/entities/EntityTreeRenderer.cpp
@@ -81,7 +81,8 @@ void EntityTreeRenderer::init() {
     _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f);
     
     connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
-    connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity);
+    connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload);
+    connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::checkAndCallPreload);
     connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
 }
 
@@ -772,9 +773,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
     _entityScripts.remove(entityID);
 }
 
-void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
-    qDebug() << "addingEntity() entityID:" << entityID;
-    
+void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
     // load the entity script if needed...
     QScriptValue entityScript = loadEntityScript(entityID);
     if (entityScript.property("preload").isValid()) {
@@ -785,6 +784,10 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
 }
 
 void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID) {
-    qDebug() << "changingEntityID() oldEntityID:" << oldEntityID << "newEntityID:" << newEntityID;
+    if (_entityScripts.contains(oldEntityID)) {
+        EntityScriptDetails details = _entityScripts[oldEntityID];
+        _entityScripts.remove(oldEntityID);
+        _entityScripts[newEntityID] = details;
+    }
 }
 
diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h
index c7068fac86..7c1c81b6c9 100644
--- a/interface/src/entities/EntityTreeRenderer.h
+++ b/interface/src/entities/EntityTreeRenderer.h
@@ -104,8 +104,8 @@ signals:
 
 public slots:
     void deletingEntity(const EntityItemID& entityID);
-    void addingEntity(const EntityItemID& entityID);
     void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
+    void checkAndCallPreload(const EntityItemID& entityID);
     
 private:
     QList<Model*> _releasedModels;
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index aa7405164e..bc2823e15c 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -124,6 +124,7 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
     } else {
         // check to see if we need to simulate this entity...
         EntityItem::SimulationState oldState = existingEntity->getSimulationState();
+        QString entityScriptBefore = existingEntity->getScript();
     
         UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties);
         recurseTreeWithOperator(&theOperator);
@@ -131,6 +132,12 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
 
         EntityItem::SimulationState newState = existingEntity->getSimulationState();
         changeEntityState(existingEntity, oldState, newState);
+
+        QString entityScriptAfter = existingEntity->getScript();
+        if (entityScriptBefore != entityScriptAfter) {
+            emitEntityScriptChanging(entityID); // the entity script has changed
+        }
+
     }
     
     containingElement = getContainingElement(entityID);
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index e419187cb6..1dea2bcd85 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -734,10 +734,13 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
                 // TODO: Do we need to also do this?
                 //    3) remember the old cube for the entity so we can mark it as dirty
                 if (entityItem) {
+                    QString entityScriptBefore = entityItem->getScript();
                     bool bestFitBefore = bestFitEntityBounds(entityItem);
                     EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
                     EntityItem::SimulationState oldState = entityItem->getSimulationState();
+
                     bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
+
                     EntityItem::SimulationState newState = entityItem->getSimulationState();
                     if (oldState != newState) {
                         _myTree->changeEntityState(entityItem, oldState, newState);
@@ -755,6 +758,12 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
                             }
                         }
                     }
+
+                    QString entityScriptAfter = entityItem->getScript();
+                    if (entityScriptBefore != entityScriptAfter) {
+                        _myTree->emitEntityScriptChanging(entityItemID); // the entity script has changed
+                    }
+
                 } else {
                     entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
                     if (entityItem) {

From 8975fbe1ee49ec98cb8f165e81a06a2e6887e202 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 11 Nov 2014 14:26:45 -0800
Subject: [PATCH 36/37] removed blank line

---
 interface/src/entities/EntityTreeRenderer.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp
index c371c015ba..1876b6c624 100644
--- a/interface/src/entities/EntityTreeRenderer.cpp
+++ b/interface/src/entities/EntityTreeRenderer.cpp
@@ -780,7 +780,6 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
         QScriptValueList entityArgs = createEntityArgs(entityID);
         entityScript.property("preload").call(entityScript, entityArgs);
     }
-    
 }
 
 void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID) {

From f9aed393528f19b1a9bd6bbe564cba2c09fc334a Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Tue, 11 Nov 2014 18:17:24 -0800
Subject: [PATCH 37/37] don't try to open a joystick if controller is NULL

---
 interface/src/scripting/JoystickScriptingInterface.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp
index 68affeda5b..40109703d6 100644
--- a/interface/src/scripting/JoystickScriptingInterface.cpp
+++ b/interface/src/scripting/JoystickScriptingInterface.cpp
@@ -52,9 +52,12 @@ JoystickScriptingInterface::JoystickScriptingInterface() :
 
         for (int i = 0; i < joystickCount; i++) {
             SDL_GameController* controller = SDL_GameControllerOpen(i);
-            SDL_JoystickID id = getInstanceId(controller);
-            Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
-            _openJoysticks[id] = joystick;
+            
+            if (controller) {
+                SDL_JoystickID id = getInstanceId(controller);
+                Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
+                _openJoysticks[id] = joystick;
+            }
         }
 
         _isInitialized = true;