diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7f05548ec4..c283bfff17 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,7 +53,7 @@ else ()
   endif ()
 endif(WIN32)
 
-if (NOT MSVC12)
+if ((NOT MSVC12) AND (NOT MSVC14))
   include(CheckCXXCompilerFlag)
   CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
   CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index cfee18941c..e125a44783 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -66,6 +66,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
     // set the logging target to the the CHILD_TARGET_NAME
     LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
 
+    // make sure we output process IDs for a child AC otherwise it's insane to parse
+    LogHandler::getInstance().setShouldOutputPID(true);
+
     // setup our _requestAssignment member variable from the passed arguments
     _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
 
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index d19eb90df6..8c6478b59f 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -39,9 +39,9 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
     _walletUUID(walletUUID),
     _assignmentServerHostname(assignmentServerHostname),
     _assignmentServerPort(assignmentServerPort)
-{    
+{
     qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
-    
+
     // start the Logging class with the parent's target name
     LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
 
@@ -77,13 +77,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) {
     while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) {
         // continue processing events so we can handle a process finishing up
         QCoreApplication::processEvents();
-    }     
+    }
 }
 
 void AssignmentClientMonitor::childProcessFinished() {
     QProcess* childProcess = qobject_cast<QProcess*>(sender());
     qint64 processID = _childProcesses.key(childProcess);
-    
+
     if (processID > 0) {
         qDebug() << "Child process" << processID << "has finished. Removing from internal map.";
         _childProcesses.remove(processID);
@@ -98,17 +98,17 @@ void AssignmentClientMonitor::stopChildProcesses() {
         qDebug() << "Attempting to terminate child process" << childProcess->processId();
         childProcess->terminate();
     }
-    
+
     simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
-    
+
     if (_childProcesses.size() > 0) {
         // ask even more firmly
         foreach(QProcess* childProcess, _childProcesses) {
             qDebug() << "Attempting to kill child process" << childProcess->processId();
             childProcess->kill();
         }
-        
-        simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); 
+
+        simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
     }
 }
 
