diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 9131c99c6d..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";
@@ -465,6 +465,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();
     
@@ -642,7 +699,6 @@ void AudioMixer::run() {
     timer.start();
 
     char clientMixBuffer[MAX_PACKET_SIZE];
-    char clientEnvBuffer[MAX_PACKET_SIZE];
     
     int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
     
@@ -759,58 +815,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);
@@ -826,6 +830,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 ccf7727265..cc88e368b2 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
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
       },
       {
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/examples/entityScripts/playSoundOnClick.js b/examples/entityScripts/playSoundOnClick.js
index fea68db2c3..4ab83a1952 100644
--- a/examples/entityScripts/playSoundOnClick.js
+++ b/examples/entityScripts/playSoundOnClick.js
@@ -12,12 +12,18 @@
 //  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;
+
+    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()...");
-		    Audio.playSound(bird,  {
-		      position: MyAvatar.position,
-          volume: 0.5
+        Audio.playSound(bird,  {
+            position: MyAvatar.position,
+            volume: 0.5
 		    });
     }; 
 })
diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js
index 6ee961af0a..07be090c31 100644
--- a/examples/entityScripts/playSoundOnEnterOrLeave.js
+++ b/examples/entityScripts/playSoundOnEnterOrLeave.js
@@ -12,13 +12,18 @@
 //  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) {
         Audio.playSound(bird, {
           position: MyAvatar.position,
           volume: 0.5
         });
+    } 
+
+    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) { 
diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html
new file mode 100644
index 0000000000..e7bf1cdf8c
--- /dev/null
+++ b/examples/html/gridControls.html
@@ -0,0 +1,193 @@
+<html>
+<head>
+    <script>
+        function loaded() {
+            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: 0, blue: 0 },
+                { red: 0, green: 255, blue: 0},
+                { red: 0, green: 0, 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;
+                        posY.value = origin.y;
+                    }
+
+                    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: {
+                            y: posY.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;
+                        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/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index 29bf1bdd79..22f75cb187 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -394,12 +394,12 @@ 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,
                     visible: false,
+                    width: 300, height: 200,
                     rotation: baseOverlayRotation,
                     ignoreRayIntersection: true, // always ignore this
                 });