@@ -122,7 +122,7 @@ void AssignmentClientMonitor::aboutToQuit() {
 void AssignmentClientMonitor::spawnChildClient() {
     QProcess* assignmentClient = new QProcess(this);
 
-    
+
     // unparse the parts of the command-line that the child cares about
     QStringList _childArguments;
     if (_assignmentPool != "") {
@@ -153,7 +153,7 @@ void AssignmentClientMonitor::spawnChildClient() {
 
     // make sure that the output from the child process appears in our output
     assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
-    
+
     assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
 
     // make sure we hear that this process has finished when it does
@@ -194,7 +194,7 @@ void AssignmentClientMonitor::checkSpares() {
             qDebug() << "asking child" << aSpareId << "to exit.";
             SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
             childNode->activateLocalSocket();
-            
+
             QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
             nodeList->writeUnverifiedDatagram(diePacket, childNode);
         }
@@ -239,7 +239,7 @@ void AssignmentClientMonitor::readPendingDatagrams() {
                     // update our records about how to reach this child
                     matchingNode->setLocalSocket(senderSockAddr);
 
-                    QVariantMap packetVariantMap = 
+                    QVariantMap packetVariantMap =
                         JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket)));
                     QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap);
 
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index ea7ac0648b..c3ca6796bc 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -850,7 +850,9 @@ void AudioMixer::run() {
 
         ++_numStatFrames;
 
+        // since we're a while loop we need to help Qt's event processing
         QCoreApplication::processEvents();
+        QCoreApplication::sendPostedEvents(this, 0);
 
         if (_isFinished) {
             break;
diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt
index 28aec6dab7..c28a8e1319 100644
--- a/cmake/externals/polyvox/CMakeLists.txt
+++ b/cmake/externals/polyvox/CMakeLists.txt
@@ -5,7 +5,7 @@ ExternalProject_Add(
   ${EXTERNAL_NAME}
   URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox.zip
   URL_MD5 904b840328278c9b36fa7a14be730c34
-  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
   BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
   LOG_DOWNLOAD 1
   LOG_CONFIGURE 1
diff --git a/cmake/macros/SetupExternalsBinaryDir.cmake b/cmake/macros/SetupExternalsBinaryDir.cmake
index a118dcf543..118df9c8fa 100644
--- a/cmake/macros/SetupExternalsBinaryDir.cmake
+++ b/cmake/macros/SetupExternalsBinaryDir.cmake
@@ -14,12 +14,12 @@ macro(SETUP_EXTERNALS_BINARY_DIR)
   # get a short name for the generator to use in the path
   STRING(REGEX REPLACE " " "-" CMAKE_GENERATOR_FOLDER_NAME ${CMAKE_GENERATOR})
 
-  if (MSVC12)
+  if (MSVC12) 
     set(CMAKE_GENERATOR_FOLDER_NAME "vc12")
-  else ()
-    if (CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
-      set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
-    endif ()
+  elseif (MSVC14)
+    set(CMAKE_GENERATOR_FOLDER_NAME "vc14")
+  elseif(CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
+    set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
   endif ()
 
   set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext")
diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js
index 146f9daca3..7d024e2fd3 100644
--- a/examples/controllers/hydra/gun.js
+++ b/examples/controllers/hydra/gun.js
@@ -99,6 +99,13 @@ var NUM_BUTTONS = 3;
 
 var screenSize = Controller.getViewportDimensions();
 var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
+Script.include(["../../libraries/toolBars.js"]);
+const persistKey = "highfidelity.gun.toolbar.position";
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
 var reticle = Overlays.addOverlay("image", {
                     x: screenSize.x / 2 - (BUTTON_SIZE / 2),
                     y: screenSize.y / 2 - (BUTTON_SIZE / 2),
@@ -108,9 +115,9 @@ var reticle = Overlays.addOverlay("image", {
                     alpha: 1
                 });
 
-var offButton = Overlays.addOverlay("image", {
-                    x: startX,
-                    y: screenSize.y - (BUTTON_SIZE + PADDING),
+var offButton = toolBar.addOverlay("image", {
+                    x: old ? old[0] : startX,
+                    y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
                     width: BUTTON_SIZE,
                     height: BUTTON_SIZE,
                     imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
@@ -118,7 +125,7 @@ var offButton = Overlays.addOverlay("image", {
                 });
 
 startX += BUTTON_SIZE + PADDING;
-var platformButton = Overlays.addOverlay("image", {
+var platformButton = toolBar.addOverlay("image", {
                     x: startX,
                     y: screenSize.y - (BUTTON_SIZE + PADDING),
                     width: BUTTON_SIZE,
@@ -128,7 +135,7 @@ var platformButton = Overlays.addOverlay("image", {
                 });
 
 startX += BUTTON_SIZE + PADDING;
-var gridButton = Overlays.addOverlay("image", {
+var gridButton = toolBar.addOverlay("image", {
                     x: startX,
                     y: screenSize.y - (BUTTON_SIZE + PADDING),
                     width: BUTTON_SIZE,
@@ -493,10 +500,8 @@ function mousePressEvent(event) {
 }
 
 function scriptEnding() {
-    Overlays.deleteOverlay(reticle); 
-    Overlays.deleteOverlay(offButton);
-    Overlays.deleteOverlay(platformButton);
-    Overlays.deleteOverlay(gridButton);
+    Overlays.deleteOverlay(reticle);
+    toolBar.cleanup();
     Overlays.deleteOverlay(pointer[0]);
     Overlays.deleteOverlay(pointer[1]);
     Overlays.deleteOverlay(text);
diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js
index 8f32b80bba..61bed8d9b1 100644
--- a/examples/defaultScripts.js
+++ b/examples/defaultScripts.js
@@ -18,3 +18,4 @@ Script.load("notifications.js");
 Script.load("users.js");
 Script.load("grab.js");
 Script.load("pointer.js");
+Script.load("directory.js");
diff --git a/examples/dialTone.js b/examples/dialTone.js
index 135acb17f4..b55e654f25 100644
--- a/examples/dialTone.js
+++ b/examples/dialTone.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Stephen Birarda on 06/08/15.
+//  Added disconnect HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.
@@ -10,14 +11,19 @@
 //
 
 // setup the local sound we're going to use
-var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav");
+var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav");
+var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav");
 
 // setup the options needed for that sound
 var connectSoundOptions = {
     localOnly: true
-}
+};
 
 // play the sound locally once we get the first audio packet from a mixer
 Audio.receivedFirstPacket.connect(function(){
     Audio.playSound(connectSound, connectSoundOptions);
 });
+
+Audio.disconnected.connect(function(){
+    Audio.playSound(disconnectSound, connectSoundOptions);
+});
diff --git a/examples/dice.js b/examples/dice.js
index 515019e740..ac5d1b7426 100644
--- a/examples/dice.js
+++ b/examples/dice.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Philip Rosedale on February 2, 2015
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Press the dice button to throw some dice from the center of the screen. 
@@ -31,9 +32,16 @@ var screenSize = Controller.getViewportDimensions();
 var BUTTON_SIZE = 32;
 var PADDING = 3;
 
-var offButton = Overlays.addOverlay("image", {
-  x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
-  y: screenSize.y - (BUTTON_SIZE + PADDING),
+Script.include(["libraries/toolBars.js"]);
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+const persistKey = "highfidelity.dice.toolbar.position";
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
+var offButton = toolBar.addOverlay("image", {
+  x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
+  y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
   width: BUTTON_SIZE,
   height: BUTTON_SIZE,
   imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
@@ -45,7 +53,7 @@ var offButton = Overlays.addOverlay("image", {
   alpha: 1
 });
 
-var deleteButton = Overlays.addOverlay("image", {
+var deleteButton = toolBar.addOverlay("image", {
   x: screenSize.x / 2 - BUTTON_SIZE,
   y: screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
@@ -59,7 +67,7 @@ var deleteButton = Overlays.addOverlay("image", {
   alpha: 1
 });
 
-var diceButton = Overlays.addOverlay("image", {
+var diceButton = toolBar.addOverlay("image", {
   x: screenSize.x / 2 + PADDING,
   y: screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
@@ -140,10 +148,8 @@ function mousePressEvent(event) {
 }
 
 function scriptEnding() {
-  Overlays.deleteOverlay(offButton);
-  Overlays.deleteOverlay(diceButton);
-  Overlays.deleteOverlay(deleteButton);
+  toolBar.cleanup();
 }
 
 Controller.mousePressEvent.connect(mousePressEvent);
-Script.scriptEnding.connect(scriptEnding);
\ No newline at end of file
+Script.scriptEnding.connect(scriptEnding);
diff --git a/examples/directory.js b/examples/directory.js
new file mode 100644
index 0000000000..b1fac19e8b
--- /dev/null
+++ b/examples/directory.js
@@ -0,0 +1,92 @@
+//
+//  directory.js
+//  examples
+//
+//  Created by David Rowe on 8 Jun 2015
+//  Copyright 2015 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
+//
+
+Script.include("libraries/globals.js");
+
+var directory = (function () {
+
+    var DIRECTORY_URL = "https://metaverse.highfidelity.com/directory",
+        directoryWindow,
+        DIRECTORY_BUTTON_URL = HIFI_PUBLIC_BUCKET + "images/tools/directory.svg",
+        BUTTON_WIDTH = 50,
+        BUTTON_HEIGHT = 50,
+        BUTTON_ALPHA = 0.9,
+        BUTTON_MARGIN = 8,
+        directoryButton,
+        EDIT_TOOLBAR_BUTTONS = 10,  // Number of buttons in edit.js toolbar
+        viewport;
+
+    function updateButtonPosition() {
+        Overlays.editOverlay(directoryButton, {
+            x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN,
+            y: (viewport.y - (EDIT_TOOLBAR_BUTTONS + 1) * (BUTTON_HEIGHT + BUTTON_MARGIN) - BUTTON_MARGIN) / 2 - 1
+        });
+    }
+
+    function onMousePressEvent(event) {
+        var clickedOverlay;
+
+        clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
+
+        if (clickedOverlay === directoryButton) {
+            if (directoryWindow.url !== DIRECTORY_URL) {
+                directoryWindow.setURL(DIRECTORY_URL);
+            }
+            directoryWindow.setVisible(true);
+            directoryWindow.raise();
+        }
+    }
+
+    function onDomainChanged() {
+        directoryWindow.setVisible(false);
+    }
+
+    function onScriptUpdate() {
+        var oldViewport = viewport;
+
+        viewport = Controller.getViewportDimensions();
+
+        if (viewport.x !== oldViewport.x || viewport.y !== oldViewport.y) {
+            updateButtonPosition();
+        }
+    }
+
+    function setUp() {
+        viewport = Controller.getViewportDimensions();
+
+        directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false);
+        directoryWindow.setVisible(false);
+
+        directoryButton = Overlays.addOverlay("image", {
+            imageURL: DIRECTORY_BUTTON_URL,
+            width: BUTTON_WIDTH,
+            height: BUTTON_HEIGHT,
+            x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN,
+            y: BUTTON_MARGIN,
+            alpha: BUTTON_ALPHA,
+            visible: true
+        });
+
+        updateButtonPosition();
+
+        Controller.mousePressEvent.connect(onMousePressEvent);
+        Window.domainChanged.connect(onDomainChanged);
+
+        Script.update.connect(onScriptUpdate);
+    }
+
+    function tearDown() {
+        Overlays.deleteOverlay(directoryButton);
+    }
+
+    setUp();
+    Script.scriptEnding.connect(tearDown);
+}());
\ No newline at end of file
diff --git a/examples/edit.js b/examples/edit.js
index 6055400289..2974397cde 100644
--- a/examples/edit.js
+++ b/examples/edit.js
@@ -2,6 +2,7 @@
 //  examples
 //
 //  Created by Brad Hefta-Gaub on 10/2/14.
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing
@@ -320,6 +321,7 @@ var toolBar = (function () {
         }
     }
 
+    const persistKey = "highfidelity.edit.toolbar.position";
     that.move = function () {
         var newViewPort,
             toolsX,
@@ -330,6 +332,15 @@ var toolBar = (function () {
         if (toolBar === undefined) {
             initialize();
 
+            toolBar.save = function () {
+                Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+            };
+            var old = JSON.parse(Settings.getValue(persistKey) || '0');
+            if (old) {
+                windowDimensions = newViewPort;
+                toolBar.move(old[0], old[1]);
+                return;
+            }
         } else if (windowDimensions.x === newViewPort.x &&
                    windowDimensions.y === newViewPort.y) {
             return;
diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js
new file mode 100644
index 0000000000..26a1999bbb
--- /dev/null
+++ b/examples/example/scripts/controllerScriptingExamples.js
@@ -0,0 +1,96 @@
+//
+//  controllerScriptingExamples.js
+//  examples
+//
+//  Created by Sam Gondelman on 6/2/15
+//  Copyright 2015 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
+//
+
+// Assumes you only have the default keyboard connected
+
+// Resets every device to its default key bindings:
+Controller.resetAllDeviceBindings();
+
+// Query all actions
+print("All Actions: \n" + Controller.getAllActions());
+
+// Each action stores:
+// action: int representation of enum
+print("Action 5 int: \n" + Controller.getAllActions()[5].action);
+
+// actionName: string representation of enum
+print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);
+
+// inputChannels: list of all inputchannels that control that action
+print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");
+
+
+// Each input channel stores:
+// action: Action that this InputChannel maps to
+print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);
+
+// scale: sensitivity of input
+print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);
+
+// input and modifier: Inputs
+print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");
+
+
+// Each Input stores:
+// device: device of input
+print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);
+
+// channel: channel of input
+print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);
+
+// type: type of input (Unknown, Button, Axis, Joint)
+print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);
+
+// id: id of input
+print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");
+
+
+// You can get the name of a device from its id
+print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));
+
+// You can also get all of a devices input channels
+print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
+
+
+// Modifying properties:
+// The following code will switch the "w" and "s" key functionality and adjust their scales
+var s = Controller.getAllActions()[0].inputChannels[0];
+var w = Controller.getAllActions()[1].inputChannels[0];
+
+// You must remove an input controller before modifying it so the old input controller isn't registered anymore
+// removeInputChannel and addInputChannel return true if successful, false otherwise
+Controller.removeInputChannel(s);
+Controller.removeInputChannel(w);
+print(s.scale);
+s.action = 1;
+s.scale = .01;
+
+w.action = 0;
+w.scale = 10000;
+Controller.addInputChannel(s);
+Controller.addInputChannel(w);
+print(s.scale);
+
+// You can get all the available inputs for any device
+// Each AvailableInput has:
+// input: the Input itself
+// inputName: string representing the input
+var availableInputs = Controller.getAvailableInputs(1);
+for (i = 0; i < availableInputs.length; i++) {
+	print(availableInputs[i].inputName);
+}
+
+// You can modify key bindings by using these avaiable inputs
+// This will replace e (up) with 6
+var e = Controller.getAllActions()[5].inputChannels[0];
+Controller.removeInputChannel(e);
+e.input = availableInputs[6].input;
+Controller.addInputChannel(e);
\ No newline at end of file
diff --git a/examples/grab.js b/examples/grab.js
index 306af86c68..f64aaa30ee 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -4,7 +4,7 @@
 //  Created by Eric Levin on May 1, 2015
 //  Copyright 2015 High Fidelity, Inc.
 //
-//  Grab's physically moveable entities with the mouse, by applying a spring force. 
+//  Grab's physically moveable entities with the mouse, by applying a spring force.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -20,7 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40;
 // NOTE: to improve readability global variable names start with 'g'
 var gIsGrabbing = false;
 var gGrabbedEntity = null;
-var gPrevMouse = {x: 0, y: 0};
+var gActionID = null;
 var gEntityProperties;
 var gStartPosition;
 var gStartRotation;
@@ -31,20 +31,20 @@ var gPlaneNormal = ZERO_VEC3;
 // gMaxGrabDistance is a function of the size of the object.
 var gMaxGrabDistance;
 
-// gGrabMode defines the degrees of freedom of the grab target positions 
-// relative to gGrabStartPosition options include: 
+// gGrabMode defines the degrees of freedom of the grab target positions
+// relative to gGrabStartPosition options include:
 //     xzPlane  (default)
 //     verticalCylinder  (SHIFT)
 //     rotate  (CONTROL)
 // Modes to eventually support?:
-//     xyPlane 
-//     yzPlane 
+//     xyPlane
+//     yzPlane
 //     polar
 //     elevationAzimuth
-var gGrabMode = "xzplane"; 
+var gGrabMode = "xzplane";
 
-// gGrabOffset allows the user to grab an object off-center.  It points from ray's intersection 
-// with the move-plane to object center (at the moment the grab is initiated).  Future target positions 
+// gGrabOffset allows the user to grab an object off-center.  It points from ray's intersection
+// with the move-plane to object center (at the moment the grab is initiated).  Future target positions
 // are relative to the ray's intersection by the same offset.
 var gGrabOffset = { x: 0, y: 0, z: 0 };
 
@@ -53,13 +53,14 @@ var gTargetRotation;
 var gLiftKey = false; // SHIFT
 var gRotateKey = false; // CONTROL
 
+var gInitialMouse = { x: 0, y: 0 };
 var gPreviousMouse = { x: 0, y: 0 };
 var gMouseCursorLocation = { x: 0, y: 0 };
 var gMouseAtRotateStart = { x: 0, y: 0 };
 
 var gBeaconHeight = 0.10;
 
-var gAngularVelocity = ZERO_VEC3;
+// var gAngularVelocity = ZERO_VEC3;
 
 // TODO: play sounds again when we aren't leaking AudioInjector threads
 // var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
@@ -140,6 +141,10 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) {
 }
 
 function computeNewGrabPlane() {
+    if (!gIsGrabbing) {
+        return;
+    }
+
     var maybeResetMousePosition = false;
     if (gGrabMode !== "rotate") {
         gMouseAtRotateStart = gMouseCursorLocation;
@@ -162,7 +167,7 @@ function computeNewGrabPlane() {
     var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition());
     xzOffset.y = 0;
     gXzDistanceToGrab = Vec3.length(xzOffset);
-    
+
     if (gGrabMode !== "rotate" && maybeResetMousePosition) {
         // we reset the mouse position whenever we stop rotating
         Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y);
@@ -173,6 +178,7 @@ function mousePressEvent(event) {
     if (!event.isLeftButton) {
         return;
     }
+    gInitialMouse = {x: event.x, y: event.y };
     gPreviousMouse = {x: event.x, y: event.y };
 
     var pickRay = Camera.computePickRay(event.x, event.y);
@@ -189,12 +195,13 @@ function mousePressEvent(event) {
 
     var clickedEntity = pickResults.entityID;
     var entityProperties = Entities.getEntityProperties(clickedEntity)
-    var objectPosition = entityProperties.position;
+    gStartPosition = entityProperties.position;
+    gStartRotation = entityProperties.rotation;
     var cameraPosition = Camera.getPosition();
 
     gBeaconHeight = Vec3.length(entityProperties.dimensions);
     gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
-    if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
+    if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) {
         // don't allow grabs of things far away
         return;
     }
@@ -205,20 +212,20 @@ function mousePressEvent(event) {
     gGrabbedEntity = clickedEntity;
     gCurrentPosition = entityProperties.position;
     gOriginalGravity = entityProperties.gravity;
-    gTargetPosition = objectPosition;
+    gTargetPosition = gStartPosition;
 
     // compute the grab point
-    var nearestPoint = Vec3.subtract(objectPosition, cameraPosition);
+    var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition);
     var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
     nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
     gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint);
 
     // compute the grab offset
-    gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane);
+    gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane);
 
     computeNewGrabPlane();
 
-    updateDropLine(objectPosition);
+    updateDropLine(gStartPosition);
 
     // TODO: play sounds again when we aren't leaking AudioInjector threads
     //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
@@ -231,6 +238,8 @@ function mouseReleaseEvent() {
         }
 
         gIsGrabbing = false
+        Entities.deleteAction(gGrabbedEntity, gActionID);
+        gActionID = null;
 
         Overlays.editOverlay(gBeacon, { visible: false });
 
@@ -250,18 +259,24 @@ function mouseMoveEvent(event) {
         gOriginalGravity = entityProperties.gravity;
     }
 
+    var actionArgs = {};
+
     if (gGrabMode === "rotate") {
         var deltaMouse = { x: 0, y: 0 };
-        var dx = event.x - gPreviousMouse.x;
-        var dy = event.y - gPreviousMouse.y;
-
+        var dx = event.x - gInitialMouse.x;
+        var dy = event.y - gInitialMouse.y;
         var orientation = Camera.getOrientation();
         var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
         dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
         var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
-        var axis = Vec3.normalize(axis);
-        var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
-        gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
+        axis = Vec3.normalize(axis);
+        var ROTATE_STRENGTH = 0.4; // magic number tuned by hand
+        var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy));
+        var deltaQ = Quat.angleAxis(angle, axis);
+        // var qZero = entityProperties.rotation;
+        var qZero = gStartRotation;
+        var qOne = Quat.multiply(deltaQ, qZero);
+        actionArgs = {targetRotation: qOne, angularTimeScale: 0.1};
     } else {
         var newTargetPosition;
         if (gGrabMode === "verticalCylinder") {
@@ -284,9 +299,18 @@ function mouseMoveEvent(event) {
             }
         }
         gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset);
+        actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1};
     }
     gPreviousMouse = { x: event.x, y: event.y };
     gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() };
+
+    if (!gActionID) {
+        gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs);
+    } else {
+        Entities.updateAction(gGrabbedEntity, gActionID, actionArgs);
+    }
+
+    updateDropLine(gTargetPosition);
 }
 
 function keyReleaseEvent(event) {
@@ -309,38 +333,8 @@ function keyPressEvent(event) {
     computeNewGrabPlane();
 }
 
-function update(deltaTime) {
-    if (!gIsGrabbing) {
-        return;
-    }
-
-    var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
-    gCurrentPosition = entityProperties.position;
-    if (gGrabMode === "rotate") {
-        gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
-        Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
-    } 
-
-    // always push toward linear grab position, even when rotating
-    var newVelocity = ZERO_VEC3;
-    var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
-    var delta = Vec3.length(dPosition);
-    if (delta > CLOSE_ENOUGH) {
-        var MAX_POSITION_DELTA = 4.0;
-        if (delta > MAX_POSITION_DELTA) {
-            dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
-        }
-        // desired speed is proportional to displacement by the inverse of timescale
-        // (for critically damped motion)
-        newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
-    }
-    Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
-    updateDropLine(gTargetPosition);
-}
-
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
 Controller.mousePressEvent.connect(mousePressEvent);
 Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
 Controller.keyPressEvent.connect(keyPressEvent);
 Controller.keyReleaseEvent.connect(keyReleaseEvent);
-Script.update.connect(update);
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index d9cad0feff..f029088b1a 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -360,6 +360,10 @@
             var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
             var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
 
+            var elHyperlinkHref = document.getElementById("property-hyperlink-href");
+            var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
+
+
 
             if (window.EventBridge !== undefined) {
                 EventBridge.scriptEventReceived.connect(function(data) {
@@ -467,6 +471,9 @@
                             elScriptURL.value = properties.script;
                             elUserData.value = properties.userData;
 
+                            elHyperlinkHref.value = properties.href;
+                            elHyperlinkDescription.value = properties.description;
+
                             for (var i = 0; i < allSections.length; i++) {
                                 for (var j = 0; j < allSections[i].length; j++) {
                                     allSections[i][j].style.display = 'none';
@@ -612,6 +619,8 @@
 
             elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
             elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
+            elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
+            elHyperlinkDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
             elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
 
             var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
@@ -850,7 +859,6 @@
             elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
             elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
 
-
             elMoveSelectionToGrid.addEventListener("click", function() {
                 EventBridge.emitWebEvent(JSON.stringify({
                     type: "action",
@@ -937,6 +945,18 @@
                 <input type="text" id="property-name"></input>
             </div>
         </div>
+
+        <div class="property">
+            <div class="label">Hyperlink</div>
+            <div class="input-area">Href<br></div>
+            <div class="value">
+                <input id="property-hyperlink-href" class="url"></input>
+            </div>
+            <div class="input-area">Description<br></div> <div class="value">
+                <input id="property-hyperlink-description" class="url"></input>
+            </div>
+        </div>
+
         <div class="property">
             <span class="label">Locked</span>
             <span class="value">
diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index d12c9dae3c..d5e2b24f36 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -304,7 +304,8 @@ SelectionDisplay = (function () {
                     visible: false,
                     dashed: true,
                     lineWidth: 2.0,
-                    ignoreRayIntersection: true // this never ray intersects
+                    ignoreRayIntersection: true, // this never ray intersects
+                    drawInFront: true
                 });
 
     var selectionBox = Overlays.addOverlay("cube", {
diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js
index 670a69dec7..6e72795ea5 100644
--- a/examples/libraries/toolBars.js
+++ b/examples/libraries/toolBars.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Clément Brisset on 5/7/14.
+//  Persistable drag position by HRS 6/11/15.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.
@@ -236,6 +237,7 @@ ToolBar = function(x, y, direction) {
                 y: y - ToolBar.SPACING
             });
         }
+        this.save();
     }
     
     this.setAlpha = function(alpha, tool) {
@@ -313,9 +315,8 @@ ToolBar = function(x, y, direction) {
     this.cleanup = function() {
         for(var tool in this.tools) {
             this.tools[tool].cleanup();
-            delete this.tools[tool];
         }
-        
+
         if (this.back != null) {
             Overlays.deleteOverlay(this.back);
             this.back = null;
@@ -327,7 +328,71 @@ ToolBar = function(x, y, direction) {
         this.width = 0;
         this.height = 0;
     }
+
+    var that = this;
+    this.contains = function (xOrPoint, optionalY) {
+        var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint,
+            y = (optionalY === undefined) ? xOrPoint.y : optionalY;
+        return (that.x <= x) && (x <= (that.x + that.width)) &&
+            (that.y <= y) && (y <= (that.y + that.height));
+    }
+    that.hover = function (enable) {
+        that.isHovering = enable;
+        if (that.back) {
+            if (enable) {
+                that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha');
+            }
+            Overlays.editOverlay(this.back, {
+                visible: enable,
+                backgroundAlpha: enable ? 0.5 : that.oldAlpha
+            });
+        }
+    };
+    // These are currently only doing that which is necessary for toolbar hover and toolbar drag.
+    // They have not yet been extended to tool hover/click/release, etc.
+    this.mousePressEvent = function (event) {
+        if (!that.contains(event)) {
+            that.mightBeDragging = false;
+            return;
+        }
+        that.mightBeDragging = true;
+        that.dragOffsetX = that.x - event.x;
+        that.dragOffsetY = that.y - event.y;
+    };
+    this.mouseMove  = function (event) {
+        if (!that.mightBeDragging || !event.isLeftButton) {
+            that.mightBeDragging = false;
+            if (!that.contains(event)) {
+                if (that.isHovering) {
+                    that.hover(false);
+                }
+                return;
+            }
+            if (!that.isHovering) {
+                that.hover(true);
+            }
+            return;
+        }
+        that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
+    };
+    Controller.mousePressEvent.connect(this.mousePressEvent);
+    Controller.mouseMoveEvent.connect(this.mouseMove);
+    // Called on move. A different approach would be to have all this on the prototype,
+    // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save.
+    this.save = function () { };
+    // This compatability hack breaks the model, but makes converting existing scripts easier:
+    this.addOverlay = function (ignored, oldSchoolProperties) {
+        var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy
+        if (that.numberOfTools() === 0) {
+            that.move(properties.x, properties.y);
+        }
+        delete properties.x;
+        delete properties.y;
+        var index = that.addTool(properties);
+        var id = that.tools[index].overlay();
+        return id;
+    }
 }
 ToolBar.SPACING = 4;
 ToolBar.VERTICAL = 0;
-ToolBar.HORIZONTAL = 1;
\ No newline at end of file
+ToolBar.HORIZONTAL = 1;
diff --git a/examples/paint.js b/examples/paint.js
index 8bba4e2571..c0cc93afc7 100644
--- a/examples/paint.js
+++ b/examples/paint.js
@@ -12,8 +12,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 Script.include('lineRider.js')
-var MAX_POINTS_PER_LINE = 80;
-
+var MAX_POINTS_PER_LINE = 30;
+var LINE_LIFETIME =  60 * 5  //5 minute lifetime
 
 var colorPalette = [{
   red: 236,
@@ -120,10 +120,12 @@ function MousePaint() {
         y: 10,
         z: 10
       },
-      lineWidth: LINE_WIDTH
+      lineWidth: LINE_WIDTH,
+      lifetime: LINE_LIFETIME
     });
     points = [];
     if (point) {
+
       points.push(point);
       path.push(point);
     }
@@ -133,22 +135,22 @@ function MousePaint() {
 
   function mouseMoveEvent(event) {
 
+    if (!isDrawing) {
+      return;
+    }
 
     var pickRay = Camera.computePickRay(event.x, event.y);
     var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
     var point = Vec3.sum(Camera.getPosition(), addVector);
+    points.push(point);
+    path.push(point);
     Entities.editEntity(line, {
       linePoints: points
     });
     Entities.editEntity(brush, {
       position: point
     });
-    if (!isDrawing) {
-      return;
-    }
 
-    points.push(point);
-    path.push(point);
 
     if (points.length === MAX_POINTS_PER_LINE) {
       //We need to start a new line!
@@ -253,7 +255,6 @@ function HydraPaint() {
   var maxLineWidth = 10;
   var currentLineWidth = minLineWidth;
   var MIN_PAINT_TRIGGER_THRESHOLD = .01;
-  var LINE_LIFETIME = 20;
   var COLOR_CHANGE_TIME_FACTOR = 0.1;
 
   var RIGHT_BUTTON_1 = 7
@@ -330,7 +331,7 @@ function HydraPaint() {
           z: 10
         },
         lineWidth: 5,
-        // lifetime: LINE_LIFETIME
+        lifetime: LINE_LIFETIME
       });
       this.points = [];
       if (point) {
diff --git a/examples/pointer.js b/examples/pointer.js
index cca46709ee..ea6b0c233f 100644
--- a/examples/pointer.js
+++ b/examples/pointer.js
@@ -3,6 +3,7 @@
 //
 //  Created by Seth Alves on May 15th
 //  Modified by Eric Levin on June 4
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Provides a pointer with option to draw on surfaces
@@ -31,9 +32,16 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
 var screenSize = Controller.getViewportDimensions();
 
 var userCanPoint = false;
-var pointerButton = Overlays.addOverlay("image", {
-  x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
-  y: screenSize.y - (BUTTON_SIZE + PADDING),
+Script.include(["libraries/toolBars.js"]);
+const persistKey = "highfidelity.pointer.toolbar.position";
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
+var pointerButton = toolBar.addOverlay("image", {
+  x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
+  y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
   height: BUTTON_SIZE,
   imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png",
@@ -150,4 +158,4 @@ Script.scriptEnding.connect(cleanup);
 
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
 Controller.mousePressEvent.connect(mousePressEvent);
-Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
\ No newline at end of file
+Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
diff --git a/examples/stick.js b/examples/stick.js
new file mode 100644
index 0000000000..ea1f3439a9
--- /dev/null
+++ b/examples/stick.js
@@ -0,0 +1,92 @@
+//  stick.js
+//  examples
+//
+//  Created by Seth Alves on 2015-6-10
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Allow avatar to hold a stick
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var hand = "left";
+var nullActionID = "00000000-0000-0000-0000-000000000000";
+var controllerID;
+var controllerActive;
+var stickID = null;
+var actionID = nullActionID;
+// sometimes if this is run immediately the stick doesn't get created?  use a timer.
+Script.setTimeout(function() {
+    stickID = Entities.addEntity({
+        type: "Model",
+        modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
+        compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
+        dimensions: {x: .11, y: .11, z: .59},
+        position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
+        rotation: MyAvatar.orientation,
+        damping: .1,
+        collisionsWillMove: true
+    });
+    actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
+                                                    hand: hand,
+                                                    timeScale: 0.15});
+}, 3000);
+
+
+function cleanUp() {
+    Entities.deleteEntity(stickID);
+}
+
+
+function positionStick(stickOrientation) {
+    var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
+    var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
+    Entities.updateAction(stickID, actionID, {relativePosition: offset,
+                                              relativeRotation: stickOrientation});
+}
+
+
+function mouseMoveEvent(event) {
+    if (!stickID || actionID == nullActionID) {
+        return;
+    }
+    var windowCenterX = Window.innerWidth / 2;
+    var windowCenterY = Window.innerHeight / 2;
+    var mouseXCenterOffset = event.x - windowCenterX;
+    var mouseYCenterOffset = event.y - windowCenterY;
+    var mouseXRatio = mouseXCenterOffset / windowCenterX;
+    var mouseYRatio = mouseYCenterOffset / windowCenterY;
+
+    var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0);
+    positionStick(stickOrientation);
+}
+
+
+function initControls(){
+    if (hand == "right") {
+        controllerID = 3; // right handed
+    } else {
+        controllerID = 4; // left handed
+    }
+}
+
+
+function update(deltaTime){
+    var palmPosition = Controller.getSpatialControlPosition(controllerID);
+    controllerActive = (Vec3.length(palmPosition) > 0);
+    if(!controllerActive){
+        return;
+    }
+
+    stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
+    var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
+    stickOrientation = Quat.multiply(stickOrientation, adjustment);
+
+    positionStick(stickOrientation);
+}
+
+
+Script.scriptEnding.connect(cleanUp);
+Controller.mouseMoveEvent.connect(mouseMoveEvent);
+Script.update.connect(update);
diff --git a/interface/resources/images/reticleLink.png b/interface/resources/images/reticleLink.png
new file mode 100644
index 0000000000..b36ee79d00
Binary files /dev/null and b/interface/resources/images/reticleLink.png differ
diff --git a/interface/resources/images/reticleWhite.png b/interface/resources/images/reticleWhite.png
new file mode 100644
index 0000000000..4c35a186b2
Binary files /dev/null and b/interface/resources/images/reticleWhite.png differ
diff --git a/interface/resources/sounds/goodbye.wav b/interface/resources/sounds/goodbye.wav
new file mode 100644
index 0000000000..bd6c51a5c0
Binary files /dev/null and b/interface/resources/sounds/goodbye.wav differ
diff --git a/interface/resources/sounds/hello.wav b/interface/resources/sounds/hello.wav
new file mode 100644
index 0000000000..6269dab5db
Binary files /dev/null and b/interface/resources/sounds/hello.wav differ
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 560dccb238..662a8603d3 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -57,6 +57,7 @@
 
 #include <AccountManager.h>
 #include <AddressManager.h>
+#include <CursorManager.h>
 #include <AmbientOcclusionEffect.h>
 #include <AudioInjector.h>
 #include <AutoUpdater.h>
@@ -101,6 +102,7 @@
 #include "ModelPackager.h"
 #include "Util.h"
 #include "InterfaceLogging.h"
+#include "InterfaceActionFactory.h"
 
 #include "avatar/AvatarManager.h"
 
@@ -259,6 +261,7 @@ bool setupEssentials(int& argc, char** argv) {
 
     DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
     DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
+    DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
 
     Setting::init();
 
@@ -296,7 +299,8 @@ bool setupEssentials(int& argc, char** argv) {
     auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
     auto offscreenUi = DependencyManager::set<OffscreenUi>();
     auto autoUpdater = DependencyManager::set<AutoUpdater>();
-    auto pathUtils =  DependencyManager::set<PathUtils>();
+    auto pathUtils = DependencyManager::set<PathUtils>();
+    auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
 
     return true;
 }
@@ -424,6 +428,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
     connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
     connect(audioIO.data(), &AudioClient::receivedFirstPacket,
             &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket);
+    connect(audioIO.data(), &AudioClient::disconnected,
+            &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected);
 
     audioThread->start();
 
@@ -527,8 +533,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
     _window->setVisible(true);
     _glWidget->setFocusPolicy(Qt::StrongFocus);
     _glWidget->setFocus();
+#ifdef Q_OS_MAC
+    // OSX doesn't seem to provide for hiding the cursor only on the GL widget
+    _window->setCursor(Qt::BlankCursor);
+#else
+    // On windows and linux, hiding the top level cursor also means it's invisible
+    // when hovering over the window menu, which is a pain, so only hide it for
+    // the GL surface
     _glWidget->setCursor(Qt::BlankCursor);
-
+#endif
+    
     // enable mouse tracking; otherwise, we only get drag events
     _glWidget->setMouseTracking(true);
 
@@ -948,12 +962,15 @@ void Application::paintGL() {
         displaySide(&renderArgs, _myCamera);
         glPopMatrix();
 
+        renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
         if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
             _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos()));
         } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
             renderRearViewMirror(&renderArgs, _mirrorViewRect);       
         }
 
+        renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
+
         auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs);
 
         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
@@ -1234,9 +1251,20 @@ void Application::keyPressEvent(QKeyEvent* event) {
                 }
                 break;
 
-            case Qt::Key_Apostrophe:
-                resetSensors();
+            case Qt::Key_Apostrophe: {
+                if (isMeta) {
+                    auto cursor = Cursor::Manager::instance().getCursor();
+                    auto curIcon = cursor->getIcon();
+                    if (curIcon == Cursor::Icon::DEFAULT) {
+                        cursor->setIcon(Cursor::Icon::LINK);
+                    } else {
+                        cursor->setIcon(Cursor::Icon::DEFAULT);
+                    }
+                } else {
+                    resetSensors();
+                }
                 break;
+            }
 
             case Qt::Key_A:
                 if (isShifted) {
@@ -1360,12 +1388,27 @@ void Application::keyPressEvent(QKeyEvent* event) {
             case Qt::Key_Slash:
                 Menu::getInstance()->triggerOption(MenuOption::Stats);
                 break;
-            case Qt::Key_Plus:
-                _myAvatar->increaseSize();
+
+            case Qt::Key_Plus: {
+                if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) {
+                    auto& cursorManager = Cursor::Manager::instance();
+                    cursorManager.setScale(cursorManager.getScale() * 1.1f);
+                } else {
+                    _myAvatar->increaseSize();
+                }
                 break;
-            case Qt::Key_Minus:
-                _myAvatar->decreaseSize();
+            }
+
+            case Qt::Key_Minus: {
+                if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) {
+                    auto& cursorManager = Cursor::Manager::instance();
+                    cursorManager.setScale(cursorManager.getScale() / 1.1f);
+                } else {
+                    _myAvatar->decreaseSize();
+                }
                 break;
+            }
+
             case Qt::Key_Equal:
                 _myAvatar->resetSize();
                 break;
@@ -3405,7 +3448,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
                 "Application::displaySide() ... entities...");
 
             RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
-            RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE;
 
             if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
                 renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
@@ -3414,10 +3456,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
                 renderDebugFlags =
                     (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
             }
-            if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
-                renderMode = RenderArgs::MIRROR_RENDER_MODE;
-            }
-            renderArgs->_renderMode = renderMode;
             renderArgs->_debugFlags = renderDebugFlags;
             _entities.render(renderArgs);
         }
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 4e1127f410..c9e5bea76e 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -148,6 +148,7 @@ public:
     static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
     static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); }
     static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); }
+    static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; }
     static void initPlugins();
     static void shutdownPlugins();
 
diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp
new file mode 100644
index 0000000000..08afc3581c
--- /dev/null
+++ b/interface/src/InterfaceActionFactory.cpp
@@ -0,0 +1,49 @@
+//
+//  InterfaceActionFactory.cpp
+//  libraries/entities/src
+//
+//  Created by Seth Alves on 2015-6-2
+//  Copyright 2015 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 <avatar/AvatarActionHold.h>
+#include <ObjectActionPullToPoint.h>
+#include <ObjectActionSpring.h>
+
+#include "InterfaceActionFactory.h"
+
+
+EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
+                                                    EntityActionType type,
+                                                    QUuid id,
+                                                    EntityItemPointer ownerEntity,
+                                                    QVariantMap arguments) {
+    EntityActionPointer action = nullptr;
+    switch (type) {
+        case ACTION_TYPE_NONE:
+            return nullptr;
+        case ACTION_TYPE_PULL_TO_POINT:
+            action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
+            break;
+        case ACTION_TYPE_SPRING:
+            action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
+            break;
+        case ACTION_TYPE_HOLD:
+            action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
+            break;
+    }
+
+    bool ok = action->updateArguments(arguments);
+    if (ok) {
+        ownerEntity->addAction(simulation, action);
+        return action;
+    }
+
+    action = nullptr;
+    return action;
+}
diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h
new file mode 100644
index 0000000000..5848df4635
--- /dev/null
+++ b/interface/src/InterfaceActionFactory.h
@@ -0,0 +1,28 @@
+//
+//  InterfaceActionFactory.cpp
+//  interface/src/
+//
+//  Created by Seth Alves on 2015-6-10
+//  Copyright 2015 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_InterfaceActionFactory_h
+#define hifi_InterfaceActionFactory_h
+
+#include "EntityActionFactoryInterface.h"
+
+class InterfaceActionFactory : public EntityActionFactoryInterface {
+public:
+    InterfaceActionFactory() : EntityActionFactoryInterface() { }
+    virtual ~InterfaceActionFactory() { }
+    virtual EntityActionPointer factory(EntitySimulation* simulation,
+                                        EntityActionType type,
+                                        QUuid id,
+                                        EntityItemPointer ownerEntity,
+                                        QVariantMap arguments);
+};
+
+#endif // hifi_InterfaceActionFactory_h
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 02af30b426..71f4621205 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -107,6 +107,9 @@ Avatar::Avatar() :
 }
 
 Avatar::~Avatar() {
+    for(auto attachment : _unusedAttachments) {
+        delete attachment;
+    }
 }
 
 const float BILLBOARD_LOD_DISTANCE = 40.0f;
@@ -298,6 +301,11 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
     pendingChanges.resetItem(_renderItemID, avatarPayloadPointer);
     _skeletonModel.addToScene(scene, pendingChanges);
     getHead()->getFaceModel().addToScene(scene, pendingChanges);
+
+    for (auto attachmentModel : _attachmentModels) {
+        attachmentModel->addToScene(scene, pendingChanges);
+    }
+
     return true;
 }
 
@@ -305,6 +313,9 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
     pendingChanges.removeItem(_renderItemID);
     _skeletonModel.removeFromScene(scene, pendingChanges);
     getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
+    for (auto attachmentModel : _attachmentModels) {
+        attachmentModel->removeFromScene(scene, pendingChanges);
+    }
 }
 
 void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
@@ -317,6 +328,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
     if (postLighting &&
         glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
+        auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
 
         // render pointing lasers
         glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f);
@@ -343,6 +355,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 pointerTransform.setTranslation(position);
                 pointerTransform.setRotation(rotation);
                 batch->setModelTransform(pointerTransform);
+                deferredLighting->bindSimpleProgram(*batch);
                 geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
             }
         }
@@ -365,6 +378,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 pointerTransform.setTranslation(position);
                 pointerTransform.setRotation(rotation);
                 batch->setModelTransform(pointerTransform);
+                deferredLighting->bindSimpleProgram(*batch);
                 geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
             }
         }
@@ -451,7 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 Transform transform;
                 transform.setTranslation(position);
                 batch->setModelTransform(transform);
-                DependencyManager::get<GeometryCache>()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR);
+                DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS
+                                                                                    , 15, 15, LOOK_AT_INDICATOR_COLOR);
             }
         }
 
@@ -483,6 +498,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                     _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
                 }
 
+                DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
                 DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15,
                     glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
                     _voiceSphereID);
@@ -517,7 +533,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
     return glm::angleAxis(angle * proportion, axis);
 }
 
-void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
+void Avatar::fixupModelsInScene() {
     // check to see if when we added our models to the scene they were ready, if they were not ready, then
     // fix them up in the scene
     render::ScenePointer scene = Application::getInstance()->getMain3DScene();
@@ -530,8 +546,24 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool
         getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
         getHead()->getFaceModel().addToScene(scene, pendingChanges);
     }
+    for (auto attachmentModel : _attachmentModels) {
+        if (attachmentModel->needsFixupInScene()) {
+            attachmentModel->removeFromScene(scene, pendingChanges);
+            attachmentModel->addToScene(scene, pendingChanges);
+        }
+    }
+    for (auto attachmentModelToRemove : _attachmentsToRemove) {
+        attachmentModelToRemove->removeFromScene(scene, pendingChanges);
+        _unusedAttachments << attachmentModelToRemove;
+    }
+    _attachmentsToRemove.clear();
     scene->enqueuePendingChanges(pendingChanges);
+}
 
+void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
+
+    fixupModelsInScene();
+    
     {
         Glower glower(renderArgs, glowLevel);
 
@@ -545,10 +577,6 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool
 
         if (postLighting) {
             getHand()->render(renderArgs, false);
-        } else {
-            // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
-            //_skeletonModel.render(renderArgs, 1.0f);
-            renderAttachments(renderArgs);
         }
     }
     getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
@@ -572,22 +600,14 @@ void Avatar::simulateAttachments(float deltaTime) {
                 _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) {
             model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
             model->setRotation(jointRotation * attachment.rotation);
-            model->setScaleToFit(true, _scale * attachment.scale);
+            model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale
+            model->setSnapModelToCenter(false); // hack to force resnap
+            model->setSnapModelToCenter(true);
             model->simulate(deltaTime);
         }
     }
 }
 
-void Avatar::renderAttachments(RenderArgs* args) {
- //   RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
-  //      RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
-    /*
-    foreach (Model* model, _attachmentModels) {
-        model->render(args, 1.0f);
-    }
-    */
-}
-
 void Avatar::updateJointMappings() {
     // no-op; joint mappings come from skeleton model
 }
@@ -945,12 +965,18 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
     }
     // make sure we have as many models as attachments
     while (_attachmentModels.size() < attachmentData.size()) {
-        Model* model = new Model(this);
+        Model* model = nullptr;
+        if (_unusedAttachments.size() > 0) {
+            model = _unusedAttachments.takeFirst();
+        } else {
+            model = new Model(this);
+        }
         model->init();
         _attachmentModels.append(model);
     }
     while (_attachmentModels.size() > attachmentData.size()) {
-        delete _attachmentModels.takeLast();
+        auto attachmentModel = _attachmentModels.takeLast();
+        _attachmentsToRemove << attachmentModel;
     }
 
     // update the urls
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index dbc59f7d9c..b198d12a6e 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -195,6 +195,8 @@ protected:
     SkeletonModel _skeletonModel;
     glm::vec3 _skeletonOffset;
     QVector<Model*> _attachmentModels;
+    QVector<Model*> _attachmentsToRemove;
+    QVector<Model*> _unusedAttachments;
     float _bodyYawDelta;
 
     // These position histories and derivatives are in the world-frame.
@@ -234,9 +236,9 @@ protected:
     void renderDisplayName(RenderArgs* renderArgs);
     virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
     virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
+    virtual void fixupModelsInScene();
 
     void simulateAttachments(float deltaTime);
-    virtual void renderAttachments(RenderArgs* args);
 
     virtual void updateJointMappings();
 
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
new file mode 100644
index 0000000000..1fbb01beb3
--- /dev/null
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -0,0 +1,108 @@
+//
+//  AvatarActionHold.cpp
+//  interface/src/avatar/
+//
+//  Created by Seth Alves 2015-6-9
+//  Copyright 2015 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 "avatar/MyAvatar.h"
+#include "avatar/AvatarManager.h"
+
+#include "AvatarActionHold.h"
+
+AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) :
+    ObjectActionSpring(id, ownerEntity) {
+    #if WANT_DEBUG
+    qDebug() << "AvatarActionHold::AvatarActionHold";
+    #endif
+}
+
+AvatarActionHold::~AvatarActionHold() {
+    #if WANT_DEBUG
+    qDebug() << "AvatarActionHold::~AvatarActionHold";
+    #endif
+}
+
+void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
+    auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    glm::vec3 palmPosition;
+    if (_hand == "right") {
+        palmPosition = myAvatar->getRightPalmPosition();
+    } else {
+        palmPosition = myAvatar->getLeftPalmPosition();
+    }
+
+    auto rotation = myAvatar->getWorldAlignedOrientation();
+    auto offset = rotation * _relativePosition;
+    auto position = palmPosition + offset;
+    rotation *= _relativeRotation;
+
+    lockForWrite();
+    _positionalTarget = position;
+    _rotationalTarget = rotation;
+    unlock();
+
+    ObjectActionSpring::updateActionWorker(deltaTimeStep);
+}
+
+
+bool AvatarActionHold::updateArguments(QVariantMap arguments) {
+    bool rPOk = true;
+    glm::vec3 relativePosition =
+        EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false);
+    bool rROk = true;
+    glm::quat relativeRotation =
+        EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false);
+    bool tSOk = true;
+    float timeScale =
+        EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
+    bool hOk = true;
+    QString hand =
+        EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false);
+
+    lockForWrite();
+    if (rPOk) {
+        _relativePosition = relativePosition;
+    } else if (!_parametersSet) {
+        _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
+    }
+
+    if (rROk) {
+        _relativeRotation = relativeRotation;
+    } else if (!_parametersSet) {
+        _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
+    }
+
+    if (tSOk) {
+        _linearTimeScale = timeScale;
+        _angularTimeScale = timeScale;
+    } else if (!_parametersSet) {
+        _linearTimeScale = 0.2;
+        _angularTimeScale = 0.2;
+    }
+
+    if (hOk) {
+        hand = hand.toLower();
+        if (hand == "left") {
+            _hand = "left";
+        } else if (hand == "right") {
+            _hand = "right";
+        } else {
+            qDebug() << "hold action -- invalid hand argument:" << hand;
+            _hand = "right";
+        }
+    } else if (!_parametersSet) {
+        _hand = "right";
+    }
+
+    _parametersSet = true;
+    _positionalTargetSet = true;
+    _rotationalTargetSet = true;
+    _active = true;
+    unlock();
+    return true;
+}
diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h
new file mode 100644
index 0000000000..705c751029
--- /dev/null
+++ b/interface/src/avatar/AvatarActionHold.h
@@ -0,0 +1,35 @@
+//
+//  AvatarActionHold.h
+//  interface/src/avatar/
+//
+//  Created by Seth Alves 2015-6-9
+//  Copyright 2015 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_AvatarActionHold_h
+#define hifi_AvatarActionHold_h
+
+#include <QUuid>
+
+#include <EntityItem.h>
+#include <ObjectActionSpring.h>
+
+class AvatarActionHold : public ObjectActionSpring {
+public:
+    AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
+    virtual ~AvatarActionHold();
+
+    virtual bool updateArguments(QVariantMap arguments);
+    virtual void updateActionWorker(float deltaTimeStep);
+
+private:
+    glm::vec3 _relativePosition;
+    glm::quat _relativeRotation;
+    QString _hand;
+    bool _parametersSet = false;
+};
+
+#endif // hifi_AvatarActionHold_h
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 8a49d69129..c4e08b5dba 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1178,44 +1178,10 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
 
     // check to see if when we added our models to the scene they were ready, if they were not ready, then
     // fix them up in the scene
-    render::ScenePointer scene = Application::getInstance()->getMain3DScene();
-    render::PendingChanges pendingChanges;
-    if (_skeletonModel.needsFixupInScene()) {
-        _skeletonModel.removeFromScene(scene, pendingChanges);
-        _skeletonModel.addToScene(scene, pendingChanges);
-    }
-    if (getHead()->getFaceModel().needsFixupInScene()) {
-        getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
-        getHead()->getFaceModel().addToScene(scene, pendingChanges);
-    }
-    scene->enqueuePendingChanges(pendingChanges);
+    fixupModelsInScene();
 
-    Camera *camera = Application::getInstance()->getCamera();
-    const glm::vec3 cameraPos = camera->getPosition();
+    const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition();
 
-
-    // HACK: comment this block which possibly change the near and break the rendering 5/6/2015
-    // Only tweak the frustum near far if it's not shadow
- /*   if (renderMode != RenderArgs::SHADOW_RENDER_MODE) {
-        // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model.
-        if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) {
-            renderFrustum->setNearClip(DEFAULT_NEAR_CLIP);
-        } else {
-            float clipDistance = _skeletonModel.getHeadClipDistance();
-            clipDistance = glm::length(getEyePosition() 
-                + camera->getOrientation() * glm::vec3(0.0f, 0.0f, -clipDistance) - cameraPos);
-            renderFrustum->setNearClip(clipDistance);
-        }
-    }*/
-
-    //  Render the body's voxels and head
-    if (!postLighting) {
-    
-        // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
-        //_skeletonModel.render(renderArgs, 1.0f);
-        renderAttachments(renderArgs);
-    }
-    
     //  Render head so long as the camera isn't inside it
     if (shouldRenderHead(renderArgs, cameraPos)) {
         getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
@@ -1571,27 +1537,6 @@ void MyAvatar::updateMotionBehavior() {
     _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
 }
 
-void MyAvatar::renderAttachments(RenderArgs* args) {
-    if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
-        Avatar::renderAttachments(args);
-        return;
-    }
-    const FBXGeometry& geometry = _skeletonModel.getGeometry()->getFBXGeometry();
-    QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
- //   RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
-  //      RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
-    
-    // FIX ME - attachments need to be added to scene too...
-    /*
-    for (int i = 0; i < _attachmentData.size(); i++) {
-        const QString& jointName = _attachmentData.at(i).jointName;
-        if (jointName != headJointName && jointName != "Head") {
-            _attachmentModels.at(i)->render(args, 1.0f);
-        }
-    }
-    */
-}
-
 //Renders sixense laser pointers for UI selection with controllers
 void MyAvatar::renderLaserPointers() {
     const float PALM_TIP_ROD_RADIUS = 0.002f;
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index c8d16e8cb0..a3dc34e6e0 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -197,9 +197,6 @@ public slots:
 signals:
     void transformChanged();
 
-protected:
-    virtual void renderAttachments(RenderArgs* args);
-    
 private:
 
     // These are made private for MyAvatar so that you will use the "use" methods instead
diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp
index 7b0f8c056c..8a336064e5 100755
--- a/interface/src/devices/KeyboardMouseDevice.cpp
+++ b/interface/src/devices/KeyboardMouseDevice.cpp
@@ -159,9 +159,25 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
     // Grab the current free device ID
     _deviceID = mapper.getFreeDeviceID();
 
-    auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy());
+    auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard"));
     proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
     proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
+    proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
+        QVector<UserInputMapper::InputPair> availableInputs;
+        for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
+        }
+        for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
+        }
+        availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
+        return availableInputs;
+    };
+    proxy->resetDeviceBindings = [this, &mapper] () -> bool {
+        mapper.removeAllInputChannelsForDevice(_deviceID);
+        this->assignDefaultInputMapping(mapper);
+        return true;
+    };
     mapper.registerDevice(_deviceID, proxy);
 }
 
diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp
index 5f12d73b37..2c747195a7 100644
--- a/interface/src/scripting/ControllerScriptingInterface.cpp
+++ b/interface/src/scripting/ControllerScriptingInterface.cpp
@@ -28,6 +28,94 @@ ControllerScriptingInterface::ControllerScriptingInterface() :
 {
 
 }
+static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
+static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
+static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
+static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
+
+QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
+void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
+QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
+void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
+QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
+void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
+void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
+
+QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("device", input.getDevice());
+    obj.setProperty("channel", input.getChannel());
+    obj.setProperty("type", (unsigned short) input.getType());
+    obj.setProperty("id", input.getID());
+    return obj;
+}
+
+void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
+    input.setDevice(object.property("device").toUInt16());
+    input.setChannel(object.property("channel").toUInt16());
+    input.setType(object.property("type").toUInt16());
+    input.setID(object.property("id").toInt32());
+}
+
+QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput()));
+    obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier()));
+    obj.setProperty("action", inputChannel.getAction());
+    obj.setProperty("scale", inputChannel.getScale());
+    return obj;
+}
+
+void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
+    UserInputMapper::Input input;
+    UserInputMapper::Input modifier;
+    inputFromScriptValue(object.property("input"), input);
+    inputChannel.setInput(input);
+    inputFromScriptValue(object.property("modifier"), modifier);
+    inputChannel.setModifier(modifier);
+    inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt()));
+    inputChannel.setScale(object.property("scale").toVariant().toFloat());
+}
+
+QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
+    QScriptValue obj = engine->newObject();
+    QVector<UserInputMapper::InputChannel> inputChannels = Application::getUserInputMapper()->getInputChannelsForAction(action);
+    QScriptValue _inputChannels = engine->newArray(inputChannels.size());
+    for (int i = 0; i < inputChannels.size(); i++) {
+        _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i]));
+    }
+    obj.setProperty("action", (int) action);
+    obj.setProperty("actionName", Application::getUserInputMapper()->getActionName(action));
+    obj.setProperty("inputChannels", _inputChannels);
+    return obj;
+}
+
+void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
+    action = UserInputMapper::Action(object.property("action").toVariant().toInt());
+}
+
+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
+    obj.setProperty("inputName", inputPair.second);
+    return obj;
+}
+
+void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
+    inputFromScriptValue(object.property("input"), inputPair.first);
+    inputPair.second = QString(object.property("inputName").toVariant().toString());
+}
+
+void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) {
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine);
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
+    qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
+    qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue);
+    qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
+    qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
+}
 
 void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
     if (event->type() == HFActionEvent::startType()) {
@@ -337,6 +425,37 @@ void ControllerScriptingInterface::updateInputControllers() {
     }
 }
 
+QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
+    return Application::getUserInputMapper()->getAllActions();
+}
+
+QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
+    return Application::getUserInputMapper()->getInputChannelsForAction(action);
+}
+
+QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
+    return Application::getUserInputMapper()->getDeviceName((unsigned short) device);
+}
+
+QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
+    return Application::getUserInputMapper()->getAllInputsForDevice(device);
+}
+
+bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
+    return Application::getUserInputMapper()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
+}
+
+bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
+    return Application::getUserInputMapper()->removeInputChannel(inputChannel);
+}
+
+QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
+    return Application::getUserInputMapper()->getAvailableInputs((unsigned short) device);
+}
+
+void ControllerScriptingInterface::resetAllDeviceBindings() {
+    Application::getUserInputMapper()->resetAllDeviceBindings();
+}
 
 InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
     AbstractInputController(),
@@ -373,4 +492,4 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
 
 InputController::Key InputController::getKey() const {
     return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
-}
+}
\ No newline at end of file
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index c088dd6c9a..9414dc887b 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -14,10 +14,11 @@
 
 #include <QtCore/QObject>
 
+#include "ui/UserInputMapper.h"
+
 #include <AbstractControllerScriptingInterface.h>
 class PalmData;
 
-
 class InputController : public  AbstractInputController {
     Q_OBJECT
 
@@ -54,6 +55,9 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
 
 public:    
     ControllerScriptingInterface();
+    
+    virtual void registerControllerTypes(QScriptEngine* engine);
+    
     void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
     void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
     
@@ -79,6 +83,14 @@ public:
     void updateInputControllers();
 
 public slots:
+    Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
+    Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
+    Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
+    Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
+    Q_INVOKABLE virtual void resetAllDeviceBindings();
     virtual bool isPrimaryButtonPressed() const;
     virtual glm::vec2 getPrimaryJoystickPosition() const;
 
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 3aae6a4d4a..6be67a7261 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -31,7 +31,7 @@ WindowScriptingInterface::WindowScriptingInterface() :
     _formResult(QDialog::Rejected) 
 {
     const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
-    connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
+    connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
     connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested);
     connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
 }
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index bc8047fc98..0c1783f8ac 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -143,7 +143,6 @@ ApplicationOverlay::ApplicationOverlay() :
     _alpha(1.0f),
     _oculusUIRadius(1.0f),
     _trailingAudioLoudness(0.0f),
-    _crosshairTexture(0),
     _previousBorderWidth(-1),
     _previousBorderHeight(-1),
     _previousMagnifierBottomLeft(),
@@ -591,8 +590,8 @@ void ApplicationOverlay::renderPointers() {
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+    //glActiveTexture(GL_TEXTURE0);
+    //bindCursorTexture();
 
     if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
         //If we are in oculus, render reticle later
@@ -637,8 +636,8 @@ void ApplicationOverlay::renderPointers() {
         _magActive[MOUSE] = false;
         renderControllerPointers();
     }
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glDisable(GL_TEXTURE_2D);
+    //glBindTexture(GL_TEXTURE_2D, 0);
+    //glDisable(GL_TEXTURE_2D);
 }
 
 void ApplicationOverlay::renderControllerPointers() {
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index ee82c9274a..eb397fe3c6 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -96,7 +96,6 @@ private:
     };
     
     float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