@@ -570,6 +570,7 @@ SelectionDisplay = (function () {
         xRailOverlay,
         yRailOverlay,
         zRailOverlay,
+        baseOfEntityProjectionOverlay,
     ].concat(stretchHandles);
 
     overlayNames[highlightBox] = "highlightBox";
@@ -878,20 +879,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 +1103,7 @@ SelectionDisplay = (function () {
 
     var initialXZPick = null;
     var isConstrained = false;
+    var constrainMajorOnly = false;
     var startPosition = null;
     var duplicatedEntityIDs = null;
     var translateXZTool = {
@@ -1162,15 +1168,23 @@ SelectionDisplay = (function () {
                 if (isConstrained) {
                     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..34e25d6733
--- /dev/null
+++ b/examples/libraries/gridTool.js
@@ -0,0 +1,189 @@
+Grid = function(opts) {
+    var that = {};
+
+    var color = { red: 100, green: 152, blue: 203 };
+    var gridColor = { red: 100, green: 152, blue: 203 };
+    var gridAlpha = 1.0;
+    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 snapToGrid = 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; };
+    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();
+
+        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 });
+        origin.x = 0;
+        origin.z = 0;
+        updateGrid();
+
+        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) {
+        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() {
+        Overlays.editOverlay(gridOverlay, {
+            position: { x: origin.y, y: origin.y, z: -origin.y },
+            visible: that.visible,
+            minorGridWidth: minorGridSpacing,
+            majorGridEvery: majorGridEvery,
+                color: gridColor,
+                alpha: gridAlpha,
+        });
+    }
+
+    function cleanup() {
+        Overlays.deleteOverlay(gridOverlay);
+    }
+
+    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 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) {
+        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/libraries/walkApi.js b/examples/libraries/walkApi.js
new file mode 100644
index 0000000000..0175c56e18
--- /dev/null
+++ b/examples/libraries/walkApi.js
@@ -0,0 +1,412 @@
+//
+//  walkObjects.js
+//
+//  version 1.001
+//
+//  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;
+    this.lastStrideLength = 0; // kept for use during transitions
+
+};  // 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
+    this.nextAnimation = nextAnimation; // name of next animation
+    if (lastAnimation === motion.selWalk ||
+        nextAnimation === motion.selSideStepLeft ||
+        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) {
+        // 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 = 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?
+
+}; // 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..98a7562bb4
--- /dev/null
+++ b/examples/libraries/walkFilters.js
@@ -0,0 +1,225 @@
+//
+//  walkFilters.js
+//
+//  version 1.001
+//
+//  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) {
+
+    // cut off frequency = 5Hz
+    this.gain = 20.20612010;
+    this.coeffOne = -0.4775922501;
+    this.coeffTwo = 1.2796324250;
+
+    // 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 (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: {
+
+                    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 (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) {
+                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..aa0b533101
--- /dev/null
+++ b/examples/libraries/walkInterface.js
@@ -0,0 +1,2690 @@
+//
+//  walkInterface.js
+//
+//  version 1.001
+//
+//  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:
+            case _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);
+                return;
+
+            case _onButton:
+
+                _state.powerOn = false;
+                _state.setInternalState(state.STANDING);
+                Overlays.editOverlay(_offButton, {
+                    visible: true
+                });
+                Overlays.editOverlay(_onButton, {
+                    visible: false
+                });
+                return;
+
+            case _backButton:
+            case _backButtonSelected:
+
+                Overlays.editOverlay(_backButton, {
+                    visible: false
+                });
+                Overlays.editOverlay(_backButtonSelected, {
+                    visible: false
+                });
+                _state.setInternalState(_state.STANDING);
+                return;
+
+            case _configWalkStylesButton:
+
+                _state.setInternalState(_state.EDIT_WALK_STYLES);
+                return;
+
+            case _configWalkTweaksButton:
+
+                _state.setInternalState(_state.EDIT_WALK_TWEAKS);
+                return;
+
+            case _configWalkJointsButton:
+
+                _state.setInternalState(_state.EDIT_WALK_JOINTS);
+                return;
+
+            case _configWalkButton:
+
+                _state.setInternalState(_state.EDIT_WALK_STYLES);
+                return;
+
+            case _configStandButton:
+
+                _state.setInternalState(_state.EDIT_STANDING);
+                return;
+
+            case _configSideStepLeftButton:
+
+                _state.setInternalState(_state.EDIT_SIDESTEP_LEFT);
+                return;
+
+            case _configSideStepRightButton:
+
+                _state.setInternalState(_state.EDIT_SIDESTEP_RIGHT);
+                return;
+
+            case _configFlyingButton:
+
+                _state.setInternalState(_state.EDIT_FLYING);
+                return;
+
+            case _configFlyingUpButton:
+
+                _state.setInternalState(_state.EDIT_FLYING_UP);
+                return;
+
+            case _configFlyingDownButton:
+
+                _state.setInternalState(_state.EDIT_FLYING_DOWN);
+                return;
+        }
+    };
+
+    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;
+            } else 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/newEditEntities.js b/examples/newEditEntities.js
index 57f3f29670..68e0d0c146 100644
--- a/examples/newEditEntities.js
+++ b/examples/newEditEntities.js
@@ -35,6 +35,10 @@ var entityPropertyDialogBox = EntityPropertyDialogBox;
 Script.include("libraries/entityCameraTool.js");
 var cameraManager = new CameraManager();
 
+Script.include("libraries/gridTool.js");
+var grid = Grid();
+gridTool = GridTool({ horizontalGrid: grid });
+
 selectionManager.setEventListener(selectionDisplay.updateHandles);
 
 var windowDimensions = Controller.getViewportDimensions();
@@ -51,11 +55,13 @@ 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_INSPECT_TOOL_ENABLED = "inspectToolEnabled";
 var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
+var SETTING_GRID_TOOL_ENABLED = 'GridToolEnabled';
 
 var modelURLs = [
         HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx",
@@ -262,10 +268,12 @@ var toolBar = (function () {
         if (activeButton === toolBar.clicked(clickedOverlay)) {
             isActive = !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 +605,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...",
+    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, isChecked: Settings.getValue(SETTING_INSPECT_TOOL_ENABLED) == "true" });
     Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
                        isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
@@ -623,6 +633,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);
 }
@@ -734,6 +746,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);
 }