-    
     void renderReticle(glm::quat orientation, float alpha);
     void renderPointers();;
     void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
@@ -108,6 +107,7 @@ private:
     void renderCameraToggle();
     void renderStatsAndLogs();
     void renderDomainConnectionStatusBorder();
+    void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
 
     TexturedHemisphere _overlays;
     
@@ -125,9 +125,12 @@ private:
     float _alpha = 1.0f;
     float _oculusUIRadius;
     float _trailingAudioLoudness;
+    
+     gpu::TexturePointer _crosshairTexture;
 
 
-    gpu::TexturePointer _crosshairTexture;
+    QMap<uint16_t, gpu::TexturePointer> _cursors;
+
     GLuint _newUiTexture{ 0 };
     
     int _reticleQuad;
diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index 32df75c46d..8359480b03 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
                       ((rgba >> 8) & 0xff)  / 255.0f,
                       (rgba & 0xff) / 255.0f);
 
+    // FIX ME: is this correct? It seems to work to fix textures bleeding into us...
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+
     DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
 }
 
@@ -460,7 +464,7 @@ void Stats::display(
     verticalOffset = STATS_PELS_INITIALOFFSET;
     horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
 
-    lines = _expanded ? 10 : 2;
+    lines = _expanded ? 10 : 3;
 
     drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset,
         (lines + 1) * STATS_PELS_PER_LINE);
@@ -608,12 +612,10 @@ void Stats::display(
     }
 
     // LOD Details
-    if (_expanded) {
-        octreeStats.str("");
-        QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText();
-        octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
-        verticalOffset += STATS_PELS_PER_LINE;
-        drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
-    }
+    octreeStats.str("");
+    QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText();
+    octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
+    verticalOffset += STATS_PELS_PER_LINE;
+    drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
 }
 
diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp
index 892ab6a9b6..e994b3cf30 100755
--- a/interface/src/ui/UserInputMapper.cpp
+++ b/interface/src/ui/UserInputMapper.cpp
@@ -13,7 +13,15 @@
 
 
 // UserInputMapper Class
+
+// Default contruct allocate the poutput size with the current hardcoded action channels
+UserInputMapper::UserInputMapper() {
+    assignDefaulActionScales();
+    createActionNames();
+}
+
 bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
+    proxy->_name += " (" + QString::number(deviceID) + ")";
     _registeredDevices[deviceID] = proxy;
     return true;
 }
@@ -27,6 +35,12 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu
     }
 }
 
+void UserInputMapper::resetAllDeviceBindings() {
+    for (auto device : _registeredDevices) {
+        device.second->resetDeviceBindings();
+    }
+}
+
 bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
     return addInputChannel(action, input, Input(), scale);
 }
@@ -37,7 +51,7 @@ bool UserInputMapper::addInputChannel(Action action, const Input& input, const I
         qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped.";
         return false;
     }
-
+    
     auto inputChannel = InputChannel(input, modifier, action, scale);
 
     // Insert or replace the input to modifiers
@@ -61,6 +75,37 @@ int UserInputMapper::addInputChannels(const InputChannels& channels) {
     return nbAdded;
 }
 
+bool UserInputMapper::removeInputChannel(InputChannel inputChannel) {
+    // Remove from Input to Modifiers map
+    if (inputChannel.hasModifier()) {
+        _inputToModifiersMap.erase(inputChannel._input.getID());
+    }
+    
+    // Remove from Action to Inputs map
+    std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
+    ret = _actionToInputsMap.equal_range(inputChannel._action);
+    for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
+        if (it->second == inputChannel) {
+            _actionToInputsMap.erase(it);
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+void UserInputMapper::removeAllInputChannels() {
+    _inputToModifiersMap.clear();
+    _actionToInputsMap.clear();
+}
+
+void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
+    QVector<InputChannel> channels = getAllInputsForDevice(device);
+    for (auto& channel : channels) {
+        removeInputChannel(channel);
+    }
+}
+
 int UserInputMapper::getInputChannels(InputChannels& channels) const {
     for (auto& channel : _actionToInputsMap) {
         channels.push_back(channel.second);
@@ -69,6 +114,20 @@ int UserInputMapper::getInputChannels(InputChannels& channels) const {
     return _actionToInputsMap.size();
 }
 
+QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
+    InputChannels allChannels;
+    getInputChannels(allChannels);
+    
+    QVector<InputChannel> channels;
+    for (InputChannel inputChannel : allChannels) {
+        if (inputChannel._input._device == device) {
+            channels.push_back(inputChannel);
+        }
+    }
+    
+    return channels;
+}
+
 void UserInputMapper::update(float deltaTime) {
 
     // Reset the axis state for next loop
@@ -130,6 +189,24 @@ void UserInputMapper::update(float deltaTime) {
     }
 }
 
+QVector<UserInputMapper::Action> UserInputMapper::getAllActions() {
+    QVector<Action> actions;
+    for (auto i = 0; i < NUM_ACTIONS; i++) {
+        actions.append(Action(i));
+    }
+    return actions;
+}
+
+QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
+    QVector<InputChannel> inputChannels;
+    std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
+    ret = _actionToInputsMap.equal_range(action);
+    for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
+        inputChannels.append(it->second);
+    }
+    return inputChannels;
+}
+
 void UserInputMapper::assignDefaulActionScales() {
     _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit
     _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit
@@ -144,3 +221,20 @@ void UserInputMapper::assignDefaulActionScales() {
     _actionScales[BOOM_IN] = 1.0f; // 1m per unit
     _actionScales[BOOM_OUT] = 1.0f; // 1m per unit
 }
+
+// This is only necessary as long as the actions are hardcoded
+// Eventually you can just add the string when you add the action
+void UserInputMapper::createActionNames() {
+    _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD";
+    _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD";
+    _actionNames[LATERAL_LEFT] = "LATERAL_LEFT";
+    _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT";
+    _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN";
+    _actionNames[VERTICAL_UP] = "VERTICAL_UP";
+    _actionNames[YAW_LEFT] = "YAW_LEFT";
+    _actionNames[YAW_RIGHT] = "YAW_RIGHT";
+    _actionNames[PITCH_DOWN] = "PITCH_DOWN";
+    _actionNames[PITCH_UP] = "PITCH_UP";
+    _actionNames[BOOM_IN] = "BOOM_IN";
+    _actionNames[BOOM_OUT] = "BOOM_OUT";
+}
\ No newline at end of file
diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h
index ab63bdbef7..0a08e277db 100755
--- a/interface/src/ui/UserInputMapper.h
+++ b/interface/src/ui/UserInputMapper.h
@@ -21,6 +21,7 @@
 
 class UserInputMapper : public QObject {
     Q_OBJECT
+    Q_ENUMS(Action)
 public:
     typedef unsigned short uint16;
     typedef unsigned int uint32;
@@ -51,8 +52,13 @@ public:
         uint16 getDevice() const { return _device; }
         uint16 getChannel() const { return _channel; }
         uint32 getID() const { return _id; }
-
         ChannelType getType() const { return (ChannelType) _type; }
+        
+        void setDevice(uint16 device) { _device = device; }
+        void setChannel(uint16 channel) { _channel = channel; }
+        void setType(uint16 type) { _type = type; }
+        void setID(uint32 ID) { _id = ID; }
+
         bool isButton() const { return getType() == ChannelType::BUTTON; }
         bool isAxis() const { return getType() == ChannelType::AXIS; }
         bool isJoint() const { return getType() == ChannelType::JOINT; }
@@ -64,6 +70,7 @@ public:
         explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
         Input(const Input& src) : _id(src._id) {}
         Input& operator = (const Input& src) { _id = src._id; return (*this); }
+        bool operator ==(const Input& right) const { return _id == right._id; }
         bool operator < (const Input& src) const { return _id < src._id; }
     };
 
@@ -83,22 +90,32 @@ public:
     typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
     typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
     typedef std::function<JointValue (const Input& input, int timestamp)> JointGetter;
+    typedef QPair<Input, QString> InputPair;
+    typedef std::function<QVector<InputPair> ()> AvailableInputGetter;
+    typedef std::function<bool ()> ResetBindings;
+    
+    typedef QVector<InputPair> AvailableInput;
 
    class DeviceProxy {
     public:
-        DeviceProxy() {}
-        
-        ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
-        AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
-        JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
-        
-        typedef std::shared_ptr<DeviceProxy> Pointer;
+       DeviceProxy(QString name) { _name = name; }
+       
+       QString _name;
+       ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
+       AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
+       JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
+       AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); };
+       ResetBindings resetDeviceBindings = [] () -> bool { return true; };
+       
+       typedef std::shared_ptr<DeviceProxy> Pointer;
     };
     // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
     uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
     bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device);
     DeviceProxy::Pointer getDeviceProxy(const Input& input);
-
+    QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; }
+    QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
+    void resetAllDeviceBindings();
 
     // Actions are the output channels of the Mapper, that's what the InputChannel map to
     // For now the Actions are hardcoded, this is bad, but we will fix that in the near future
@@ -123,7 +140,12 @@ public:
 
         NUM_ACTIONS,
     };
+    
+    std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS);
+    void createActionNames();
 
+    QVector<Action> getAllActions();
+    QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; }
     float getActionState(Action action) const { return _actionStates[action]; }
     void assignDefaulActionScales();
 
@@ -140,27 +162,43 @@ public:
         Input _modifier = Input(); // make it invalid by default, meaning no modifier
         Action _action = LONGITUDINAL_BACKWARD;
         float _scale = 0.0f;
+        
+        Input getInput() const { return _input; }
+        Input getModifier() const { return _modifier; }
+        Action getAction() const { return _action; }
+        float getScale() const { return _scale; }
+        
+        void setInput(Input input) { _input = input; }
+        void setModifier(Input modifier) { _modifier = modifier; }
+        void setAction(Action action) { _action = action; }
+        void setScale(float scale) { _scale = scale; }
 
         InputChannel() {}
         InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) :
             _input(input), _modifier(modifier), _action(action), _scale(scale) {}
         InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {}
         InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); }
-    
+        bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; }
         bool hasModifier() { return _modifier.isValid(); }
     };
     typedef std::vector< InputChannel > InputChannels;
 
     // Add a bunch of input channels, return the true number of channels that successfully were added
     int addInputChannels(const InputChannels& channels);
+    // Remove the first found instance of the input channel from the input mapper, true if found
+    bool removeInputChannel(InputChannel channel);
+    void removeAllInputChannels();
+    void removeAllInputChannelsForDevice(uint16 device);
     //Grab all the input channels currently in use, return the number
     int getInputChannels(InputChannels& channels) const;
+    QVector<InputChannel> getAllInputsForDevice(uint16 device);
+    QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
+    std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; }
 
     // Update means go grab all the device input channels and update the output channel values
     void update(float deltaTime);
-
-    // Default contruct allocate the poutput size with the current hardcoded action channels
-    UserInputMapper() { assignDefaulActionScales(); }
+    
+    UserInputMapper();
 
 protected:
     typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
@@ -177,4 +215,12 @@ protected:
     std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
 };
 
+Q_DECLARE_METATYPE(UserInputMapper::InputPair)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
+Q_DECLARE_METATYPE(UserInputMapper::Input)
+Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
+Q_DECLARE_METATYPE(UserInputMapper::Action)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
+
 #endif // hifi_UserInputMapper_h
diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h
index 1a808bc15c..0264c6e3c0 100644
--- a/interface/src/ui/overlays/Overlay.h
+++ b/interface/src/ui/overlays/Overlay.h
@@ -123,6 +123,7 @@ protected:
 namespace render {
    template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); 
    template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
+   template <> int payloadGetLayer(const Overlay::Pointer& overlay);
    template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
 }
 
diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp
index cf3262d05c..d5e4b34f6b 100644
--- a/interface/src/ui/overlays/OverlaysPayload.cpp
+++ b/interface/src/ui/overlays/OverlaysPayload.cpp
@@ -37,7 +37,7 @@ namespace render {
     template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
         if (overlay->is3D() && !static_cast<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) {
             if (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront()) {
-                return ItemKey::Builder().withTypeShape().withNoDepthSort().build();
+                return ItemKey::Builder().withTypeShape().withLayered().build();
             } else {
                 return ItemKey::Builder::opaqueShape();
             }
@@ -53,6 +53,17 @@ namespace render {
             return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f));
         }
     }
+    template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
+        // MAgic number while we are defining the layering mechanism:
+        const int LAYER_2D =  2;
+        const int LAYER_3D_FRONT = 1;
+        const int LAYER_3D = 0;
+        if (overlay->is3D()) {
+            return (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D);
+        } else {
+            return LAYER_2D;
+        }
+    }
     template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
         if (args) {
             glPushMatrix();
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 340ca9374d..6450f25208 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -156,6 +156,7 @@ void AudioClient::audioMixerKilled() {
     _hasReceivedFirstPacket = false;
     _outgoingAvatarAudioSequenceNumber = 0;
     _stats.reset();
+    emit disconnected();
 }
 
 
diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h
index 3b2c1c1ae6..642edde84a 100644
--- a/libraries/audio-client/src/AudioClient.h
+++ b/libraries/audio-client/src/AudioClient.h
@@ -186,6 +186,7 @@ signals:
     void deviceChanged();
 
     void receivedFirstPacket();
+    void disconnected();
 
 protected:
     AudioClient();
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 98cbc1f845..3e83c6f559 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
 void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
     checkAndCallPreload(entityID);
     auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
-    addEntityToScene(entity);
+    if (entity) {
+        addEntityToScene(entity);
+    }
 }
 
 void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index b2400b797e..23b93250bc 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -9,15 +9,18 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderableBoxEntityItem.h"
+
 #include <glm/gtx/quaternion.hpp>
 
 #include <gpu/GPUConfig.h>
 #include <gpu/Batch.h>
 
 #include <DeferredLightingEffect.h>
+#include <ObjectMotionState.h>
 #include <PerfStat.h>
 
-#include "RenderableBoxEntityItem.h"
+#include "RenderableDebugableEntityItem.h"
 
 EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
     return EntityItemPointer(new RenderableBoxEntityItem(entityID, properties));
@@ -27,23 +30,11 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
     Q_ASSERT(getType() == EntityTypes::Box);
     glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
-
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
-    bool highlightSimulationOwnership = false;
-    if (debugSimulationOwnership) {
-        auto nodeList = DependencyManager::get<NodeList>();
-        const QUuid& myNodeID = nodeList->getSessionUUID();
-        highlightSimulationOwnership = (getSimulatorID() == myNodeID);
-    }
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransformToCenter());
-    if (highlightSimulationOwnership) {
-        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
-    } else {
-        DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
-    }
+    batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
+    DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
 
     RenderableDebugableEntityItem::render(this, args);
 };
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
index 06a62706b9..b14da9ee22 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
@@ -13,7 +13,6 @@
 #define hifi_RenderableBoxEntityItem_h
 
 #include <BoxEntityItem.h>
-#include "RenderableDebugableEntityItem.h"
 #include "RenderableEntityItem.h"
 
 class RenderableBoxEntityItem : public BoxEntityItem {
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
index ca81ae4f2b..6a511e0d30 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
@@ -10,58 +10,54 @@
 //
 
 
+#include "RenderableDebugableEntityItem.h"
 
 #include <glm/gtx/quaternion.hpp>
 
 #include <gpu/GPUConfig.h>
 #include <gpu/Batch.h>
-
 #include <DeferredLightingEffect.h>
-#include <PhysicsEngine.h>
-
-#include "RenderableDebugableEntityItem.h"
+#include <ObjectMotionState.h>
 
 
 void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args,
                                                       float puffedOut, glm::vec4& color) {
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(entity->getTransformToCenter());
+    batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
     DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
 }
 
-void RenderableDebugableEntityItem::renderHoverDot(EntityItem* entity, RenderArgs* args) {
-    const int SLICES = 8, STACKS = 8;
-    float radius = 0.05f;
-    glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
-    
-    Q_ASSERT(args->_batch);
-    gpu::Batch& batch = *args->_batch;
-    Transform transform = entity->getTransformToCenter();
-    // Cancel true dimensions and set scale to 2 * radius (diameter)
-    transform.postScale(2.0f * glm::vec3(radius, radius, radius) / entity->getDimensions());
-    batch.setModelTransform(transform);
-    DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, blueColor);
-}
-
 void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) {
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
+    if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
+        Q_ASSERT(args->_batch);
+        gpu::Batch& batch = *args->_batch;
+
+        batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
+        
+        auto nodeList = DependencyManager::get<NodeList>();
+        const QUuid& myNodeID = nodeList->getSessionUUID();
+        bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
+        if (highlightSimulationOwnership) {
+            glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
+            renderBoundingBox(entity, args, 0.08f, greenColor);
+        }
 
-    if (debugSimulationOwnership) {
         quint64 now = usecTimestampNow();
         if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
             glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
-            renderBoundingBox(entity, args, 0.2f, redColor);
+            renderBoundingBox(entity, args, 0.16f, redColor);
         }
 
         if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
             glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
-            renderBoundingBox(entity, args, 0.3f, yellowColor);
+            renderBoundingBox(entity, args, 0.24f, yellowColor);
         }
 
         ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
         if (motionState && motionState->isActive()) {
-            renderHoverDot(entity, args);
+            glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
+            renderBoundingBox(entity, args, 0.32f, blueColor);
         }
     }
 }
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
index 758bac353b..2680d882f5 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
@@ -17,7 +17,6 @@
 class RenderableDebugableEntityItem {
 public:
     static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color);
-    static void renderHoverDot(EntityItem* entity, RenderArgs* args);
     static void render(EntityItem* entity, RenderArgs* args);
 };
 
diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
index 4e5de331bb..65407c74e7 100644
--- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
@@ -49,6 +49,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
     
     batch._glLineWidth(getLineWidth());
     if (getLinePoints().size() > 1) {
+        DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
         DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
     }
     batch._glLineWidth(1.0f);
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index e0bc493a5c..14a64d289e 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -201,14 +201,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
     glm::vec3 position = getPosition();
     glm::vec3 dimensions = getDimensions();
 
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
-    bool highlightSimulationOwnership = false;
-    if (debugSimulationOwnership) {
-        auto nodeList = DependencyManager::get<NodeList>();
-        const QUuid& myNodeID = nodeList->getSessionUUID();
-        highlightSimulationOwnership = (getSimulatorID() == myNodeID);
-    }
-
     if (hasModel()) {
         if (_model) {
             if (QUrl(getModelURL()) != _model->getURL()) {
@@ -274,11 +266,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
                 }
             }
         }
-
-        if (highlightSimulationOwnership) {
-            glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
-            RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
-        }
     } else {
         glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
         RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
index 6a50cbf1cb..91c89bb183 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
@@ -53,6 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
         batch.setUniformTexture(0, _texture->getGPUTexture());
     }
     batch.setModelTransform(getTransformToCenter());
+    DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
     DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID);
 };
 
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index d5cb7d11b8..6d9cb525d6 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderableSphereEntityItem.h"
+
 #include <glm/gtx/quaternion.hpp>
 
 #include <gpu/GPUConfig.h>
@@ -18,7 +20,7 @@
 #include <DeferredLightingEffect.h>
 #include <PerfStat.h>
 
-#include "RenderableSphereEntityItem.h"
+#include "RenderableDebugableEntityItem.h"
 
 EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
     return EntityItemPointer(new RenderableSphereEntityItem(entityID, properties));
@@ -37,6 +39,8 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransformToCenter());
+    batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
     DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
+
+    RenderableDebugableEntityItem::render(this, args);
 };
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 8a67bb99f2..d06ffb9400 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -47,12 +47,13 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
     DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
     
-    float scale = _lineHeight / _textRenderer->getRowHeight();
+    float scale = _lineHeight / _textRenderer->getFontSize();
     transformToTopLeft.setScale(scale); // Scale to have the correct line height
     batch.setModelTransform(transformToTopLeft);
     
-    float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight;
-    glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin);
+    float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight;
+    glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
+                                 dimensions.y - 2.0f * topMargin);
     _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
 }
 
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
index 9c4f8ae0bb..8c147cac05 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
@@ -14,6 +14,7 @@
 #include <gpu/GPUConfig.h>
 #include <gpu/Batch.h>
 
+#include <AbstractViewStateInterface.h>
 #include <DeferredLightingEffect.h>
 #include <DependencyManager.h>
 #include <GeometryCache.h>