@@ -759,25 +775,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) {
diff --git a/examples/walk.js b/examples/walk.js
index ac0a2b1d39..5a0df72f26 100644
--- a/examples/walk.js
+++ b/examples/walk.js
@@ -1,3853 +1,2613 @@
 //
 //  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;//3.919;
+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;
+// location of animation files and overlay images
+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] ) {
+// 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);
+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 = {
-      position: Camera.getPosition(),
-      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;
-        }
-
-		// 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();
-
-		// 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());
-
-		if(!editing) {
-
-			// 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
-
-			// calcualte (local) change in velocity
-			var velocity = MyAvatar.getVelocity();
-			speed = Vec3.length(velocity);
-
-			// 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_WALK_STYLES: {
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
 			}
 
-			deltaX = localVelocity.x;
-			deltaY = localVelocity.y;
-			deltaZ = -localVelocity.z;
+			case state.EDIT_WALK_TWEAKS: {
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+
+			case state.EDIT_WALK_JOINTS: {
+				motion.curAnim = motion.selWalk;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+			case state.EDIT_STANDING: {
+				motion.curAnim = motion.selStand;
+				motion.direction = FORWARDS;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+
+			case state.EDIT_SIDESTEP_LEFT: {
+				motion.curAnim = motion.selSideStepLeft;
+				motion.direction = LEFT;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+
+			case state.EDIT_SIDESTEP_RIGHT: {
+				motion.curAnim = motion.selSideStepRight;
+				motion.direction = RIGHT;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+
+			case state.EDIT_FLYING: {
+				motion.curAnim = motion.selFly;
+				motion.direction = FORWARDS;
+				animateAvatar(deltaTime, speed);
+				break;
+			}
+
+			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;
+			} 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;
+			if (actionToTake === state.WALKING &&
+			   (motion.direction === LEFT ||
+				motion.direction === RIGHT)) {
+					actionToTake = state.SIDE_STEP;
 			}
 
-			// maybe at walking speed, but flying up?
-			if( actionToTake === WALKING &&
-			  ( principleDirection === DIRECTION_UP )) {
-
-					actionToTake = FLYING;
-					standHints--;
-					flyHints++;
+			// maybe at walking speed, but flying up or down?
+			if (actionToTake === state.WALKING &&
+			   (motion.direction === UP ||
+			    motion.direction === DOWN)) {
+					actionToTake = state.FLYING;
 			}
 
-			// 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};
-
-
 			// select appropriate animation and initiate Transition if required
-			switch(actionToTake) {
+			// note: The transitions are not compete, and are the most likely
+			// candidate for the next worklist item
+			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.selFly:
+							case motion.selFlyUp:
+							case motion.selFlyDown: {
 
-								case selectedWalk:
-
-									// Walking to Standing
-									var timeWalking = new Date().getTime() - framesHistory.lastWalkStartTime;
-
-									var bezierCoeffsOne = {x:0.0, y:1.0};
-									var bezierCoeffsTwo = {x:0.0, y:1.0};
-									var transitionTime = 0.4;
-
-									// 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 selectedSideStepLeft:
-								case selectedSideStepRight:
-
-									break;
-
-								default:
-
-									currentTransition = new Transition(currentAnimation, selectedStand, [], 0.3, {x:0.5,y:0.08}, {x:0.28,y:1});
-									break;
+								// Flying to Standing
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selStand,
+									[], 0.5,
+									{x: 0.5, y: 0.08},
+									{x: 0.28, y: 1});
+								break;
 							}
 
-							setInternalState(STANDING);
-							currentAnimation = selectedStand;
+							default:
 
+								break;
 						}
-						animateAvatar(1,0,principleDirection);
+						state.setInternalState(state.STANDING);
+						motion.curAnim = motion.selStand;
 					}
+					animateAvatar(deltaTime, speed);
 					break;
+				}
 
-				case WALKING:
-				case SIDE_STEPPING:
-					if( walkHints > requiredHints ||
-						INTERNAL_STATE===WALKING ||
-						INTERNAL_STATE===SIDE_STEPPING ) { // wait for few consecutive hints (17mS each)
+				case state.WALKING: {
 
-						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;
-							}
+					if (state.currentState !== state.WALKING) {
 
+						if (motion.direction === BACKWARDS) {
+							motion.walkWheelPos = motion.selWalk.calibration.startAngleBackwards;
 						} else {
-
-							if(currentAnimation !== selectedFly) { // 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!==selectedFly) {
-									delete currentTransition;
-									currentTransition = null;
-								}
-								switch(currentAnimation) {
-
-									case selectedStand:
-
-										currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
-										break;
-
-									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;
-							}
+							motion.walkWheelPos = motion.selWalk.calibration.startAngleForwards;
 						}
-						animateAvatar(deltaTime, speed, principleDirection);
+
+						switch (motion.curAnim) {
+
+							case motion.selStand: {
+
+								// Standing to Walking
+								motion.curTransition = new Transition(
+									motion.curAnim,
+									motion.selWalk,
+									[], 0.25,
+									{x: 0.5, y: 0.5},
+									{x: 0.5, y: 0.5});
+								break;
+							}
+
+							case motion.selFly:
+							case motion.selFlyUp:
+							case motion.selFlyDown: {
+
+								// 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;
+							}
+
+							default:
+
+								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;
+						} else {
+							motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart;
+						}
+						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.35,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+								}
+
+								case motion.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyUp,
+										[], 0.35,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+								}
+
+								default:
+
+									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.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFlyDown,
+										[], 0.45,
+										{x: 0.5, y: 0.08},
+										{x: 0.28, y: 1});
+									break;
+								}
+
+								default:
+
+									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.selFly:
+								case motion.selFlyUp:
+								case motion.selFlyDown: {
+
+									motion.curTransition = new Transition(
+										motion.curAnim,
+										motion.selFly,
+										[], 0.75,
+										{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)
 
-		} // 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) {
+
+	options = {
+	  position: Camera.getPosition(),
+    volume: 0.3
+	}
+  
+	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;
+				}
+
+				motion.curTransition.walkWheelIncrement = 3;
+
+				// keep the walkwheel turning by setting the walkWheelIncrement
+				// until our feet are tucked nicely underneath us.
+				if (angleToLeftStop < angleToRightStop) {
+					motion.curTransition.walkStopAngle = leftStop;
+				} else {
+					motion.curTransition.walkStopAngle = rightStop;
+				}
+
+			} else {
+
+				// 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);
+
+				} else motion.curTransition.walkWheelIncrement = 0; // sidestep
+			}
+		} } // end motion.curTransition !== nullTransition
 
 
-		// record the frame's stats for later reference
-        var thisMotion = new RecentMotion(localVelocity, acceleration, principleDirection, INTERNAL_STATE);
-		framesHistory.recentMotions.push(thisMotion);
-		framesHistory.recentMotions.shift();
+	// 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;
+		}
 
-		// before we go, populate the stats overlay
-		if( statsOn ) {
+		var tolerance = 1.0;
+		if (motion.walkWheelPos < (strideMaxAt + tolerance) &&
+			motion.walkWheelPos > (strideMaxAt - tolerance)) {
 
-			var cumulativeTimeMS = Math.floor(cumulativeTime*1000);
-			var deltaTimeMS = deltaTime * 1000;
-			var frameExecutionTime = new Date().getTime() - frameStartTime;
-			if(frameExecutionTime>frameExecutionTimeMax) frameExecutionTimeMax = frameExecutionTime;
+			// measure and save stride length
+			var footRPos = MyAvatar.getJointPosition("RightFoot");
+			var footLPos = MyAvatar.getJointPosition("LeftFoot");
+			motion.strideLength = Vec3.distance(footRPos, footLPos);
 
-			var angluarVelocity = Vec3.length(MyAvatar.getAngularVelocity());
+			if (motion.direction === FORWARDS) {
+				motion.curAnim.calibration.strideLengthForwards = motion.strideLength;
+			} else if (motion.direction === BACKWARDS) {
+				motion.curAnim.calibration.strideLengthBackwards = motion.strideLength;
+			}
 
-			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});
+		} else {
 
-			// 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;
+			// 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);
+	// calculate hips translation
+	if (motion.curTransition !== nullTransition) {
 
-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);
+		if (motion.curTransition.walkingAtStart) {
 
-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);
+			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 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);
+			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;
 