@@ -100,10 +101,17 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
             case SHAPE_TYPE_COMPOUND: {
                 PerformanceTimer perfTimer("zone->renderCompound");
                 updateGeometry();
-                
-                if (_model && _model->isActive()) {
-                    // FIX ME: this is no longer available... we need to switch to payloads
-                    //_model->renderInScene(getLocalRenderAlpha(), args);
+                if (_model && _model->needsFixupInScene()) {
+                    // check to see if when we added our models to the scene they were ready, if they were not ready, then
+                    // fix them up in the scene
+                    render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
+                    render::PendingChanges pendingChanges;
+                    _model->removeFromScene(scene, pendingChanges);
+                    _model->addToScene(scene, pendingChanges);
+                    
+                    scene->enqueuePendingChanges(pendingChanges);
+                    
+                    _model->setVisibleInScene(getVisible(), scene);
                 }
                 break;
             }
@@ -131,6 +139,15 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
                 break;
         }
     }
+    
+    if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) &&
+        _model && !_model->needsFixupInScene()) {
+        // If the model is in the scene but doesn't need to be, remove it.
+        render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
+        render::PendingChanges pendingChanges;
+        _model->removeFromScene(scene, pendingChanges);
+        scene->enqueuePendingChanges(pendingChanges);
+    }
 }
 
 bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
@@ -145,3 +162,51 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
     
     return false;
 }
+
+class RenderableZoneEntityItemMeta {
+public:
+    RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
+    typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
+    typedef Payload::DataPointer Pointer;
+    
+    EntityItemPointer entity;
+};
+
+namespace render {
+    template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
+        return ItemKey::Builder::opaqueShape();
+    }
+    
+    template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
+        if (payload && payload->entity) {
+            return payload->entity->getAABox();
+        }
+        return render::Item::Bound();
+    }
+    template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {
+        if (args) {
+            if (payload && payload->entity) {
+                payload->entity->render(args);
+            }
+        }
+    }
+}
+
+bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
+                                           render::PendingChanges& pendingChanges) {
+    _myMetaItem = scene->allocateID();
+    
+    auto renderData = RenderableZoneEntityItemMeta::Pointer(new RenderableZoneEntityItemMeta(self));
+    auto renderPayload = render::PayloadPointer(new RenderableZoneEntityItemMeta::Payload(renderData));
+    
+    pendingChanges.resetItem(_myMetaItem, renderPayload);
+    return true;
+}
+
+void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
+                                                render::PendingChanges& pendingChanges) {
+    pendingChanges.removeItem(_myMetaItem);
+    if (_model) {
+        _model->removeFromScene(scene, pendingChanges);
+    }
+}
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
index b2a9791d44..f455ea34de 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
@@ -35,6 +35,9 @@ public:
     virtual void render(RenderArgs* args);
     virtual bool contains(const glm::vec3& point) const;
     
+    virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
+    virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
+    
 private:
     Model* getModel();
     void initialSimulation();
@@ -45,6 +48,8 @@ private:
     
     Model* _model;
     bool _needsInitialSimulation;
+    
+    render::ItemID _myMetaItem;
 };
 
 #endif // hifi_RenderableZoneEntityItem_h
diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h
new file mode 100644
index 0000000000..9820313aae
--- /dev/null
+++ b/libraries/entities/src/EntityActionFactoryInterface.h
@@ -0,0 +1,33 @@
+//
+//  EntityActionFactoryInterface.cpp
+//  libraries/entities/src
+//
+//  Created by Seth Alves on 2015-6-2
+//  Copyright 2015 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_EntityActionFactoryInterface_h
+#define hifi_EntityActionFactoryInterface_h
+
+#include <DependencyManager.h>
+
+#include "EntityActionInterface.h"
+
+class EntityActionFactoryInterface : public QObject, public Dependency {
+    Q_OBJECT
+    SINGLETON_DEPENDENCY
+
+ public:
+    EntityActionFactoryInterface() { }
+    virtual ~EntityActionFactoryInterface() { }
+    virtual EntityActionPointer factory(EntitySimulation* simulation,
+                                        EntityActionType type,
+                                        QUuid id,
+                                        EntityItemPointer ownerEntity,
+                                        QVariantMap arguments) { assert(false); return nullptr; }
+};
+
+#endif // hifi_EntityActionFactoryInterface_h
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index f26dd006ff..e97669686c 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -22,6 +22,12 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
     if (normalizedActionTypeString == "pulltopoint") {
         return ACTION_TYPE_PULL_TO_POINT;
     }
+    if (normalizedActionTypeString == "spring") {
+        return ACTION_TYPE_SPRING;
+    }
+    if (normalizedActionTypeString == "hold") {
+        return ACTION_TYPE_HOLD;
+    }
 
     qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
     return ACTION_TYPE_NONE;
@@ -33,31 +39,37 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
             return "none";
         case ACTION_TYPE_PULL_TO_POINT:
             return "pullToPoint";
+        case ACTION_TYPE_SPRING:
+            return "spring";
+        case ACTION_TYPE_HOLD:
+            return "hold";
     }
     assert(false);
     return "none";
 }
 
 glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
-                                                     QString argumentName, bool& ok) {
+                                                     QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
-        qDebug() << objectName << "requires argument:" << argumentName;
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariant resultV = arguments[argumentName];
     if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
         qDebug() << objectName << "argument" << argumentName << "must be a map";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariantMap resultVM = resultV.toMap();
     if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
         qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariant xV = resultVM["x"];
@@ -73,17 +85,65 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
     if (!xOk || !yOk || !zOk) {
         qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
-    return vec3(x, y, z);
+    return glm::vec3(x, y, z);
 }
 
+glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
+                                                     QString argumentName, bool& ok, bool required) {
+    if (!arguments.contains(argumentName)) {
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariant resultV = arguments[argumentName];
+    if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
+        qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariantMap resultVM = resultV.toMap();
+    if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
+        qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariant xV = resultVM["x"];
+    QVariant yV = resultVM["y"];
+    QVariant zV = resultVM["z"];
+    QVariant wV = resultVM["w"];
+
+    bool xOk = true;
+    bool yOk = true;
+    bool zOk = true;
+    bool wOk = true;
+    float x = xV.toFloat(&xOk);
+    float y = yV.toFloat(&yOk);
+    float z = zV.toFloat(&zOk);
+    float w = wV.toFloat(&wOk);
+    if (!xOk || !yOk || !zOk || !wOk) {
+        qDebug() << objectName << "argument" << argumentName
+                 << "must be a map with keys of x, y, z, w and values of type float.";
+        ok = false;
+        return glm::quat();
+    }
+
+    return glm::quat(w, x, y, z);
+}
 
 float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
-                                                  QString argumentName, bool& ok) {
+                                                  QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
-        qDebug() << objectName << "requires argument:" << argumentName;
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
         ok = false;
         return 0.0f;
     }
@@ -99,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
 
     return v;
 }
+
+QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
+                                                     QString argumentName, bool& ok, bool required) {
+    if (!arguments.contains(argumentName)) {
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
+        ok = false;
+        return "";
+    }
+
+    QVariant vV = arguments[argumentName];
+    QString v = vV.toString();
+    return v;
+}
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 74efae3239..486d3f5948 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -14,12 +14,16 @@
 
 #include <QUuid>
 
+#include "EntityItem.h"
+
 class EntitySimulation;
 
 enum EntityActionType {
     // keep these synchronized with actionTypeFromString and actionTypeToString
     ACTION_TYPE_NONE,
-    ACTION_TYPE_PULL_TO_POINT
+    ACTION_TYPE_PULL_TO_POINT,
+    ACTION_TYPE_SPRING,
+    ACTION_TYPE_HOLD
 };
 
 
@@ -32,18 +36,35 @@ public:
     virtual const EntityItemPointer& getOwnerEntity() const = 0;
     virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
     virtual bool updateArguments(QVariantMap arguments) = 0;
-    // virtual QByteArray serialize() = 0;
-    // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data);
 
     static EntityActionType actionTypeFromString(QString actionTypeString);
     static QString actionTypeToString(EntityActionType actionType);
 
 protected:
+    virtual glm::vec3 getPosition() = 0;
+    virtual void setPosition(glm::vec3 position) = 0;
+    virtual glm::quat getRotation() = 0;
+    virtual void setRotation(glm::quat rotation) = 0;
+    virtual glm::vec3 getLinearVelocity() = 0;
+    virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
+    virtual glm::vec3 getAngularVelocity() = 0;
+    virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
+
+    // these look in the arguments map for a named argument.  if it's not found or isn't well formed,
+    // ok will be set to false (note that it's never set to true -- set it to true before calling these).
+    // if required is true, failure to extract an argument will cause a warning to be printed.
+    static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments,
+                                          QString argumentName, bool& ok, bool required = true);
+    static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments,
+                                          QString argumentName, bool& ok, bool required = true);
+    static float extractFloatArgument(QString objectName, QVariantMap arguments,
+                                      QString argumentName, bool& ok, bool required = true);
+    static QString extractStringArgument(QString objectName, QVariantMap arguments,
+                                         QString argumentName, bool& ok, bool required = true);
 
-    static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
-    static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
 };
 
+
 typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
 
 #endif // hifi_EntityActionInterface_h
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 4a10aad95d..f64dad0ef4 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -70,7 +70,9 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
     _dirtyFlags(0),
     _element(nullptr),
     _physicsInfo(nullptr),
-    _simulated(false)
+    _simulated(false),
+    _href(""),
+    _description("")
 {
     quint64 now = usecTimestampNow();
     _lastSimulated = now;
@@ -117,6 +119,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
     requestedProperties += PROP_MARKETPLACE_ID;
     requestedProperties += PROP_NAME;
     requestedProperties += PROP_SIMULATOR_ID;
+    requestedProperties += PROP_HREF;
+    requestedProperties += PROP_DESCRIPTION;
     
     return requestedProperties;
 }
@@ -246,6 +250,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
         APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
         APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
         APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
+        APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
+        APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
+
 
         appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
                                 requestedProperties,
@@ -573,6 +580,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
 
     READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
     READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
+    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
+
     bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
 
     ////////////////////////////////////
@@ -905,6 +915,8 @@ EntityItemProperties EntityItem::getProperties() const {
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
 
     properties._defaultSettings = false;
     
@@ -963,6 +975,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
 
     if (somethingChanged) {
         uint64_t now = usecTimestampNow();
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 77a6627853..73f9127361 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -28,13 +28,16 @@
 #include "EntityItemID.h"
 #include "EntityItemProperties.h"
 #include "EntityItemPropertiesDefaults.h"
-#include "EntityActionInterface.h"
 #include "EntityTypes.h"
 
 class EntitySimulation;
 class EntityTreeElement;
 class EntityTreeElementExtraEncodeData;
 
+class EntityActionInterface;
+typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
+
+
 namespace render {
     class Scene;
     class PendingChanges;
@@ -203,7 +206,14 @@ public:
     
     inline const glm::quat& getRotation() const { return _transform.getRotation(); }
     inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); }
-    
+
+    // Hyperlink related getters and setters
+    QString getHref() const { return _href; }
+    void setHref(QString value) { _href = value; }
+
+    QString getDescription() const { return _description; }
+    void setDescription(QString value) { _description = value; }
+
     /// Dimensions in meters (0.0 - TREE_SCALE)
     inline const glm::vec3& getDimensions() const { return _transform.getScale(); }
     virtual void setDimensions(const glm::vec3& value);
@@ -415,6 +425,8 @@ protected:
     quint64 _simulatorIDChangedTime; // when was _simulatorID last updated?
     QString _marketplaceID;
     QString _name;
+    QString _href; //Hyperlink href
+    QString _description; //Hyperlink description
 
     // NOTE: Damping is applied like this:  v *= pow(1 - damping, dt)
     //
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 90f2b22698..cbb3b1dc31 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -347,6 +347,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
     CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle);
     CHECK_PROPERTY_CHANGE(PROP_LINE_WIDTH, lineWidth);
     CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints);
+    CHECK_PROPERTY_CHANGE(PROP_HREF, href);
+    CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
+
     changedProperties += _stage.getChangedProperties();
     changedProperties += _atmosphere.getChangedProperties();
     changedProperties += _skybox.getChangedProperties();
@@ -439,7 +442,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
     COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle);
     COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth);
     COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints);
-    
+    COPY_PROPERTY_TO_QSCRIPTVALUE(href);
+    COPY_PROPERTY_TO_QSCRIPTVALUE(description);
+
     // Sitting properties support
     if (!skipDefaults) {
         QScriptValue sittingPoints = engine->newObject();
@@ -548,6 +553,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
     COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(lineWidth, float, setLineWidth);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription);
+
 
     if (!honorReadOnly) {
         // this is used by the json reader to set things that we don't want javascript to able to affect.
@@ -712,6 +720,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
             APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
             APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
             APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID());
+            APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref());
+            APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
             
             if (properties.getType() == EntityTypes::Web) {
                 APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
@@ -962,6 +972,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
+    READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
     
     if (properties.getType() == EntityTypes::Web) {
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
@@ -1147,6 +1159,9 @@ void EntityItemProperties::markAllChanged() {
     _lineWidthChanged = true;
     _linePointsChanged = true;
 
+    _hrefChanged = true;
+    _descriptionChanged = true;
+
 }
 
 /// The maximum bounding cube for the entity, independent of it's rotation.
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 3c8133af8f..068bc98f7e 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -148,6 +148,8 @@ public:
     DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
     DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float);
     DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>);
+    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
+    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
 
     static QString getBackgroundModeString(BackgroundMode mode);
 
@@ -295,6 +297,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
     
     properties.getStage().debugDump();
     properties.getAtmosphere().debugDump();
diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h
index 8eb09fece0..f1ebdb8a1f 100644
--- a/libraries/entities/src/EntityPropertyFlags.h
+++ b/libraries/entities/src/EntityPropertyFlags.h
@@ -117,6 +117,10 @@ enum EntityPropertyList {
     //for lines
     PROP_LINE_WIDTH,
     PROP_LINE_POINTS,
+
+    // used by hyperlinks
+    PROP_HREF,
+    PROP_DESCRIPTION,
     
     ////////////////////////////////////////////////////////////////////////////////////////////////////
     // ATTENTION: add new properties ABOVE this line
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 836e7f5225..a091c786b7 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -17,6 +17,8 @@
 #include "ZoneEntityItem.h"
 #include "EntitiesLogging.h"
 #include "EntitySimulation.h"
+#include "EntityActionInterface.h"
+#include "EntityActionFactoryInterface.h"
 
 #include "EntityScriptingInterface.h"
 
@@ -491,12 +493,19 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
                                           const QUuid& entityID,
                                           const QVariantMap& arguments) {
     QUuid actionID = QUuid::createUuid();
+    auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
     bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
+            // create this action even if the entity doesn't have physics info.  it will often be the
+            // case that a script adds an action immediately after an object is created, and the physicsInfo
+            // is computed asynchronously.
+            // if (!entity->getPhysicsInfo()) {
+            //     return false;
+            // }
             EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
             if (actionType == ACTION_TYPE_NONE) {
                 return false;
             }
-            if (simulation->actionFactory(actionType, actionID, entity, arguments)) {
+            if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) {
                 return true;
             }
             return false;
diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h
index 0c9b3efee6..7d244086e5 100644
--- a/libraries/entities/src/EntitySimulation.h
+++ b/libraries/entities/src/EntitySimulation.h
@@ -18,6 +18,7 @@
 
 #include <PerfStat.h>
 
+#include "EntityActionInterface.h"
 #include "EntityItem.h"
 #include "EntityTree.h"
 
@@ -56,10 +57,6 @@ public:
 
     friend class EntityTree;
 
-    virtual EntityActionPointer actionFactory(EntityActionType type,
-                                                 QUuid id,
-                                                 EntityItemPointer ownerEntity,
-                                                 QVariantMap arguments) { return nullptr; }
     virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; }
     virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; }
     virtual void removeActions(QList<QUuid> actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; }
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index f282f7e35d..de8f53c503 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -2646,34 +2646,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
         }
     }
     geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
-
-    // process attachments
-    QVariantHash attachments = mapping.value("attach").toHash();
-    for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) {
-        FBXAttachment attachment;
-        attachment.jointIndex = modelIDs.indexOf(processID(it.key()));
-        attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f);
-
-        QVariantList properties = it->toList();
-        if (properties.isEmpty()) {
-            attachment.url = it->toString();
-        } else {
-            attachment.url = properties.at(0).toString();
-
-            if (properties.size() >= 2) {
-                attachment.translation = parseVec3(properties.at(1).toString());
-
-                if (properties.size() >= 3) {
-                    attachment.rotation = glm::quat(glm::radians(parseVec3(properties.at(2).toString())));
-
-                    if (properties.size() >= 4) {
-                        attachment.scale = parseVec3(properties.at(3).toString());
-                    }
-                }
-            }
-        }
-        geometry.attachments.append(attachment);
-    }
     
     // Add sitting points
     QVariantHash sittingPoints = mapping.value("sit").toHash();
diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h
index 08ac0e308c..200cd4a121 100644
--- a/libraries/fbx/src/FBXReader.h
+++ b/libraries/fbx/src/FBXReader.h
@@ -189,17 +189,6 @@ public:
 Q_DECLARE_METATYPE(FBXAnimationFrame)
 Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>)
 
-/// An attachment to an FBX document.
-class FBXAttachment {
-public:
-    
-    int jointIndex;
-    QUrl url;
-    glm::vec3 translation;
-    glm::quat rotation;
-    glm::vec3 scale;
-};
-
 /// A point where an avatar can sit
 class SittingPoint {
 public:
@@ -256,9 +245,7 @@ public:
     Extents meshExtents;
     
     QVector<FBXAnimationFrame> animationFrames;
-    
-    QVector<FBXAttachment> attachments;
-    
+        
     int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; }
     QStringList getJointNames() const;
     
diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp
index 32be82b392..a62c0fcea2 100644
--- a/libraries/fbx/src/FSTReader.cpp
+++ b/libraries/fbx/src/FSTReader.cpp
@@ -124,7 +124,9 @@ FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) {
         _namesToTypes["head"] = HEAD_MODEL ;
         _namesToTypes["body"] = BODY_ONLY_MODEL;
         _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL;
-        _namesToTypes["attachment"] = ATTACHMENT_MODEL;
+        
+        // NOTE: this is not yet implemented, but will be used to allow you to attach fully independent models to your avatar
+        _namesToTypes["attachment"] = ATTACHMENT_MODEL; 
     }
     return _namesToTypes[name];
 }
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
index 080fb98690..b7fea218d8 100644
--- a/libraries/fbx/src/OBJReader.cpp
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -544,7 +544,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
     qCDebug(modelformat) << "---------------- fbxGeometry ----------------";
     qCDebug(modelformat) << "  hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints;
     qCDebug(modelformat) << "  offset =" << fbxgeo.offset;
-    qCDebug(modelformat) << "  attachments.count() = " << fbxgeo.attachments.count();
     qCDebug(modelformat) << "  meshes.count() =" << fbxgeo.meshes.count();
     foreach (FBXMesh mesh, fbxgeo.meshes) {
         qCDebug(modelformat) << "    vertices.count() =" << mesh.vertices.count();
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index 642ca4748d..51575bfde5 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -218,7 +218,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
                 const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
                 const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
 
-               DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
+                DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
 
                 const QString DOMAIN_ID_KEY = "id";
                 QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 23237cab32..186ff40f60 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -180,10 +180,6 @@ btCollisionShape* EntityMotionState::computeNewShape() {
     return nullptr;
 }
 
-// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates
-// we alwasy resend packets for objects that have stopped moving up to some max limit.
-const int MAX_NUM_NON_MOVING_UPDATES = 5;
-
 bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { 
     if (!_body || !_entity) {
         return false;
@@ -495,6 +491,10 @@ void EntityMotionState::measureBodyAcceleration() {
         glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
         _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
         _lastVelocity = velocity;
+        if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS && !_candidateForOwnership) {
+            _loopsSinceOwnershipBid = 0;
+            _loopsWithoutOwner = 0;
+        }
     }
 }
 glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const {
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index 6ff4098ba8..ee7ba9ce7c 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -24,7 +24,14 @@ ObjectAction::~ObjectAction() {
 }
 
 void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
-    qDebug() << "ObjectAction::updateAction called";
+    if (!_active) {
+        return;
+    }
+    if (!_ownerEntity) {
+        qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
+        return;
+    }
+    updateActionWorker(deltaTimeStep);
 }
 
 void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
@@ -33,3 +40,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
 void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const {
     simulation->removeAction(_id);
 }
+
+btRigidBody* ObjectAction::getRigidBody() {
+    if (!_ownerEntity) {
+        return nullptr;
+    }
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        return nullptr;
+    }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    return motionState->getRigidBody();
+}
+
+glm::vec3 ObjectAction::getPosition() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getCenterOfMassPosition());
+}
+
+void ObjectAction::setPosition(glm::vec3 position) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    // XXX
+    // void setWorldTransform (const btTransform &worldTrans)
+    assert(false);
+    rigidBody->activate();
+}
+
+glm::quat ObjectAction::getRotation() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
+    }
+    return bulletToGLM(rigidBody->getOrientation());
+}
+
+void ObjectAction::setRotation(glm::quat rotation) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    // XXX
+    // void setWorldTransform (const btTransform &worldTrans)
+    assert(false);
+    rigidBody->activate();
+}
+
+glm::vec3 ObjectAction::getLinearVelocity() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getLinearVelocity());
+}
+
+void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
+    rigidBody->activate();
+}
+
+glm::vec3 ObjectAction::getAngularVelocity() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getAngularVelocity());
+}
+
+void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
+    rigidBody->activate();
+}
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
index 10b086c07d..0fd7383e6f 100644
--- a/libraries/physics/src/ObjectAction.h
+++ b/libraries/physics/src/ObjectAction.h
@@ -13,12 +13,17 @@
 #ifndef hifi_ObjectAction_h
 #define hifi_ObjectAction_h
 
-#include <btBulletDynamicsCommon.h>
-
 #include <QUuid>
 
+#include <btBulletDynamicsCommon.h>
+
 #include <EntityItem.h>
 
+#include "ObjectMotionState.h"
+#include "BulletUtil.h"
+#include "EntityActionInterface.h"
+
+
 class ObjectAction : public btActionInterface, public EntityActionInterface {
 public:
     ObjectAction(QUuid id, EntityItemPointer ownerEntity);
@@ -30,6 +35,9 @@ public:
     virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
     virtual bool updateArguments(QVariantMap arguments) { return false; }
 
+    // this is called from updateAction and should be overridden by subclasses
+    virtual void updateActionWorker(float deltaTimeStep) {}
+
     // these are from btActionInterface
     virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
     virtual void debugDraw(btIDebugDraw* debugDrawer);
@@ -39,6 +47,16 @@ private:
     QReadWriteLock _lock;
 
 protected:
+    virtual btRigidBody* getRigidBody();
+    virtual glm::vec3 getPosition();
+    virtual void setPosition(glm::vec3 position);
+    virtual glm::quat getRotation();
+    virtual void setRotation(glm::quat rotation);
+    virtual glm::vec3 getLinearVelocity();
+    virtual void setLinearVelocity(glm::vec3 linearVelocity);
+    virtual glm::vec3 getAngularVelocity();
+    virtual void setAngularVelocity(glm::vec3 angularVelocity);
+
     bool tryLockForRead() { return _lock.tryLockForRead(); }
     void lockForWrite() { _lock.lockForWrite(); }
     void unlock() { _lock.unlock(); }
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
index 78f202a24f..053bef6a03 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.cpp
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -9,9 +9,6 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ObjectMotionState.h"
-#include "BulletUtil.h"
-
 #include "ObjectActionPullToPoint.h"
 
 ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) :
@@ -27,28 +24,34 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
     #endif
 }
 
-void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) {
     if (!tryLockForRead()) {
         // don't risk hanging the thread running the physics simulation
         return;
     }
-    void* physicsInfo = _ownerEntity->getPhysicsInfo();
 
-    if (_active && physicsInfo) {
-        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
-        btRigidBody* rigidBody = motionState->getRigidBody();
-        if (rigidBody) {
-            glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
-            float offsetLength = glm::length(offset);
-            if (offsetLength > IGNORE_POSITION_DELTA) {
-                glm::vec3 newVelocity = glm::normalize(offset) * _speed;
-                rigidBody->setLinearVelocity(glmToBullet(newVelocity));
-                rigidBody->activate();
-            } else {
-                rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
-            }
-        }
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        unlock();
+        return;
     }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    btRigidBody* rigidBody = motionState->getRigidBody();
+    if (!rigidBody) {
+        unlock();
+        return;
+    }
+
+    glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
+    float offsetLength = glm::length(offset);
+    if (offsetLength > IGNORE_POSITION_DELTA) {
+        glm::vec3 newVelocity = glm::normalize(offset) * _speed;
+        rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+        rigidBody->activate();
+    } else {
+        rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
+    }
+
     unlock();
 }
 
diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h
index 3aca70d640..2596f13515 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.h
+++ b/libraries/physics/src/ObjectActionPullToPoint.h
@@ -23,7 +23,7 @@ public:
     virtual ~ObjectActionPullToPoint();
 
     virtual bool updateArguments(QVariantMap arguments);
-    virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+    virtual void updateActionWorker(float deltaTimeStep);
 
 private:
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
new file mode 100644
index 0000000000..8eb4f7f652
--- /dev/null
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -0,0 +1,144 @@
+//
+//  ObjectActionSpring.cpp
+//  libraries/physics/src
+//
+//  Created by Seth Alves 2015-6-5
+//  Copyright 2015 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 "ObjectActionSpring.h"
+
+ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) :
+    ObjectAction(id, ownerEntity) {
+    #if WANT_DEBUG
+    qDebug() << "ObjectActionSpring::ObjectActionSpring";
+    #endif
+}
+
+ObjectActionSpring::~ObjectActionSpring() {
+    #if WANT_DEBUG
+    qDebug() << "ObjectActionSpring::~ObjectActionSpring";
+    #endif
+}
+
+void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
+    if (!tryLockForRead()) {
+        // don't risk hanging the thread running the physics simulation
+        qDebug() << "ObjectActionSpring::updateActionWorker lock failed";
+        return;
+    }
+
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        unlock();
+        return;
+    }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    btRigidBody* rigidBody = motionState->getRigidBody();
+    if (!rigidBody) {
+        unlock();
+        qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody";
+        return;
+    }
+
+    // handle the linear part
+    if (_positionalTargetSet) {
+        glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
+        float offsetLength = glm::length(offset);
+        float speed = offsetLength / _linearTimeScale;
+
+        if (offsetLength > IGNORE_POSITION_DELTA) {
+            glm::vec3 newVelocity = glm::normalize(offset) * speed;
+            rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+            rigidBody->activate();
+        } else {
+            rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
+        }
+    }
+
+    // handle rotation
+    if (_rotationalTargetSet) {
+        glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
+        // if qZero and qOne are too close to each other, we can get NaN for angle.
+        auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
+        const float almostOne = 0.99999f;
+        if (glm::abs(alignmentDot) < almostOne) {
+            glm::quat target = _rotationalTarget;
+            if (alignmentDot < 0) {
+                target = -target;
+            }
+            glm::quat qZeroInverse = glm::inverse(bodyRotation);
+            glm::quat deltaQ = target * qZeroInverse;
+            glm::vec3 axis = glm::axis(deltaQ);
+            float angle = glm::angle(deltaQ);
+            assert(!isNaN(angle));
+            glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
+            rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
+            rigidBody->activate();
+        } else {
+            rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
+        }
+    }
+
+    unlock();
+}
+
+
+bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
+    // targets are required, spring-constants are optional
+    bool ptOk = true;
+    glm::vec3 positionalTarget =
+        EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
+    bool pscOk = true;
+    float linearTimeScale =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false);
+    if (ptOk && pscOk && linearTimeScale <= 0.0f) {
+        qDebug() << "spring action -- linearTimeScale must be greater than zero.";
+        return false;
+    }
+
+    bool rtOk = true;
+    glm::quat rotationalTarget =
+        EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
+    bool rscOk = true;
+    float angularTimeScale =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false);
+
+    if (!ptOk && !rtOk) {
+        qDebug() << "spring action requires either targetPosition or targetRotation argument";
+        return false;
+    }
+
+    lockForWrite();
+
+    _positionalTargetSet = _rotationalTargetSet = false;
+
+    if (ptOk) {
+        _positionalTarget = positionalTarget;
+        _positionalTargetSet = true;
+
+        if (pscOk) {
+            _linearTimeScale = linearTimeScale;
+        } else {
+            _linearTimeScale = 0.1f;
+        }
+    }
+
+    if (rtOk) {
+        _rotationalTarget = rotationalTarget;
+        _rotationalTargetSet = true;
+
+        if (rscOk) {
+            _angularTimeScale = angularTimeScale;
+        } else {
+            _angularTimeScale = 0.1f;
+        }
+    }
+
+    _active = true;
+    unlock();
+    return true;
+}
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
new file mode 100644
index 0000000000..9f3df0fdf8
--- /dev/null
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -0,0 +1,39 @@
+//
+//  ObjectActionSpring.h
+//  libraries/physics/src
+//
+//  Created by Seth Alves 2015-6-5
+//  Copyright 2015 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_ObjectActionSpring_h
+#define hifi_ObjectActionSpring_h
+
+#include <QUuid>
+
+#include <EntityItem.h>
+#include "ObjectAction.h"
+
+class ObjectActionSpring : public ObjectAction {
+public:
+    ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity);
+    virtual ~ObjectActionSpring();
+
+    virtual bool updateArguments(QVariantMap arguments);
+    virtual void updateActionWorker(float deltaTimeStep);
+
+protected:
+
+    glm::vec3 _positionalTarget;
+    float _linearTimeScale;
+    bool _positionalTargetSet;
+
+    glm::quat _rotationalTarget;
+    float _angularTimeScale;
+    bool _rotationalTargetSet;
+};
+
+#endif // hifi_ObjectActionSpring_h
diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h
index b17dc67cff..561ce02d62 100644
--- a/libraries/physics/src/ObjectMotionState.h
+++ b/libraries/physics/src/ObjectMotionState.h
@@ -53,8 +53,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | Enti
 class OctreeEditPacketSender;
 class PhysicsEngine;
 
-extern const int MAX_NUM_NON_MOVING_UPDATES;
-
 class ObjectMotionState : public btMotionState {
 public:
     // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 711c5e49da..c68b993fe2 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+
+
 #include "PhysicsHelpers.h"
 #include "PhysicsLogging.h"
 #include "ShapeManager.h"
-#include "ObjectActionPullToPoint.h"
 
 #include "PhysicalEntitySimulation.h"
 
@@ -234,29 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE
     }
 }
 
-EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type,
-                                                               QUuid id,
-                                                               EntityItemPointer ownerEntity,
-                                                               QVariantMap arguments) {
-    EntityActionPointer action = nullptr;
-    switch (type) {
-        case ACTION_TYPE_NONE:
-            return nullptr;
-        case ACTION_TYPE_PULL_TO_POINT:
-            action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
-            break;
-    }
-
-    bool ok = action->updateArguments(arguments);
-    if (ok) {
-        ownerEntity->addAction(this, action);
-        return action;
-    }
-
-    action = nullptr;
-    return action;
-}
-
 void PhysicalEntitySimulation::applyActionChanges() {
     if (_physicsEngine) {
         foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h
index 82b0f8ad51..81ab9f5cce 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.h
+++ b/libraries/physics/src/PhysicalEntitySimulation.h
@@ -32,10 +32,6 @@ public:
 
     void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender);
 
-    virtual EntityActionPointer actionFactory(EntityActionType type,
-                                              QUuid id,
-                                              EntityItemPointer ownerEntity,
-                                              QVariantMap arguments);
     virtual void applyActionChanges();
 
 protected: // only called by EntitySimulation
diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp
index e5c974dfbc..55fc5e6295 100644
--- a/libraries/physics/src/PhysicsEngine.cpp
+++ b/libraries/physics/src/PhysicsEngine.cpp
@@ -227,8 +227,7 @@ void PhysicsEngine::stepSimulation() {
     // (3) synchronize outgoing motion states
     // (4) send outgoing packets
 
-    const int MAX_NUM_SUBSTEPS = 4;
-    const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP;
+    const float MAX_TIMESTEP = (float)PHYSICS_ENGINE_MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP;
     float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds());
     _clock.reset();
     float timeStep = btMin(dt, MAX_TIMESTEP);
@@ -245,7 +244,7 @@ void PhysicsEngine::stepSimulation() {
         _characterController->preSimulation(timeStep);
     }
 
-    int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
+    int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
     if (numSubsteps > 0) {
         BT_PROFILE("postSimulation");
         _numSubsteps += (uint32_t)numSubsteps;
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 2ae8968571..145f352658 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -405,9 +405,6 @@ void Model::reset() {
     if (_jointStates.isEmpty()) {
         return;
     }
-    foreach (Model* attachment, _attachments) {
-        attachment->reset();
-    }
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
     for (int i = 0; i < _jointStates.size(); i++) {
         _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
@@ -419,14 +416,7 @@ void Model::reset() {
 }
 
 bool Model::updateGeometry() {
-    // NOTE: this is a recursive call that walks all attachments, and their attachments
     bool needFullUpdate = false;
-    for (int i = 0; i < _attachments.size(); i++) {
-        Model* model = _attachments.at(i);
-        if (model->updateGeometry()) {
-            needFullUpdate = true;
-        }
-    }
 
     bool needToRebuild = false;
     if (_nextGeometry) {
@@ -445,6 +435,7 @@ bool Model::updateGeometry() {
 
     QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis);
     if (_geometry != geometry) {
+
         // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above.
         // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry.
 
@@ -499,12 +490,6 @@ bool Model::updateGeometry() {
             }
             _blendedVertexBuffers.push_back(buffer);
         }
-        foreach (const FBXAttachment& attachment, fbxGeometry.attachments) {
-            Model* model = new Model(this);
-            model->init();
-            model->setURL(attachment.url);
-            _attachments.append(model);
-        }
         needFullUpdate = true;
     }
     return needFullUpdate;
@@ -827,71 +812,41 @@ void Model::renderSetup(RenderArgs* args) {
 }
 
 
-class TransparentMeshPart {
+class MeshPartPayload {
 public:
-    TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
-    typedef render::Payload<TransparentMeshPart> Payload;
+    MeshPartPayload(bool transparent, Model* model, int meshIndex, int partIndex) :
+        transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { }
+    typedef render::Payload<MeshPartPayload> Payload;
     typedef Payload::DataPointer Pointer;
    
-    Model* model;   
+    bool transparent;
+    Model* model;
+    QUrl url;
     int meshIndex;
     int partIndex;
 };
 
 namespace render {
-    template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) { 
+    template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { 
         if (!payload->model->isVisible()) {
             return ItemKey::Builder().withInvisible().build();
         }
-        return ItemKey::Builder::transparentShape();
+        return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape();
     }
     
-    template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) { 
+    template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { 
         if (payload) {
             return payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
         }
         return render::Item::Bound();
     }
-    template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) {
+    template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) {
         if (args) {
-            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true);
+            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, payload->transparent);
         }
     }
-}
 
-class OpaqueMeshPart {
-public:
-    OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
-    typedef render::Payload<OpaqueMeshPart> Payload;
-    typedef Payload::DataPointer Pointer;
-
-    Model* model;   
-    int meshIndex;
-    int partIndex;
-};
-
-namespace render {
-    template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) { 
-        if (!payload->model->isVisible()) {
-            return ItemKey::Builder().withInvisible().build();
-        }
-        return ItemKey::Builder::opaqueShape();
-    }
-    
-    template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) { 
-        if (payload) {
-            Item::Bound result = payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
-            //qDebug() << "payloadGetBound(OpaqueMeshPart) " << result;
-            return result;
-        }
-        return render::Item::Bound();
-    }
-    template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) {
-        if (args) {
-            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false);
-        }
-    }
-   /* template <> const model::MaterialKey& shapeGetMaterialKey(const OpaqueMeshPart::Pointer& payload) {
+   /* template <> const model::MaterialKey& shapeGetMaterialKey(const MeshPartPayload::Pointer& payload) {
         return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex);
     }*/
 }
@@ -916,24 +871,19 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
 
     bool somethingAdded = false;
 
-    // allow the attachments to add to scene
-    foreach (Model* attachment, _attachments) {
-        bool attachementSomethingAdded = attachment->addToScene(scene, pendingChanges);
-        somethingAdded = somethingAdded || attachementSomethingAdded;
-    }
-    
     foreach (auto renderItem, _transparentRenderItems) {
         auto item = scene->allocateID();
-        auto renderData = TransparentMeshPart::Pointer(renderItem);
-        auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData));
+        auto renderData = MeshPartPayload::Pointer(renderItem);
+        auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
         pendingChanges.resetItem(item, renderPayload);
         _renderItems.insert(item, renderPayload);
         somethingAdded = true;
     }
+
     foreach (auto renderItem, _opaqueRenderItems) {
         auto item = scene->allocateID();
-        auto renderData = OpaqueMeshPart::Pointer(renderItem);
-        auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData));
+        auto renderData = MeshPartPayload::Pointer(renderItem);
+        auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
         pendingChanges.resetItem(item, renderPayload);
         _renderItems.insert(item, renderPayload);
         somethingAdded = true;
@@ -945,11 +895,6 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
 }
 
 void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
-    // allow the attachments to remove to scene
-    foreach (Model* attachment, _attachments) {
-        attachment->removeFromScene(scene, pendingChanges);
-    }
-
     foreach (auto item, _renderItems.keys()) {
         pendingChanges.removeItem(item);
     }
@@ -957,210 +902,6 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
     _readyWhenAdded = false;
 }
 
-bool Model::render(RenderArgs* renderArgs, float alpha) {
-    return true; //
-    PROFILE_RANGE(__FUNCTION__);
-
-    // render the attachments
-    foreach (Model* attachment, _attachments) {
-        attachment->render(renderArgs, alpha);
-    }
-    if (_meshStates.isEmpty()) {
-        return false;
-    }
-
-    renderSetup(renderArgs);
-    return renderCore(renderArgs, alpha);
-}
-
-bool Model::renderCore(RenderArgs* args, float alpha) {
-  return true;
-  
-    PROFILE_RANGE(__FUNCTION__);
-    if (!_viewState) {
-        return false;
-    }
-
-    auto mode = args->_renderMode;
-
-    // Let's introduce a gpu::Batch to capture all the calls to the graphics api
-    _renderBatch.clear();
-    gpu::Batch& batch = _renderBatch;
-
-    // Setup the projection matrix
-    if (args && args->_viewFrustum) {
-        glm::mat4 proj;
-        // If for easier debug depending on the pass
-        if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-            args->_viewFrustum->evalProjectionMatrix(proj); 
-        } else {
-            args->_viewFrustum->evalProjectionMatrix(proj); 
-        }
-        batch.setProjectionTransform(proj);
-    }
-
-    // Capture the view matrix once for the rendering of this model
-    if (_transforms.empty()) {
-        _transforms.push_back(Transform());
-    }
-
-    _transforms[0] = _viewState->getViewTransform();
-
-    // apply entity translation offset to the viewTransform  in one go (it's a preTranslate because viewTransform goes from world to eye space)
-    _transforms[0].preTranslate(-_translation);
-
-    batch.setViewTransform(_transforms[0]);
-
-    /*DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(
-        mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE,
-        mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE,
-        mode == RenderArgs::DEFAULT_RENDER_MODE);
-        */
-     /*if (mode != RenderArgs::SHADOW_RENDER_MODE)*/ {
-        GLenum buffers[3];
-        int bufferCount = 0;
-
-       // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
-        }
-     //   if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
-        }
-       // if (mode == RenderArgs::DEFAULT_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
-        }
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-      //  batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer());
-    }
-
-    const float DEFAULT_ALPHA_THRESHOLD = 0.5f;
-    
-
-    //renderMeshes(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, forceRenderMeshes);
-    int opaqueMeshPartsRendered = 0;
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, false, args, true);
-
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, false, args, true);
-
-    // render translucent meshes afterwards
-    //DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(false, true, true);
-    {
-        GLenum buffers[2];
-        int bufferCount = 0;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-    }
-
-    int translucentMeshPartsRendered = 0;
-    const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, false, args, true);
-
-    {
-        GLenum buffers[1];
-        int bufferCount = 0;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-    }
-
-   // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) {
-    if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-    //    batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryTransparentFramebuffer());
-
-        const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f;
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, false, args, true);
-
-   //     batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer());
-    }
-
-    GLBATCH(glDepthMask)(true);
-    GLBATCH(glDepthFunc)(GL_LESS);
-    GLBATCH(glDisable)(GL_CULL_FACE);
-    
-    if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-        GLBATCH(glCullFace)(GL_BACK);
-    }
-
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 1);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 2);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 3);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-
-    // deactivate vertex arrays after drawing
-    GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY);
-    GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY);
-    GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY);
-    GLBATCH(glDisableClientState)(GL_COLOR_ARRAY);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT);
-    
-    // bind with 0 to switch back to normal operation
-    GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0);
-    GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, 0);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-
-    // Back to no program
-    GLBATCH(glUseProgram)(0);
-
-    // Render!
-    {
-        PROFILE_RANGE("render Batch");
-
-        #if defined(ANDROID)
-        #else
-            glPushMatrix();
-        #endif
-
-        ::gpu::GLBackend::renderBatch(batch, true); // force sync with gl state here
- 
-        #if defined(ANDROID)
-        #else
-            glPopMatrix();
-        #endif
-    }
-
-    // restore all the default material settings
-    _viewState->setupWorldLight();
-    
-    #ifdef WANT_DEBUG_MESHBOXES
-    renderDebugMeshBoxes();
-    #endif
-    
-    return true;
-}
-
 void Model::renderDebugMeshBoxes() {
     int colorNdx = 0;
     _mutex.lock();
@@ -1267,12 +1008,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const {
 
     Extents translatedExtents = { rotatedExtents.minimum + _translation, 
                                   rotatedExtents.maximum + _translation };
+
     return translatedExtents;
 }
 
 /// Returns the world space equivalent of some box in model space.
 AABox Model::calculateScaledOffsetAABox(const AABox& box) const {
-    
     return AABox(calculateScaledOffsetExtents(Extents(box)));
 }
 
@@ -1341,9 +1082,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
     if (_url == url && _geometry && _geometry->getURL() == url) {
         return;
     }
-    
+
     _readyWhenAdded = false; // reset out render items.
     _needsReload = true;
+    invalidCalculatedMeshBoxes();
     
     _url = url;
 
@@ -1532,7 +1274,7 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
     }
 }
 