-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);
+			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;
 
+			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;
 
-// 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);
+			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;
 
+			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;
 
-// 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);
+		// end if walking at start of transition
 
-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);
+		} 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);
+		// end if walking at start of transition
 
-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);
+		} else {
 
-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);
+		swayOsc = motion.curAnim.joints[0].sway *
+				  Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) +
+				  motion.curAnim.joints[0].swayOffset;
 
-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);
+		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 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);
+				// 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 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);
+		thrustOsc = motion.curAnim.joints[0].thrust *
+					Math.sin(filter.degToRad(cycle * adjFreq * 2 +
+					motion.curAnim.joints[0].thrustPhase)) +
+					motion.curAnim.joints[0].thrustOffset;
+	}
 
-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);
+	// 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 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);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviFront);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviRight);
+	aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviUp);
 
+	MyAvatar.setSkeletonOffset({
+		x: aviTranslationOffset.x,
+		y: aviTranslationOffset.y,
+		z: aviTranslationOffset.z
+	});
 
-// 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);
+	// hips rotation
+	if (motion.curTransition !== nullTransition) {
 
-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);
+		if (motion.curTransition.walkingAtStart) {
 
-// 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."
-                });
+			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;
 
-// 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) {
+			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;
+
+			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);
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d2d5ff2480..e4ab3994ff 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -92,6 +92,7 @@
 #include "scripting/MenuScriptingInterface.h"
 #include "scripting/SettingsScriptingInterface.h"
 #include "scripting/WindowScriptingInterface.h"
+#include "scripting/WebWindowClass.h"
 
 #include "ui/DataWebDialog.h"
 #include "ui/InfoView.h"
@@ -723,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();
@@ -785,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
@@ -2008,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);
     }
@@ -3904,6 +3897,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
     // register `location` on the global object.
     scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
                                        LocationScriptingInterface::locationSetter);
+
+    scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
     
     scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
     scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
@@ -4313,8 +4308,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
@@ -4325,10 +4318,9 @@ bool Application::isVSyncOn() const {
     } else {
         return true;
     }
-    */
-#else
-    return true;
+    */    
 #endif
+    return true;
 }
 
 bool Application::isVSyncEditable() const {
@@ -4343,7 +4335,6 @@ bool Application::isVSyncEditable() const {
         return true;
     }
     */
-#else
 #endif
     return false;
 }
diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index a5f6bd897f..2dc94e6027 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -518,6 +518,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());
@@ -549,14 +576,14 @@ 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);
+                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
@@ -565,6 +592,60 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF
     }
 }
 
+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)) {
+        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(reinterpret_cast<int16_t*>(inputByteArray.data()),
+                         reinterpret_cast<int16_t*>(loopBackByteArray.data()),
+                         inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t),
+                         _inputFormat, _outputFormat);
+    }
+    
+    if (hasLocalReverb) {
+        QByteArray loopbackCopy;
+        if (!hasEcho) {
+            loopbackCopy = loopBackByteArray;
+        }
+        
+        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 = reinterpret_cast<int16_t*>(loopbackCopy.data());
+            for (int i = 0; i < numLoopbackSamples; ++i) {
+                loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i],
+                                                MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
+            }
+        }
+    }
+    
+    if (_loopbackOutputDevice) {
+        _loopbackOutputDevice->write(loopBackByteArray);
+    }
+}
+
 void Audio::handleAudioInput() {
     static char audioDataPacket[MAX_PACKET_SIZE];
 
@@ -609,34 +690,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());
     