-void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
+void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRescale) {
     // NOTE: if the model is not active, then it means we don't actually know the true/natural dimensions of the
     // mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this
     // case we will record that we do want to do this, but we will stick our desired single dimension into the
@@ -1545,7 +1287,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
         return;
     }
     
-    if (_scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) {
+    if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) {
         _scaleToFit = scaleToFit;
         
         // we only need to do this work if we're "turning on" scale to fit.
@@ -1555,7 +1297,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
             float maxScale = largestDimension / maxDimension;
             glm::vec3 modelMeshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
             glm::vec3 dimensions = modelMeshDimensions * maxScale;
-        
+
             _scaleToFitDimensions = dimensions;
             _scaledToFit = false; // force rescaling
         }
@@ -1626,7 +1368,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
 }
 
 void Model::simulateInternal(float deltaTime) {
-    // NOTE: this is a recursive call that walks all attachments, and their attachments
     // update the world space transforms for all joints
     
     // update animations
@@ -1643,31 +1384,7 @@ void Model::simulateInternal(float deltaTime) {
 
     _shapesAreDirty = !_shapes.isEmpty();
     
-    // update the attachment transforms and simulate them
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
-    for (int i = 0; i < _attachments.size(); i++) {
-        const FBXAttachment& attachment = geometry.attachments.at(i);
-        Model* model = _attachments.at(i);
-        
-        glm::vec3 jointTranslation = _translation;
-        glm::quat jointRotation = _rotation;
-        if (_showTrueJointTransforms) {
-            getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
-            getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
-        } else {
-            getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
-            getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
-        }
-        
-        model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
-        model->setRotation(jointRotation * attachment.rotation);
-        model->setScale(_scale * attachment.scale);
-        
-        if (model->isActive()) {
-            model->simulateInternal(deltaTime);
-        }
-    }
-    
     glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
     for (int i = 0; i < _meshStates.size(); i++) {
         MeshState& state = _meshStates[i];
@@ -2005,10 +1722,6 @@ void Model::applyNextGeometry() {
 }
 
 void Model::deleteGeometry() {
-    foreach (Model* attachment, _attachments) {
-        delete attachment;
-    }
-    _attachments.clear();
     _blendedVertexBuffers.clear();
     _jointStates.clear();
     _meshStates.clear();
@@ -2051,13 +1764,38 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
     if (!_calculatedMeshPartBoxesValid) {
         recalculateMeshBoxes(true);
     }
+
+    if (meshIndex < _meshStates.size()) {
+        const MeshState& state = _meshStates.at(meshIndex);
+        bool isSkinned = state.clusterMatrices.size() > 1;
+        if (isSkinned) {
+            // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us
+            return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
+        }
+    }
+    
     if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair<int,int>(meshIndex, partIndex))) {
-        return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
+        
+        // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
+        //           box of the mesh part fails. It seems to create boxes that are not consistent with where the
+        //           geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh
+        //           things will render properly.
+        //
+        //    return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
+        //
+        //    NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly
+        //          without recalculating them...
+        //    return _calculatedMeshBoxes[meshIndex];
+        //
+        // If we not skinned use the bounds of the subMesh for all it's parts
+        const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex);
+        return calculateScaledOffsetExtents(mesh.meshExtents);
     }
     return AABox();
 }
 
 void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) {
+
     if (!_readyWhenAdded) {
         return; // bail asap
     }
@@ -2072,20 +1810,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     gpu::Batch& batch = *(args->_batch);
     auto mode = args->_renderMode;
 
-    // render the part bounding box
-    #ifdef DEBUG_BOUNDING_PARTS
-    {
-        glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f);
-        AABox partBounds = getPartBounds(meshIndex, partIndex);
-
-        glm::mat4 translation = glm::translate(partBounds.calcCenter());
-        glm::mat4 scale = glm::scale(partBounds.getDimensions());
-        glm::mat4 modelToWorldMatrix = translation * scale;
-        batch.setModelTransform(modelToWorldMatrix);
-        //qDebug() << "partBounds:" << partBounds;
-        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
-    }
-    #endif //def DEBUG_BOUNDING_PARTS
 
     // Capture the view matrix once for the rendering of this model
     if (_transforms.empty()) {
@@ -2112,6 +1836,29 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     bool hasLightmap = mesh.hasEmissiveTexture();
     bool isSkinned = state.clusterMatrices.size() > 1;
     bool wireframe = isWireframe();
+
+    // render the part bounding box
+    #ifdef DEBUG_BOUNDING_PARTS
+    {
+        AABox partBounds = getPartBounds(meshIndex, partIndex);
+        bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE;
+
+        glm::vec4 cubeColor;
+        if (isSkinned) {
+            cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f);
+        } else if (inView) {
+            cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f);
+        } else {
+            cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f);
+        }
+
+        Transform transform;
+        transform.setTranslation(partBounds.calcCenter());
+        transform.setScale(partBounds.getDimensions());
+        batch.setModelTransform(transform);
+        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
+    }
+    #endif //def DEBUG_BOUNDING_PARTS
     
     if (wireframe) {
         translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
@@ -2172,16 +1919,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
 
     // guard against partially loaded meshes
     if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) {
-        return; 
+        return;
     }
 
     const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex);
     const FBXMeshPart& part = mesh.parts.at(partIndex);
     model::MaterialPointer material = part._material;
 
+    #ifdef WANT_DEBUG
     if (material == nullptr) {
-    //    qCDebug(renderutils) << "WARNING: material == nullptr!!!";
+        qCDebug(renderutils) << "WARNING: material == nullptr!!!";
     }
+    #endif
     
     if (material != nullptr) {
 
@@ -2263,7 +2012,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     }
     
     qint64 offset = _calculatedMeshPartOffet[QPair<int,int>(meshIndex, partIndex)];
-    
+
     if (part.quadIndices.size() > 0) {
         batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
         offset += part.quadIndices.size() * sizeof(int);
@@ -2283,8 +2032,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
 }
 
 void Model::segregateMeshGroups() {
-    _renderBuckets.clear();
-
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
     const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
 
@@ -2294,6 +2041,9 @@ void Model::segregateMeshGroups() {
         qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
         return;
     }
+    
+    _transparentRenderItems.clear();
+    _opaqueRenderItems.clear();
 
     // Run through all of the meshes, and place them into their segregated, but unsorted buckets
     for (int i = 0; i < networkMeshes.size(); i++) {
@@ -2318,67 +2068,23 @@ void Model::segregateMeshGroups() {
         for (int partIndex = 0; partIndex < totalParts; partIndex++) {
             // this is a good place to create our renderPayloads
             if (translucentMesh) {
-                _transparentRenderItems << std::shared_ptr<TransparentMeshPart>(new TransparentMeshPart(this, i, partIndex));
+                _transparentRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(true, this, i, partIndex));
             } else {
-                _opaqueRenderItems << std::shared_ptr<OpaqueMeshPart>(new OpaqueMeshPart(this, i, partIndex));
+                _opaqueRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(false, this, i, partIndex));
             }
         }
-
-        
-        QString materialID;
-
-        // create a material name from all the parts. If there's one part, this will be a single material and its
-        // true name. If however the mesh has multiple parts the name will be all the part's materials mashed together
-        // which will result in those parts being sorted away from single material parts.
-        QString lastPartMaterialID;
-        foreach(FBXMeshPart part, mesh.parts) {
-            if (part.materialID != lastPartMaterialID) {
-                materialID += part.materialID;
-            }
-            lastPartMaterialID = part.materialID;
-        }
-        const bool wantDebug = false;
-        if (wantDebug) {
-            qCDebug(renderutils) << "materialID:" << materialID << "parts:" << mesh.parts.size();
-        }
-        
-        RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe);
-
-        // reuse or create the bucket corresponding to that key and insert the mesh as unsorted
-        _renderBuckets[key.getRaw()]._unsortedMeshes.insertMulti(materialID, i);
     }
-    
-    for(auto& b : _renderBuckets) {
-        foreach(auto i, b.second._unsortedMeshes) {
-            b.second._meshes.append(i);
-        }
-        b.second._unsortedMeshes.clear();
-    }
-
     _meshGroupsKnown = true;
 } 
 
-QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) {
-    PROFILE_RANGE(__FUNCTION__);
-
-    // depending on which parameters we were called with, pick the correct mesh group to render
-    QVector<int>* whichList = NULL;
-
-    RenderKey key(translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
-
-    auto bucket = _renderBuckets.find(key.getRaw());
-    if (bucket != _renderBuckets.end()) {
-        whichList = &(*bucket).second._meshes;
-    }
-
-    return whichList;
-}
-
 void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
                             bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
                             Locations*& locations) {
 
     RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
+    if (mode == RenderArgs::MIRROR_RENDER_MODE) {
+        key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR);
+    }
     auto pipeline = _renderPipelineLib.find(key.getRaw());
     if (pipeline == _renderPipelineLib.end()) {
         qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw();
@@ -2402,212 +2108,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
     }
 }
 
-int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
-                            bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
-                            bool forceRenderSomeMeshes) {
-
-    PROFILE_RANGE(__FUNCTION__);
-    int meshPartsRendered = 0;
-
-    //Pick the mesh list with the requested render flags
-    QVector<int>* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
-    if (!whichList) {
-        return 0;
-    }
-    QVector<int>& list = *whichList;
-
-    // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs    
-    if (list.empty()) {
-        return 0;
-    }
-
-    Locations* locations = nullptr;
-    pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe,
-                                args, locations);
-    meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold,
-                                args, locations, forceRenderSomeMeshes);
-
-    return meshPartsRendered;
-}
-
-
-int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args,
-                                        Locations* locations, bool forceRenderMeshes) {
-    PROFILE_RANGE(__FUNCTION__);
-
-    auto textureCache = DependencyManager::get<TextureCache>();
-
-    QString lastMaterialID;
-    int meshPartsRendered = 0;
-    updateVisibleJointStates();
-    const FBXGeometry& geometry = _geometry->getFBXGeometry();
-    const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
-
-    // i is the "index" from the original networkMeshes QVector...
-    foreach (int i, list) {
-    
-        // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
-        // to false to rebuild out mesh groups.
-        
-        if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) {
-            _meshGroupsKnown = false; // regenerate these lists next time around.
-            _readyWhenAdded = false; // in case any of our users are using scenes
-            invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
-            continue;
-        }
-        
-        // exit early if the translucency doesn't match what we're drawing
-        const NetworkMesh& networkMesh = networkMeshes.at(i);
-        const FBXMesh& mesh = geometry.meshes.at(i);    
-
-        batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0);
-        int vertexCount = mesh.vertices.size();
-        if (vertexCount == 0) {
-            // sanity check
-            continue;
-        }
-        
-        // if we got here, then check to see if this mesh is in view
-        if (args) {
-            bool shouldRender = true;
-            if (args->_viewFrustum) {
-            
-                shouldRender = forceRenderMeshes || 
-                                    args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE;
-            
-                if (shouldRender && !forceRenderMeshes) {
-                    float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter());
-                    shouldRender = !_viewState ? false : _viewState->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(),
-                                                                            distance);
-                }
-            }
-
-            if (!shouldRender) {
-                continue; // skip this mesh
-            }
-        }
-
-        const MeshState& state = _meshStates.at(i);
-        if (state.clusterMatrices.size() > 1) {
-            GLBATCH(glUniformMatrix4fv)(locations->clusterMatrices, state.clusterMatrices.size(), false,
-                (const float*)state.clusterMatrices.constData());
-            batch.setModelTransform(Transform());
-        } else {
-            batch.setModelTransform(Transform(state.clusterMatrices[0]));
-        }
-
-        if (mesh.blendshapes.isEmpty()) {
-            batch.setInputFormat(networkMesh._vertexFormat);
-            batch.setInputStream(0, *networkMesh._vertexStream);
-        } else {
-            batch.setInputFormat(networkMesh._vertexFormat);
-            batch.setInputBuffer(0, _blendedVertexBuffers[i], 0, sizeof(glm::vec3));
-            batch.setInputBuffer(1, _blendedVertexBuffers[i], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3));
-            batch.setInputStream(2, *networkMesh._vertexStream);
-        }
-
-        if (mesh.colors.isEmpty()) {
-            GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f);
-        }
-
-        qint64 offset = 0;
-        for (int j = 0; j < networkMesh.parts.size(); j++) {
-            const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
-            const FBXMeshPart& part = mesh.parts.at(j);
-            model::MaterialPointer material = part._material;
-            if ((networkPart.isTranslucent() || part.opacity != 1.0f) != translucent) {
-                offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int);
-                continue;
-            }
-
-            // apply material properties
-            if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-             ///   GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-                
-            } else {
-                if (lastMaterialID != part.materialID) {
-                    const bool wantDebug = false;
-                    if (wantDebug) {
-                        qCDebug(renderutils) << "Material Changed ---------------------------------------------";
-                        qCDebug(renderutils) << "part INDEX:" << j;
-                        qCDebug(renderutils) << "NEW part.materialID:" << part.materialID;
-                    }
-
-                    if (locations->materialBufferUnit >= 0) {
-                        batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
-                    }
-
-                    Texture* diffuseMap = networkPart.diffuseTexture.data();
-                    if (mesh.isEye && diffuseMap) {
-                        diffuseMap = (_dilatedTextures[i][j] =
-                            static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
-                    }
-                    static bool showDiffuse = true;
-                    if (showDiffuse && diffuseMap) {
-                        batch.setUniformTexture(0, diffuseMap->getGPUTexture());
-                        
-                    } else {
-                        batch.setUniformTexture(0, textureCache->getWhiteTexture());
-                    }
-
-                    if (locations->texcoordMatrices >= 0) {
-                        glm::mat4 texcoordTransform[2];
-                        if (!part.diffuseTexture.transform.isIdentity()) {
-                            part.diffuseTexture.transform.getMatrix(texcoordTransform[0]);
-                        }
-                        if (!part.emissiveTexture.transform.isIdentity()) {
-                            part.emissiveTexture.transform.getMatrix(texcoordTransform[1]);
-                        }
-                        GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform);
-                    }
-
-                    if (!mesh.tangents.isEmpty()) {                 
-                        Texture* normalMap = networkPart.normalTexture.data();
-                        batch.setUniformTexture(1, !normalMap ?
-                            textureCache->getBlueTexture() : normalMap->getGPUTexture());
-
-                    }
-                
-                    if (locations->specularTextureUnit >= 0) {
-                        Texture* specularMap = networkPart.specularTexture.data();
-                        batch.setUniformTexture(locations->specularTextureUnit, !specularMap ?
-                                                    textureCache->getWhiteTexture() : specularMap->getGPUTexture());
-                    }
-                }
-
-                // HACK: For unkwon reason (yet!) this code that should be assigned only if the material changes need to be called for every
-                // drawcall with an emissive, so let's do it for now.
-                if (locations->emissiveTextureUnit >= 0) {
-                    //  assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader
-                    float emissiveOffset = part.emissiveParams.x;
-                    float emissiveScale = part.emissiveParams.y;
-                    GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale);
-
-                    Texture* emissiveMap = networkPart.emissiveTexture.data();
-                        batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ?
-                                                    textureCache->getWhiteTexture() : emissiveMap->getGPUTexture());
-                }
-
-                lastMaterialID = part.materialID;
-            }
-            
-            meshPartsRendered++;
-            
-            if (part.quadIndices.size() > 0) {
-                batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
-                offset += part.quadIndices.size() * sizeof(int);
-            }
-
-            if (part.triangleIndices.size() > 0) {
-                batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset);
-                offset += part.triangleIndices.size() * sizeof(int);
-            }
-
-        }
-    }
-
-    return meshPartsRendered;
-}
 
 ModelBlender::ModelBlender() :
     _pendingBlenders(0) {
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 6f751a5f8d..7c1572418e 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -51,17 +51,11 @@ namespace render {
     class PendingChanges;
     typedef unsigned int ItemID;
 }
-class OpaqueMeshPart;
-class TransparentMeshPart;
+class MeshPartPayload;
 
-inline uint qHash(const std::shared_ptr<TransparentMeshPart>& a, uint seed) {
+inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
     return qHash(a.get(), seed);
 }
-inline uint qHash(const std::shared_ptr<OpaqueMeshPart>& a, uint seed) {
-    return qHash(a.get(), seed);
-}
-
-
 
 /// A generic 3D model displaying geometry loaded from a URL.
 class Model : public QObject, public PhysicsEntity {
@@ -77,7 +71,7 @@ public:
     virtual ~Model();
     
     /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
-    void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
+    void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);
     bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
     bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
     const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
@@ -350,8 +344,6 @@ private:
 
     QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
     
-    QVector<Model*> _attachments;
-
     QSet<WeakAnimationHandlePointer> _animationHandles;
 
     QList<AnimationHandlePointer> _runningAnimations;
@@ -401,18 +393,8 @@ private:
     int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
 
     // helper functions used by render() or renderInScene()
-    bool renderCore(RenderArgs* args, float alpha);
-    int renderMeshes(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
-                        bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args = NULL,
-                        bool forceRenderMeshes = false);
-                        
+
     void setupBatchTransform(gpu::Batch& batch, RenderArgs* args);
-    QVector<int>* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe);
-
-    int renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
-                                        RenderArgs* args, Locations* locations, 
-                                        bool forceRenderSomeMeshes = false);
-
     static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
                             bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
                             Locations*& locations);
@@ -523,35 +505,15 @@ private:
     };
     static RenderPipelineLib _renderPipelineLib;
 
-   
-    class RenderBucket {
-    public:
-        QVector<int> _meshes;
-        QMap<QString, int> _unsortedMeshes;
-    };
-    typedef std::unordered_map<int, RenderBucket> BaseRenderBucketMap;
-    class RenderBucketMap : public BaseRenderBucketMap {
-    public:
-        typedef RenderKey Key;
-    };
-    RenderBucketMap _renderBuckets;
-
     bool _renderCollisionHull;
     
     
-    QSet<std::shared_ptr<TransparentMeshPart>> _transparentRenderItems;
-    QSet<std::shared_ptr<OpaqueMeshPart>> _opaqueRenderItems;
+    QSet<std::shared_ptr<MeshPartPayload>> _transparentRenderItems;
+    QSet<std::shared_ptr<MeshPartPayload>> _opaqueRenderItems;
     QMap<render::ItemID, render::PayloadPointer> _renderItems;
     bool _readyWhenAdded = false;
     bool _needsReload = true;
     
-    
-private:
-    // FIX ME - We want to get rid of this interface for rendering...
-    // right now the only remaining user are Avatar attachments.
-    // that usage has been temporarily disabled... 
-    bool render(RenderArgs* renderArgs, float alpha = 1.0f);
-    
 };
 
 Q_DECLARE_METATYPE(QPointer<Model>)
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 777d9466a5..d9dda279e0 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
    _jobs.push_back(Job(RenderDeferred()));
    _jobs.push_back(Job(ResolveDeferred()));
    _jobs.push_back(Job(DrawTransparentDeferred()));
+   _jobs.push_back(Job(DrawPostLayered()));
    _jobs.push_back(Job(ResetGLState()));
 }
 
@@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
 
     // render opaques
     auto& scene = sceneContext->_scene;
-    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered());
     auto& renderDetails = renderContext->args->_details;
 
     ItemIDsBounds inItems;
@@ -135,10 +136,12 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
         {
             GLenum buffers[3];
             int bufferCount = 0;
@@ -163,7 +166,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
 
     // render transparents
     auto& scene = sceneContext->_scene;
-    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered());
     auto& renderDetails = renderContext->args->_details;
     
     ItemIDsBounds inItems;
@@ -203,11 +206,12 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
-
         const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
 
         {
diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index d081c0480a..0eb560bf72 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -101,7 +101,7 @@ struct QuadBuilder {
                                     texMin);
     }
     QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) :
-    QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size,
+    QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size,
                     glyph.texOffset, glyph.texSize) {}
     
 };
@@ -113,7 +113,7 @@ public:
     void read(QIODevice& path);
 
     glm::vec2 computeExtent(const QString& str) const;