@@ -973,30 +1028,7 @@ 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);
     }
 }
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index d6e26864e6..d8e5c5386c 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -268,8 +268,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);
 
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/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 5329ff5c1d..9ceb073167 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
 
diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp
index 2f8ddb1095..1876b6c624 100644
--- a/interface/src/entities/EntityTreeRenderer.cpp
+++ b/interface/src/entities/EntityTreeRenderer.cpp
@@ -81,6 +81,9 @@ 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::checkAndCallPreload);
+    connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::checkAndCallPreload);
+    connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
 }
 
 QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
@@ -770,3 +773,20 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
     _entityScripts.remove(entityID);
 }
 
+void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& 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) {
+    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 ff9066dd6d..7c1c81b6c9 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 changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
+    void checkAndCallPreload(const EntityItemID& entityID);
     
 private:
     QList<Model*> _releasedModels;
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..8d426d067b 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,10 +487,9 @@ 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;
+                    face = subMeshFace;
                     extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
                 }
             }
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;
diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp
new file mode 100644
index 0000000000..d280d8eecf
--- /dev/null
+++ b/interface/src/scripting/WebWindowClass.cpp
@@ -0,0 +1,68 @@
+//
+//  WebWindowClass.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 "WebWindowClass.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);
+}
+
+WebWindowClass::WebWindowClass(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, &WebWindowClass::destroyed, _window, &QWidget::deleteLater);
+}
+
+WebWindowClass::~WebWindowClass() {
+}
+
+void WebWindowClass::setVisible(bool visible) {
+    QMetaObject::invokeMethod(_window, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible));
+}
+
+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(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, &WebWindowClass::deleteLater);
+
+    return engine->newQObject(retVal);
+}
diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h
new file mode 100644
index 0000000000..7b77299774
--- /dev/null
+++ b/interface/src/scripting/WebWindowClass.h
@@ -0,0 +1,51 @@
+//
+//  WebWindowClass.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_WebWindowClass_h
+#define hifi_WebWindowClass_h
+
+#include <QScriptContext>
+#include <QScriptEngine>
+
+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 WebWindowClass : public QObject {
+    Q_OBJECT
+    Q_PROPERTY(QObject* eventBridge READ getEventBridge)
+public:
+    WebWindowClass(const QString& url, int width, int height);
+    ~WebWindowClass();
+
+    static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
+
+public slots:
+    void setVisible(bool visible);
+    ScriptEventBridge* getEventBridge() const { return _eventBridge; }
+
+private:
+    QWidget* _window;
+    ScriptEventBridge* _eventBridge;
+};
+
+#endif
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();
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());
 
diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp
index f4b0bb82b5..fb6ef63647 100644
--- a/interface/src/ui/RearMirrorTools.cpp
+++ b/interface/src/ui/RearMirrorTools.cpp
@@ -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);
 }
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/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
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") {
diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp
index 709591b62b..63384371bd 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
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
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 199bd92030..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);
@@ -168,6 +175,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 +192,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 +306,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 +327,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 +1002,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..1dea2bcd85 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
@@ -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) {
@@ -762,6 +771,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);
                     }
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
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 3f89cdf9ba..2691b10273 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -311,6 +311,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);
@@ -625,7 +630,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()) {
@@ -651,7 +656,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") {
@@ -689,7 +694,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 f96b4655a9..bb279b8887 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; }
@@ -103,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);
 
@@ -131,7 +133,6 @@ protected:
     int _numAvatarSoundSentBytes;
 
 private:
-    QUrl resolveInclude(const QString& include) const;
     void sendAvatarIdentityPacket();
     void sendAvatarBillboardPacket();