-    float getRowHeight() const { return _rowHeight; }
+    float getFontSize() const { return _fontSize; }
     
     // Render string to batch
     void drawString(gpu::Batch& batch, float x, float y, const QString& str,
@@ -139,7 +139,6 @@ private:
     // Font characteristics
     QString _family;
     float _fontSize = 0.0f;
-    float _rowHeight = 0.0f;
     float _leading = 0.0f;
     float _ascent = 0.0f;
     float _descent = 0.0f;
@@ -251,13 +250,14 @@ glm::vec2 Font3D::computeTokenExtent(const QString& token) const {
 glm::vec2 Font3D::computeExtent(const QString& str) const {
     glm::vec2 extent = glm::vec2(0.0f, 0.0f);
     
-    QStringList tokens = splitLines(str);
-    foreach(const QString& token, tokens) {
-        glm::vec2 tokenExtent = computeTokenExtent(token);
-        extent.x = std::max(tokenExtent.x, extent.x);
+    QStringList lines{ splitLines(str) };
+    if (!lines.empty()) {
+        for(const auto& line : lines) {
+            glm::vec2 tokenExtent = computeTokenExtent(line);
+            extent.x = std::max(tokenExtent.x, extent.x);
+        }
+        extent.y = lines.count() * _fontSize;
     }
-    extent.y = tokens.count() * _rowHeight;
-    
     return extent;
 }
 
@@ -288,8 +288,7 @@ void Font3D::read(QIODevice& in) {
     readStream(in, _descent);
     readStream(in, _spaceWidth);
     _fontSize = _ascent + _descent;
-    _rowHeight = _fontSize + _leading;
-
+    
     // Read character count
     uint16_t count;
     readStream(in, count);
@@ -393,7 +392,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
             }
             if (isNewLine || forceNewLine) {
                 // Character return, move the advance to a new line
-                advance = glm::vec2(x, advance.y - _rowHeight);
+                advance = glm::vec2(x, advance.y - _leading);
 
                 if (isNewLine) {
                     // No need to draw anything, go directly to next token
@@ -413,7 +412,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
                 for (auto c : token) {
                     auto glyph = _glyphs[c];
                     
-                    QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize));
+                    QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent));
                     _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd);
                     _numVertices += 4;
                     
@@ -477,9 +476,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const {
     return glm::vec2(0.0f, 0.0f);
 }
 
-float TextRenderer3D::getRowHeight() const {
+float TextRenderer3D::getFontSize() const {
     if (_font) {
-        return _font->getRowHeight();
+        return _font->getFontSize();
     }
     return 0.0f;
 }
diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h
index 8f55d0c977..e61203b06f 100644
--- a/libraries/render-utils/src/TextRenderer3D.h
+++ b/libraries/render-utils/src/TextRenderer3D.h
@@ -50,7 +50,7 @@ public:
     ~TextRenderer3D();
 
     glm::vec2 computeExtent(const QString& str) const;
-    float getRowHeight() const;
+    float getFontSize() const;
     
     void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f),
                const glm::vec2& bounds = glm::vec2(-1.0f));
diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp
index bfc888ea8a..bb4ba00cee 100755
--- a/libraries/render/src/render/DrawTask.cpp
+++ b/libraries/render/src/render/DrawTask.cpp
@@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo
     }
 }
 
-
 void addClearStateCommands(gpu::Batch& batch) {
     batch._glDepthMask(true);
     batch._glDepthFunc(GL_LESS);
@@ -264,7 +263,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer
 
     renderContext->_numDrawnOpaqueItems = renderedItems.size();
 
-
     ItemIDsBounds sortedItems;
     sortedItems.reserve(culledItems.size());
     if (renderContext->_sortOpaque) {
@@ -281,10 +279,12 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
         {
             GLenum buffers[3];
             int bufferCount = 0;
@@ -348,11 +348,12 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
-
         const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
         const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
 
@@ -435,6 +436,9 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi
     Transform viewMat;
     args->_viewFrustum->evalProjectionMatrix(projMat);
     args->_viewFrustum->evalViewTransform(viewMat);
+    if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+        viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+    }
     batch.setProjectionTransform(projMat);
     batch.setViewTransform(viewMat);
 
@@ -446,6 +450,51 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi
     args->_context->syncCache();
 }
 
+template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
+    PerformanceTimer perfTimer("DrawPostLayered");
+    assert(renderContext->args);
+    assert(renderContext->args->_viewFrustum);
+
+    // render backgrounds
+    auto& scene = sceneContext->_scene;
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
+
+
+    ItemIDsBounds inItems;
+    inItems.reserve(items.size());
+    for (auto id : items) {
+        auto& item = scene->getItem(id);
+        if (item.getKey().isVisible() && (item.getLayer() > 0)) {
+            inItems.emplace_back(id);
+        }
+    }
+    if (inItems.empty()) {
+        return;
+    }
+
+    RenderArgs* args = renderContext->args;
+    gpu::Batch batch;
+    args->_batch = &batch;
+
+    glm::mat4 projMat;
+    Transform viewMat;
+    args->_viewFrustum->evalProjectionMatrix(projMat);
+    args->_viewFrustum->evalViewTransform(viewMat);
+    if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+        viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+    }
+    batch.setProjectionTransform(projMat);
+    batch.setViewTransform(viewMat);
+
+    batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
+
+    renderItems(sceneContext, renderContext, inItems);
+    args->_context->render((*args->_batch));
+    args->_batch = nullptr;
+
+    // Force the context sync
+    args->_context->syncCache();
+}
 
 
 void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h
index 1f260583f2..0052c314c0 100755
--- a/libraries/render/src/render/DrawTask.h
+++ b/libraries/render/src/render/DrawTask.h
@@ -87,6 +87,14 @@ public:
 };
 template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
 
+
+class DrawPostLayered {
+public:
+};
+template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
+
+
+
 class ResetGLState {
 public:
 };
diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp
index 8615f7cf7a..1d2e54541b 100644
--- a/libraries/render/src/render/Scene.cpp
+++ b/libraries/render/src/render/Scene.cpp
@@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey
 }
 
 void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
-    (*this)[ItemFilter::Builder::opaqueShape()];
-    (*this)[ItemFilter::Builder::transparentShape()];
+    (*this)[ItemFilter::Builder::opaqueShape().withoutLayered()];
+    (*this)[ItemFilter::Builder::transparentShape().withoutLayered()];
     (*this)[ItemFilter::Builder::light()];
     (*this)[ItemFilter::Builder::background()];
+    (*this)[ItemFilter::Builder::opaqueShape().withLayered()];
+    (*this)[ItemFilter::Builder::transparentShape().withLayered()];
 }
 
 
@@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) {
     }
 }
 
-void Item::kill() {
-    _payload.reset();
-    _key._flags.reset();
-}
-
 void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
     _resetItems.push_back(id);
     _resetPayloads.push_back(payload);
@@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) {
         _items[(*updateID)].update((*updateFunctor));
     }
 }
-
-void Scene::registerObserver(ObserverPointer& observer) {
-    // make sure it's a valid observer
-    if (observer && (observer->getScene() == nullptr)) {
-        // Then register the observer
-        _observers.push_back(observer);
-
-        // And let it do what it wants to do
-        observer->registerScene(this);
-    }
-}
-
-void Scene::unregisterObserver(ObserverPointer& observer) {
-    // make sure it's a valid observer currently registered
-    if (observer && (observer->getScene() == this)) {
-        // let it do what it wants to do
-        observer->unregisterScene();
-
-        // Then unregister the observer
-        auto it = std::find(_observers.begin(), _observers.end(), observer);
-        _observers.erase(it);
-    }
-}
-
diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 8cb29609ba..5ec9f0c951 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -36,7 +36,6 @@ public:
     enum FlagBit {
         TYPE_SHAPE = 0,   // Item is a Shape
         TYPE_LIGHT,       // Item is a Light
-        TYPE_BACKGROUND,  // Item is a Background
         TRANSLUCENT,      // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
         VIEW_SPACE,       // Transformed in view space, and not in world space
         DYNAMIC,          // Dynamic and bound will change unlike static item
@@ -44,7 +43,7 @@ public:
         INVISIBLE,        // Visible or not? could be just here to cast shadow
         SHADOW_CASTER,    // Item cast shadows
         PICKABLE,         // Item can be picked/selected
-        NO_DEPTH_SORT,    // Item should not be depth sorted
+        LAYERED,          // Item belongs to one of the layers different from the default layer
 
         NUM_FLAGS,      // Not a valid flag
     };
@@ -57,6 +56,7 @@ public:
     ItemKey(const Flags& flags) : _flags(flags) {}
 
     class Builder {
+        friend class ItemKey;
         Flags _flags{ 0 };
     public:
         Builder() {}
@@ -65,7 +65,6 @@ public:
 
         Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
         Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
-        Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); }
         Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
         Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
         Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
@@ -73,14 +72,15 @@ public:
         Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
         Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
         Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
-        Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); }
+        Builder& withLayered() { _flags.set(LAYERED); return (*this); }
 
         // Convenient standard keys that we will keep on using all over the place
-        static ItemKey opaqueShape() { return Builder().withTypeShape().build(); }
-        static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
-        static ItemKey light() { return Builder().withTypeLight().build(); }
-        static ItemKey background() { return Builder().withTypeBackground().build(); }
+        static Builder opaqueShape() { return Builder().withTypeShape(); }
+        static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
+        static Builder light() { return Builder().withTypeLight(); }
+        static Builder background() { return Builder().withViewSpace().withLayered(); }
     };
+    ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
 
     bool isOpaque() const { return !_flags[TRANSLUCENT]; }
     bool isTransparent() const { return _flags[TRANSLUCENT]; }
@@ -101,8 +101,7 @@ public:
 
     bool isPickable() const { return _flags[PICKABLE]; }
 
-    bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; }
-    bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; }
+    bool isLayered() const { return _flags[LAYERED]; }
 };
 
 inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
@@ -122,6 +121,7 @@ public:
     ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
 
     class Builder {
+        friend class ItemFilter;
         ItemKey::Flags _value{ 0 };
         ItemKey::Flags _mask{ 0 };
     public:
@@ -131,7 +131,6 @@ public:
 
         Builder& withTypeShape()        { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
         Builder& withTypeLight()        { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
-        Builder& withTypeBackground()   { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); }
         
         Builder& withOpaque()           { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
         Builder& withTransparent()      { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
@@ -153,16 +152,18 @@ public:
 
         Builder& withPickable()         { _value.set(ItemKey::PICKABLE);  _mask.set(ItemKey::PICKABLE); return (*this); }
 
-        Builder& withDepthSort()      { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
-        Builder& withNotDepthSort()   { _value.set(ItemKey::NO_DEPTH_SORT);  _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
+        Builder& withoutLayered()       { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
+        Builder& withLayered()          { _value.set(ItemKey::LAYERED);  _mask.set(ItemKey::LAYERED); return (*this); }
 
         // Convenient standard keys that we will keep on using all over the place
-        static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); }
-        static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); }
-        static ItemFilter light() { return Builder().withTypeLight().build(); }
-        static ItemFilter background() { return Builder().withTypeBackground().build(); }
+        static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
+        static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
+        static Builder light() { return Builder().withTypeLight(); }
+        static Builder background() { return Builder().withViewSpace().withLayered(); }
     };
 
+    ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
+
     // Item Filter operator testing if a key pass the filter
     bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
 
@@ -179,7 +180,7 @@ public:
 };
 
 inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
-    debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape())
+    debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
                  << "]";
     return debug;
 }
@@ -214,38 +215,43 @@ public:
     public:
         virtual const ItemKey getKey() const = 0;
         virtual const Bound getBound() const = 0;
-        virtual void render(RenderArgs* args) = 0;
+        virtual int getLayer() const = 0;
 
-        virtual void update(const UpdateFunctorPointer& functor) = 0;
+        virtual void render(RenderArgs* args) = 0;
 
         virtual const model::MaterialKey getMaterialKey() const = 0;
 
         ~PayloadInterface() {}
     protected:
+        friend class Item;
+        virtual void update(const UpdateFunctorPointer& functor) = 0;
     };
-    
-
-    
     typedef std::shared_ptr<PayloadInterface> PayloadPointer;
 
-    
-
     Item() {}
     ~Item() {}
 
+    // Main scene / item managment interface Reset/Update/Kill
     void resetPayload(const PayloadPointer& payload);
-    void kill();
+    void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); } // Communicate update to the payload
+    void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key
 
     // Check heuristic key
     const ItemKey& getKey() const { return _key; }
 
     // Payload Interface
+
+    // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
     const Bound getBound() const { return _payload->getBound(); }
+
+    // Get the layer where the item belongs. 0 by default meaning NOT LAYERED
+    int getLayer() const { return _payload->getLayer(); }
+
+    // Render call for the item
     void render(RenderArgs* args) { _payload->render(args); }
-    void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); }
 
     // Shape Type Interface
-    const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); }
+    const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); }
 
 protected:
     PayloadPointer _payload;
@@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) {
 // of the Payload interface
 template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
 template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
+template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
 template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
     
 // Shape type interface
@@ -290,19 +297,25 @@ public:
     typedef std::shared_ptr<T> DataPointer;
     typedef UpdateFunctor<T> Updater;
 
-    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
+    Payload(const DataPointer& data) : _data(data) {}
 
     // Payload general interface
     virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
     virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
+    virtual int getLayer() const { return payloadGetLayer<T>(_data); }
+
+
     virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); } 
 
     // Shape Type interface
     virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); }
 
-    Payload(const DataPointer& data) : _data(data) {}
 protected:
     DataPointer _data;
+
+    // Update mechanics
+    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
+    friend class Item;
 };
 
 // Let's show how to make a simple FooPayload example:
@@ -358,6 +371,7 @@ public:
 
 typedef std::vector< ItemIDAndBounds > ItemIDsBounds;
 
+
 // A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
 class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
 public:
@@ -374,8 +388,6 @@ public:
 };
 
 class Engine;
-class Observer;
-
 
 class PendingChanges {
 public:
@@ -411,36 +423,6 @@ typedef std::queue<PendingChanges> PendingChangesQueue;
 // Items are notified accordingly on any update message happening
 class Scene {
 public:
- 
-    class Observer {
-    public:
-        Observer(Scene* scene) {
-            
-        }
-        ~Observer() {}
-
-        const Scene* getScene() const { return _scene; }
-        Scene* editScene() { return _scene; }
-
-    protected:
-        Scene* _scene = nullptr;
-        virtual void onRegisterScene() {}
-        virtual void onUnregisterScene() {}
-
-        friend class Scene;
-        void registerScene(Scene* scene) {
-            _scene = scene;
-            onRegisterScene();
-        }
-
-        void unregisterScene() {
-            onUnregisterScene();
-            _scene = 0;
-        }
-    };
-    typedef std::shared_ptr< Observer > ObserverPointer;
-    typedef std::vector< ObserverPointer > Observers;
-
     Scene();
     ~Scene() {}
 
@@ -450,11 +432,6 @@ public:
     /// Enqueue change batch to the scene
     void enqueuePendingChanges(const PendingChanges& pendingChanges);
 
-    /// Scene Observer listen to any change and get notified
-    void registerObserver(ObserverPointer& observer);
-    void unregisterObserver(ObserverPointer& observer);
-
-
     /// Access the main bucketmap of items
     const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
 
@@ -483,10 +460,6 @@ protected:
     void removeItems(const ItemIDs& ids);
     void updateItems(const ItemIDs& ids, UpdateFunctors& functors);
 
-
-    // The scene context listening for any change to the database
-    Observers _observers;
-
     friend class Engine;
 };
 
diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
index 11755add8e..43076039a9 100644
--- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h
+++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
@@ -52,6 +52,8 @@ class AbstractControllerScriptingInterface : public QObject {
     Q_OBJECT
 
 public slots:
+    virtual void registerControllerTypes(QScriptEngine* engine) = 0;
+    
     virtual bool isPrimaryButtonPressed() const = 0;
     virtual glm::vec2 getPrimaryJoystickPosition() const = 0;
 
diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h
index d74e1ed1e0..470d038196 100644
--- a/libraries/script-engine/src/AudioScriptingInterface.h
+++ b/libraries/script-engine/src/AudioScriptingInterface.h
@@ -39,6 +39,7 @@ signals:
     void mutedByMixer();
     void environmentMuted();
     void receivedFirstPacket();
+    void disconnected();
 
 private:
     AudioScriptingInterface();
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 30c3fbe8e4..182a0aea8d 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -317,6 +317,7 @@ void ScriptEngine::init() {
     registerAnimationTypes(this);
     registerAvatarTypes(this);
     registerAudioMetaTypes(this);
+    _controllerScriptingInterface->registerControllerTypes(this);
 
     qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
     qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
diff --git a/libraries/shared/src/PhysicsHelpers.h b/libraries/shared/src/PhysicsHelpers.h
index bef7275067..0e58ae99f0 100644
--- a/libraries/shared/src/PhysicsHelpers.h
+++ b/libraries/shared/src/PhysicsHelpers.h
@@ -15,6 +15,7 @@
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
 
+const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 4;
 const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
 
 // return incremental rotation (Bullet-style) caused by angularVelocity over timeStep
diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp
new file mode 100644
index 0000000000..38c746994d
--- /dev/null
+++ b/libraries/ui/src/CursorManager.cpp
@@ -0,0 +1,92 @@
+//
+//  Created by Bradley Austin Davis on 2015/06/08
+//  Copyright 2015 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 "CursorManager.h"
+
+#include <QCursor>
+#include <QWidget>
+#include <QUrl>
+
+#include <PathUtils.h>
+
+namespace Cursor {
+
+    void Instance::setIcon(uint16_t icon) {
+        _icon = icon;
+    }
+
+    uint16_t Instance::getIcon() const {
+        return _icon;
+    }
+
+
+    class MouseInstance : public Instance {
+        Source getType() const {
+            return Source::MOUSE;
+        }
+
+        ivec2 getScreenPosition() const {
+            return toGlm(QCursor::pos());
+        }
+
+        ivec2 getWindowPosition(QWidget* widget) const {
+            return toGlm(widget->mapFromGlobal(QCursor::pos()));
+        }
+
+        vec2 getRelativePosition(QWidget* widget) const {
+            vec2 pos = getWindowPosition(widget);
+            pos /= vec2(toGlm(widget->size()));
+            return pos;
+        }
+    };
+
+    static QMap<uint16_t, QString> ICONS;
+    static uint16_t _customIconId = Icon::USER_BASE;
+
+    Manager::Manager() {
+        ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png";
+        ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png";
+    }
+
+    Manager& Manager::instance() {
+        static Manager instance;
+        return instance;
+    }
+
+    uint8_t Manager::getCount() {
+        return 1;
+    }
+
+    Instance* Manager::getCursor(uint8_t index) {
+        Q_ASSERT(index < getCount());
+        static MouseInstance mouseInstance;
+        if (index == 0) {
+            return &mouseInstance;
+        }
+        return nullptr;
+    }
+
+    uint16_t Manager::registerIcon(const QString& path) {
+        ICONS[_customIconId] = path;
+        return _customIconId++;
+    }
+
+    const QString& Manager::getIconImage(uint16_t icon) {
+        Q_ASSERT(ICONS.count(icon));
+        return ICONS[icon];
+    }
+
+    float Manager::getScale() {
+        return _scale;
+    }
+
+    void Manager::setScale(float scale) {
+        _scale = scale;
+    }
+
+}
\ No newline at end of file
diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h
new file mode 100644
index 0000000000..c5810caf58
--- /dev/null
+++ b/libraries/ui/src/CursorManager.h
@@ -0,0 +1,61 @@
+//
+//  Created by Bradley Austin Davis on 2015/06/08
+//  Copyright 2015 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
+//
+
+#pragma once
+#include <stdint.h>
+
+#include <GLMHelpers.h>
+
+namespace Cursor {
+    enum class Source {
+        MOUSE,
+        LEFT_HAND,
+        RIGHT_HAND,
+        UNKNOWN,
+    };
+
+    enum Icon {
+        DEFAULT,
+        LINK,
+        GRAB,
+
+        // Add new system cursors here
+
+        // User cursors will have ids over this value
+        USER_BASE = 0xFF,
+    };
+
+    class Instance {
+    public:
+        virtual Source getType() const = 0;
+        virtual ivec2 getWindowPosition(QWidget* widget) const = 0;
+        virtual vec2 getRelativePosition(QWidget* widget) const = 0;
+        virtual ivec2 getScreenPosition() const = 0;
+        virtual void setIcon(uint16_t icon);
+        virtual uint16_t getIcon() const;
+    private:
+        uint16_t _icon;
+    };
+
+    class Manager {
+        Manager();
+        Manager(const Manager& other) = delete;
+    public:
+        static Manager& instance();
+        uint8_t getCount();
+        float getScale();
+        void setScale(float scale);
+        Instance* getCursor(uint8_t index = 0);
+        uint16_t registerIcon(const QString& path);
+        const QString& getIconImage(uint16_t icon);
+    private:
+        float _scale{ 1.0f };
+    };
+}
+
+