diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index d5eb12c36d..5001e4419d 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -38,7 +38,7 @@
 
 static const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
 static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f;    // attenuation = -6dB * log2(distance)
-static const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f;
+static const float DEFAULT_NOISE_MUTING_THRESHOLD = 1.0f;
 static const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
 static const QString AUDIO_ENV_GROUP_KEY = "audio_env";
 static const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
@@ -69,7 +69,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
     packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
     packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
     packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
-    packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); 
+    packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
     packetReceiver.registerListener(PacketType::PerAvatarGainSet, this, "handlePerAvatarGainSetDataPacket");
 
     connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 0084e51239..84fccdafe7 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -1089,9 +1089,9 @@
         {
           "name": "noise_muting_threshold",
           "label": "Noise Muting Threshold",
-          "help": "Loudness value for noise background between 0 and 1.0 (0: mute everyone, 1.0: never mute)",
-          "placeholder": "0.003",
-          "default": "0.003",
+          "help": "Loudness value for noise background between 0 and 1.0 (0: mute everyone, 1.0: never mute). 0.003 is a typical setting to mute loud people.",
+          "placeholder": "1.0",
+          "default": "1.0",
           "advanced": false
         },
         {
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 6061812d2a..7ab1e7e8bd 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -85,19 +85,6 @@ namespace render {
     }
 }
 
-static uint64_t timeProcessingJoints = 0;
-static int32_t numJointsProcessed = 0;
-
-float Avatar::getNumJointsProcessedPerSecond() {
-    float rate = 0.0f;
-    if (timeProcessingJoints > 0) {
-        rate = (float)(numJointsProcessed * USECS_PER_SECOND) / (float)timeProcessingJoints;
-    }
-    timeProcessingJoints = 0;
-    numJointsProcessed = 0;
-    return rate;
-}
-
 Avatar::Avatar(RigPointer rig) :
     AvatarData(),
     _skeletonOffset(0.0f),
@@ -127,6 +114,7 @@ Avatar::Avatar(RigPointer rig) :
     _nameRectGeometryID = geometryCache->allocateID();
     _leftPointerGeometryID = geometryCache->allocateID();
     _rightPointerGeometryID = geometryCache->allocateID();
+    _lastRenderUpdateTime = usecTimestampNow();
 }
 
 Avatar::~Avatar() {
@@ -187,25 +175,35 @@ AABox Avatar::getBounds() const {
 }
 
 void Avatar::animateScaleChanges(float deltaTime) {
-    float currentScale = getUniformScale();
-    auto desiredScale = getDomainLimitedScale();
-    if (currentScale != desiredScale) {
+    if (_isAnimatingScale) {
+        float currentScale = getUniformScale();
+        float desiredScale = getDomainLimitedScale();
+
         // use exponential decay toward the domain limit clamped scale
         const float SCALE_ANIMATION_TIMESCALE = 0.5f;
         float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);
         float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
 
         // snap to the end when we get close enough
-        const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
-        if (fabsf(desiredScale - currentScale) / desiredScale < MIN_RELATIVE_SCALE_ERROR) {
+        const float MIN_RELATIVE_ERROR = 0.03f;
+        float relativeError = fabsf(desiredScale - currentScale) / desiredScale;
+        if (relativeError < MIN_RELATIVE_ERROR) {
             animatedScale = desiredScale;
+            _isAnimatingScale = false;
         }
-
         setScale(glm::vec3(animatedScale)); // avatar scale is uniform
+
+        // TODO: rebuilding the shape constantly is somehwat expensive.
+        // We should only rebuild after significant change.
         rebuildCollisionShape();
     }
 }
 
+void Avatar::setTargetScale(float targetScale) {
+    AvatarData::setTargetScale(targetScale);
+    _isAnimatingScale = true;
+}
+
 void Avatar::updateAvatarEntities() {
     PerformanceTimer perfTimer("attachments");
     // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity()
@@ -302,63 +300,23 @@ void Avatar::updateAvatarEntities() {
     }
 }
 
-void Avatar::setShouldDie() {
-    // This will cause the avatar to be shrunk away and removed (the actual Avatar gets removed), but then it comes back.
-    _owningAvatarMixer.clear();
+bool Avatar::shouldDie() const {
+    const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
+    return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS;
 }
 
-void Avatar::simulate(float deltaTime) {
+void Avatar::simulate(float deltaTime, bool inView) {
+    PROFILE_RANGE(simulation, "simulate");
     PerformanceTimer perfTimer("simulate");
-
-    if (!isDead() && !_motionState) {
-        DependencyManager::get<AvatarManager>()->addAvatarToSimulation(this);
-    }
-    animateScaleChanges(deltaTime);
-
-    bool avatarInView = false;
-    { // update the shouldAnimate flag to match whether or not we will render the avatar.
-        PerformanceTimer perfTimer("cull");
-        {
-            // simple frustum check
-            PerformanceTimer perfTimer("inView");
-            ViewFrustum viewFrustum;
-            qApp->copyDisplayViewFrustum(viewFrustum);
-            avatarInView = viewFrustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())
-                || viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());
-        }
-        PerformanceTimer lodPerfTimer("LOD");
-        if (avatarInView) {
-            const float MINIMUM_VISIBILITY_FOR_ON = 0.4f;
-            const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f;
-            ViewFrustum viewFrustum;
-            qApp->copyViewFrustum(viewFrustum);
-            float visibility = calculateRenderAccuracy(viewFrustum.getPosition(),
-                getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());
-            if (!_shouldAnimate) {
-                if (visibility > MINIMUM_VISIBILITY_FOR_ON) {
-                    _shouldAnimate = true;
-                    qCDebug(interfaceapp) << "Restoring" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
-                }
-            } else if (visibility < MAXIMUM_VISIBILITY_FOR_OFF) {
-                _shouldAnimate = false;
-                qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
-            }
-        }
-    }
-
-    uint64_t start = usecTimestampNow();
-    // CRUFT? _shouldSkipRender is never set 'true'
-    if (_shouldAnimate && avatarInView && !_shouldSkipRender) {
-        {
-            PerformanceTimer perfTimer("skeleton");
+    {
+        PROFILE_RANGE(simulation, "updateJoints");
+        if (inView && _hasNewJointData) {
             _skeletonModel->getRig()->copyJointsFromJointData(_jointData);
-            _skeletonModel->simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);
+            _skeletonModel->simulate(deltaTime, true);
+
             locationChanged(); // joints changed, so if there are any children, update them.
-            _hasNewJointRotations = false;
-            _hasNewJointTranslations = false;
-        }
-        {
-            PerformanceTimer perfTimer("head");
+            _hasNewJointData = false;
+
             glm::vec3 headPosition = getPosition();
             if (!_skeletonModel->getHeadPosition(headPosition)) {
                 headPosition = getPosition();
@@ -366,16 +324,12 @@ void Avatar::simulate(float deltaTime) {
             Head* head = getHead();
             head->setPosition(headPosition);
             head->setScale(getUniformScale());
-            head->simulate(deltaTime, false, !_shouldAnimate);
+            head->simulate(deltaTime, false);
+        } else {
+            // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
+            _skeletonModel->simulate(deltaTime, false);
         }
-    } else {
-        // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
-        getHead()->setPosition(getPosition());
-        PerformanceTimer perfTimer("skeleton");
-        _skeletonModel->simulate(deltaTime, false);
     }
-    timeProcessingJoints += usecTimestampNow() - start;
-    numJointsProcessed += _jointData.size();
 
     // update animation for display name fade in/out
     if ( _displayNameTargetAlpha != _displayNameAlpha) {
@@ -394,11 +348,13 @@ void Avatar::simulate(float deltaTime) {
         _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
     }
 
-    measureMotionDerivatives(deltaTime);
-
-    simulateAttachments(deltaTime);
-    updatePalms();
-    updateAvatarEntities();
+    {
+        PROFILE_RANGE(simulation, "misc");
+        measureMotionDerivatives(deltaTime);
+        simulateAttachments(deltaTime);
+        updatePalms();
+        updateAvatarEntities();
+    }
 }
 
 bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
@@ -1106,7 +1062,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
     if (_moving && _motionState) {
         _motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
     }
-    if (_moving || _hasNewJointRotations || _hasNewJointTranslations) {
+    if (_moving || _hasNewJointData) {
         locationChanged();
     }
 
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index d370402865..8f2b0817c1 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -58,8 +58,6 @@ class Avatar : public AvatarData {
     Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
 
 public:
-    static float getNumJointsProcessedPerSecond();
-
     explicit Avatar(RigPointer rig = nullptr);
     ~Avatar();
 
@@ -68,7 +66,7 @@ public:
 
     void init();
     void updateAvatarEntities();
-    void simulate(float deltaTime);
+    void simulate(float deltaTime, bool inView);
     virtual void simulateAttachments(float deltaTime);
 
     virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
@@ -141,8 +139,6 @@ public:
 
     Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
 
-    Q_INVOKABLE bool getShouldRender() const { return !_shouldSkipRender; }
-
     /// Scales a world space position vector relative to the avatar position and scale
     /// \param vector position to be scaled. Will store the result
     void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
@@ -179,7 +175,12 @@ public:
     glm::vec3 getUncachedRightPalmPosition() const;
     glm::quat getUncachedRightPalmRotation() const;
 
-    Q_INVOKABLE void setShouldDie();
+    uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; }
+    void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; }
+
+    bool shouldDie() const;
+    void animateScaleChanges(float deltaTime);
+    void setTargetScale(float targetScale) override;
 
 public slots:
 
@@ -230,8 +231,6 @@ protected:
     // protected methods...
     bool isLookingAtMe(AvatarSharedPointer avatar) const;
 
-    virtual void animateScaleChanges(float deltaTime);
-
     glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
     glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
     glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
@@ -261,14 +260,14 @@ protected:
     void ensureInScene(AvatarSharedPointer self);
 
 private:
+    uint64_t _lastRenderUpdateTime { 0 };
     int _leftPointerGeometryID { 0 };
     int _rightPointerGeometryID { 0 };
     int _nameRectGeometryID { 0 };
     bool _initialized;
-    bool _shouldAnimate { true };
-    bool _shouldSkipRender { false };
     bool _isLookAtTarget { false };
     bool _inScene { false };
+    bool _isAnimatingScale { false };
 
     float getBoundingRadius() const;
 
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 6080b3e67b..df3164e6fc 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -137,53 +137,131 @@ float AvatarManager::getAvatarDataRate(const QUuid& sessionID, const QString& ra
     return avatar->getDataRate(rateName);
 }
 
+class AvatarPriority {
+public:
+    AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
+    AvatarSharedPointer avatar;
+    float priority;
+    // NOTE: we invert the less-than operator to sort high priorities to front
+    bool operator<(const AvatarPriority& other) const { return priority > other.priority; }
+};
+
 void AvatarManager::updateOtherAvatars(float deltaTime) {
     // lock the hash for read to check the size
     QReadLocker lock(&_hashLock);
-
     if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) {
         return;
     }
-
     lock.unlock();
 
-    bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
-    PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
-
     PerformanceTimer perfTimer("otherAvatars");
-    render::PendingChanges pendingChanges;
+    uint64_t startTime = usecTimestampNow();
 
-    // simulate avatars
-    auto hashCopy = getHashCopy();
+    auto avatarMap = getHashCopy();
+    QList<AvatarSharedPointer> avatarList = avatarMap.values();
+    ViewFrustum cameraView;
+    qApp->copyDisplayViewFrustum(cameraView);
+    glm::vec3 frustumCenter = cameraView.getPosition();
 
-    uint64_t start = usecTimestampNow();
-    AvatarHash::iterator avatarIterator = hashCopy.begin();
-    while (avatarIterator != hashCopy.end()) {
-        auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
+    const float OUT_OF_VIEW_PENALTY = -10.0;
 
-        if (avatar == _myAvatar || !avatar->isInitialized()) {
-            // DO NOT update _myAvatar!  Its update has already been done earlier in the main loop.
-            // DO NOT update or fade out uninitialized Avatars
-            ++avatarIterator;
-        } else if (avatar->shouldDie()) {
-            removeAvatar(avatarIterator.key());
-            ++avatarIterator;
-        } else {
-            avatar->ensureInScene(avatar);
-            avatar->simulate(deltaTime);
-            ++avatarIterator;
+    std::priority_queue<AvatarPriority> sortedAvatars;
+    {
+        PROFILE_RANGE(simulation, "sort");
+        for (int32_t i = 0; i < avatarList.size(); ++i) {
+            const auto& avatar = std::static_pointer_cast<Avatar>(avatarList.at(i));
+            if (avatar == _myAvatar || !avatar->isInitialized()) {
+                // DO NOT update _myAvatar!  Its update has already been done earlier in the main loop.
+                // DO NOT update or fade out uninitialized Avatars
+                continue;
+            }
+            if (avatar->shouldDie()) {
+                removeAvatar(avatar->getID());
+                continue;
+            }
+            if (avatar->isDead()) {
+                continue;
+            }
 
-            avatar->updateRenderItem(pendingChanges);
+            // priority = weighted linear combination of:
+            //   (a) apparentSize
+            //   (b) proximity to center of view
+            //   (c) time since last update
+            //   (d) TIME_PENALTY to help recently updated entries sort toward back
+            glm::vec3 avatarPosition = avatar->getPosition();
+            glm::vec3 offset = avatarPosition - frustumCenter;
+            float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
+            float radius = avatar->getBoundingRadius();
+            const glm::vec3& forward = cameraView.getDirection();
+            float apparentSize = radius / distance;
+            float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance;
+            const float TIME_PENALTY = 0.080f; // seconds
+            float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY;
+            // NOTE: we are adding values of different units to get a single measure of "priority".
+            // Thus we multiply each component by a conversion "weight" that scales its units
+            // relative to the others.  These weights are pure magic tuning and are hard coded in the
+            // relation below: (hint: unitary weights are not explicityly shown)
+            float priority = apparentSize + 0.25f * cosineAngle + age;
+
+            // decrement priority of avatars outside keyhole
+            if (distance > cameraView.getCenterRadius()) {
+                if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
+                    priority += OUT_OF_VIEW_PENALTY;
+                }
+            }
+            sortedAvatars.push(AvatarPriority(avatar, priority));
         }
     }
+
+    render::PendingChanges pendingChanges;
+    const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec
+    const uint64_t MAX_UPDATE_BUDGET = 2000; // usec
+    uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET;
+    uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET;
+    while (!sortedAvatars.empty()) {
+        const AvatarPriority& sortData = sortedAvatars.top();
+        const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
+
+        // for ALL avatars...
+        avatar->ensureInScene(avatar);
+        if (!avatar->getMotionState()) {
+            ShapeInfo shapeInfo;
+            avatar->computeShapeInfo(shapeInfo);
+            btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
+            if (shape) {
+                // don't add to the simulation now, instead put it on a list to be added later
+                AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape);
+                avatar->setMotionState(motionState);
+                _motionStatesToAddToPhysics.insert(motionState);
+                _motionStatesThatMightUpdate.insert(motionState);
+            }
+        }
+        avatar->animateScaleChanges(deltaTime);
+
+        uint64_t now = usecTimestampNow();
+        if (now < renderExpiry) {
+            // we're within budget
+            const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY;
+            bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
+            avatar->simulate(deltaTime, inView);
+            avatar->updateRenderItem(pendingChanges);
+            avatar->setLastRenderUpdateTime(startTime);
+        } else if (now < maxExpiry) {
+            // we've spent most of our time budget, but we still simulate() the avatar as it if were out of view
+            // --> some avatars may freeze until their priority trickles up
+            const bool inView = false;
+            avatar->simulate(deltaTime, inView);
+        } else {
+            // we've spent ALL of our time budget --> bail on the rest of the avatar updates
+            // --> some scale or fade animations may glitch
+            // --> some avatar velocity measurements may be a little off
+            break;
+        }
+        sortedAvatars.pop();
+    }
     qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges);
 
-    // simulate avatar fades
     simulateAvatarFades(deltaTime);
-
-    PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec",
-            { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } });
-    PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } });
 }
 
 void AvatarManager::postUpdate(float deltaTime) {
@@ -206,6 +284,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
     while (fadingIterator != _avatarFades.end()) {
         auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
         avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
+        avatar->animateScaleChanges(deltaTime);
         if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
             avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
             // only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
@@ -215,7 +294,8 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
                 ++fadingIterator;
             }
         } else {
-            avatar->simulate(deltaTime);
+            const bool inView = true; // HACK
+            avatar->simulate(deltaTime, inView);
             ++fadingIterator;
         }
     }
@@ -391,21 +471,6 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
     }
 }
 
-void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
-    assert(!avatar->getMotionState());
-
-    ShapeInfo shapeInfo;
-    avatar->computeShapeInfo(shapeInfo);
-    btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
-    if (shape) {
-        // we don't add to the simulation now, we put it on a list to be added later
-        AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
-        avatar->setMotionState(motionState);
-        _motionStatesToAddToPhysics.insert(motionState);
-        _motionStatesThatMightUpdate.insert(motionState);
-    }
-}
-
 void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
     if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
         for (auto avatarData : _avatarHash) {
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 193fa35ec5..787d6f2d83 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -69,8 +69,6 @@ public:
     void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
     void handleCollisionEvents(const CollisionEvents& collisionEvents);
 
-    void addAvatarToSimulation(Avatar* avatar);
-
     Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString(""));
     Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
                                                                   const QScriptValue& avatarIdsToInclude = QScriptValue(),
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index 928f46facb..d7bf2b79bf 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -70,7 +70,7 @@ void Head::reset() {
     _baseYaw = _basePitch = _baseRoll = 0.0f;
 }
 
-void Head::simulate(float deltaTime, bool isMine, bool billboard) {
+void Head::simulate(float deltaTime, bool isMine) {
     //  Update audio trailing average for rendering facial animations
     const float AUDIO_AVERAGING_SECS = 0.05f;
     const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
@@ -117,7 +117,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
         }
     }
    
-    if (!(_isFaceTrackerConnected || billboard)) {
+    if (!_isFaceTrackerConnected) {
 
         if (!_isEyeTrackerConnected) {
             // Update eye saccades
@@ -220,7 +220,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
     _leftEyePosition = _rightEyePosition = getPosition();
     _eyePosition = getPosition();
 
-    if (!billboard && _owningAvatar) {
+    if (_owningAvatar) {
         auto skeletonModel = static_cast<Avatar*>(_owningAvatar)->getSkeletonModel();
         if (skeletonModel) {
             skeletonModel->getEyePositions(_leftEyePosition, _rightEyePosition);
@@ -378,10 +378,6 @@ glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
     return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
 }
 
-glm::vec3 Head::getScalePivot() const {
-    return _position;
-}
-
 void Head::setFinalPitch(float finalPitch) {
     _deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch;
 }
diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h
index 29cd06865c..3d25c79087 100644
--- a/interface/src/avatar/Head.h
+++ b/interface/src/avatar/Head.h
@@ -31,7 +31,7 @@ public:
 
     void init();
     void reset();
-    void simulate(float deltaTime, bool isMine, bool billboard = false);
+    void simulate(float deltaTime, bool isMine);
     void setScale(float scale);
     void setPosition(glm::vec3 position) { _position = position; }
     void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
@@ -70,8 +70,6 @@ public:
 
     bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
     float getAverageLoudness() const { return _averageLoudness; }
-    /// \return the point about which scaling occurs.
-    glm::vec3 getScalePivot() const;
 
     void setDeltaPitch(float pitch) { _deltaPitch = pitch; }
     float getDeltaPitch() const { return _deltaPitch; }
diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp
index 54f6682191..4b77323bba 100644
--- a/interface/src/avatar/SkeletonModel.cpp
+++ b/interface/src/avatar/SkeletonModel.cpp
@@ -220,15 +220,19 @@ void SkeletonModel::updateAttitude() {
 // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
 // but just before head has been simulated.
 void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
-    updateAttitude();
-    setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
+    if (fullUpdate) {
+        updateAttitude();
+        setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
 
-    Model::simulate(deltaTime, fullUpdate);
+        Model::simulate(deltaTime, fullUpdate);
 
-    // let rig compute the model offset
-    glm::vec3 registrationPoint;
-    if (_rig->getModelRegistrationPoint(registrationPoint)) {
-        setOffset(registrationPoint);
+        // let rig compute the model offset
+        glm::vec3 registrationPoint;
+        if (_rig->getModelRegistrationPoint(registrationPoint)) {
+            setOffset(registrationPoint);
+        }
+    } else {
+        Model::simulate(deltaTime, fullUpdate);
     }
 
     if (!isActive() || !_owningAvatar->isMyAvatar()) {
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 7cfbfb174e..fbeddf41e0 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -253,6 +253,16 @@ int WindowScriptingInterface::createMessageBox(QString title, QString text, int
 void WindowScriptingInterface::updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton) {
     auto messageBox = _messageBoxes.value(id);
     if (messageBox) {
+        if (QThread::currentThread() != thread()) {
+            QMetaObject::invokeMethod(this, "updateMessageBox",
+                Q_ARG(int, id),
+                Q_ARG(QString, title),
+                Q_ARG(QString, text),
+                Q_ARG(int, buttons),
+                Q_ARG(int, defaultButton));
+            return;
+        }
+
         messageBox->setProperty("title", title);
         messageBox->setProperty("text", text);
         messageBox->setProperty("buttons", buttons);
@@ -263,6 +273,12 @@ void WindowScriptingInterface::updateMessageBox(int id, QString title, QString t
 void WindowScriptingInterface::closeMessageBox(int id) {
     auto messageBox = _messageBoxes.value(id);
     if (messageBox) {
+        if (QThread::currentThread() != thread()) {
+            QMetaObject::invokeMethod(this, "closeMessageBox",
+                Q_ARG(int, id));
+            return;
+        }
+
         disconnect(messageBox);
         messageBox->setVisible(false);
         messageBox->deleteLater();
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index a4cb62cc13..828e2e772d 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -157,7 +157,7 @@ void setupPreferences() {
     }
     {
         auto getter = [=]()->float { return myAvatar->getUniformScale(); };
-        auto setter = [=](float value) { myAvatar->setTargetScaleVerbose(value); }; // The hell?
+        auto setter = [=](float value) { myAvatar->setTargetScale(value); };
         auto preference = new SpinnerPreference(AVATAR_TUNING, "Avatar scale (default is 1.0)", getter, setter);
         preference->setMin(0.01f);
         preference->setMax(99.9f);
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index fdeeb01739..ac16b16c1d 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -1269,6 +1269,7 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
 
 void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
     PerformanceTimer perfTimer("copyJoints");
+    PROFILE_RANGE(simulation_animation_detail, "copyJoints");
     if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) {
         // make a vector of rotations in absolute-geometry-frame
         const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses();
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index eac21a9fe2..b726af3c3b 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -63,8 +63,7 @@ AvatarData::AvatarData() :
     _handState(0),
     _keyState(NO_KEY_DOWN),
     _forceFaceTrackerConnected(false),
-    _hasNewJointRotations(true),
-    _hasNewJointTranslations(true),
+    _hasNewJointData(true),
     _headData(NULL),
     _displayNameTargetAlpha(1.0f),
     _displayNameAlpha(1.0f),
@@ -134,11 +133,6 @@ void AvatarData::setTargetScale(float targetScale) {
     }
 }
 
-void AvatarData::setTargetScaleVerbose(float targetScale) {
-    setTargetScale(targetScale);
-    qCDebug(avatars) << "Changed scale to " << _targetScale;
-}
-
 glm::vec3 AvatarData::getHandPosition() const {
     return getOrientation() * _handPosition + getPosition();
 }
@@ -712,8 +706,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
         glm::quat newOrientation;
         sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, newOrientation);
         glm::quat currentOrientation = getLocalOrientation();
+
         if (currentOrientation != newOrientation) {
-            _hasNewJointRotations = true;
+            _hasNewJointData = true;
             setLocalOrientation(newOrientation);
         }
         int numBytesRead = sourceBuffer - startSection;
@@ -978,7 +973,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
             JointData& data = _jointData[i];
             if (validTranslations[i]) {
                 sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
-                _hasNewJointTranslations = true;
+                _hasNewJointData = true;
                 data.translationSet = true;
             }
         }
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index c56660ca06..fc4cc78447 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -85,7 +85,6 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
 const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
 
 
-
 // Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of
 // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain
 // backward compatibility. The bits are ordered as such (0-7 left to right).
@@ -405,8 +404,7 @@ public:
 
     //  Scale
     float getTargetScale() const;
-    void setTargetScale(float targetScale);
-    void setTargetScaleVerbose(float targetScale);
+    virtual void setTargetScale(float targetScale);
 
     float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
 
@@ -513,8 +511,6 @@ public:
 
     const glm::vec3& getTargetVelocity() const { return _targetVelocity; }
 
-    bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
-
     void clearRecordingBasis();
     TransformPointer getRecordingBasis() const;
     void setRecordingBasis(TransformPointer recordingBasis = TransformPointer());
@@ -600,8 +596,7 @@ protected:
     KeyState _keyState;
 
     bool _forceFaceTrackerConnected;
-    bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar
-    bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
+    bool _hasNewJointData; // set in AvatarData, cleared in Avatar
 
     HeadData* _headData;
 
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
index c338d6e641..2242bba5d9 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
@@ -48,39 +48,18 @@ public:
             void update();
             uvec3 getPageCounts(const uvec3& dimensions) const;
             uint32_t getPageCount(const uvec3& dimensions) const;
+            uint32_t getSize() const;
 
             GL45Texture& texture;
             bool sparse { false };
             uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION };
             GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL };
+            uint32_t allocatedPages { 0 };
             uint32_t maxPages { 0 };
             uint32_t pageBytes { 0 };
             GLint pageDimensionsIndex { 0 };
         };
 
-#if INCREMENTAL_TRANSFER
-        struct TransferState {
-            TransferState(GL45Texture& texture);
-            uvec3 currentPageSize() const;
-            void updateMip();
-            void populatePage(std::vector<uint8_t>& dest);
-            bool increment();
-
-            GL45Texture& texture;
-            GLTexelFormat texelFormat;
-            uint8_t face { 0 };
-            uint16_t mipLevel { 0 };
-            uint32_t bytesPerLine { 0 };
-            uint32_t bytesPerPixel { 0 };
-            uint32_t bytesPerPage { 0 };
-            uvec3 mipDimensions;
-            uvec3 mipOffset;
-            const uint8_t* srcPointer { nullptr };
-        };
-    protected:
-        TransferState _transferState;
-#endif
-
     protected:
         void updateMips() override;
         void stripToMip(uint16_t newMinMip);
@@ -98,8 +77,6 @@ public:
         void derez();
 
         SparseInfo _sparseInfo;
-        uint32_t _allocatedPages { 0 };
-        uint32_t _lastMipAllocatedPages { 0 };
         uint16_t _mipOffset { 0 };
         friend class GL45Backend;
     };
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
index 9b16908244..6632bf936e 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
@@ -116,6 +116,8 @@ void SparseInfo::maybeMakeSparse() {
     }
 }
 
+#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
+
 // This can only be called after we've established our storage size
 void SparseInfo::update() {
     if (!sparse) {
@@ -124,6 +126,9 @@ void SparseInfo::update() {
     glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel);
     pageBytes = texture._gpuObject.getTexelFormat().getSize();
     pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z;
+    // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for 
+    // sparse textures as compared to non-sparse, so we acount for that here.
+    pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE);
 
     for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) {
         auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
@@ -146,6 +151,11 @@ uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const {
     return pageCounts.x * pageCounts.y * pageCounts.z;
 }
 
+
+uint32_t SparseInfo::getSize() const {
+    return allocatedPages * pageBytes;
+}
+
 void GL45Backend::initTextureManagementStage() {
     // enable the Sparse Texture on gl45
     _textureManagement._sparseCapable = true;
@@ -160,93 +170,6 @@ void GL45Backend::initTextureManagementStage() {
     }
 }
 
-#if INCREMENTAL_TRANSFER
-
-using TransferState = GL45Backend::GL45Texture::TransferState;
-
-TransferState::TransferState(GL45Texture& texture) : texture(texture) {
-}
-
-void TransferState::updateMip() {
-    mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
-    mipOffset = uvec3();
-    if (!texture._gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
-        srcPointer = nullptr;
-        return;
-    }
-
-    auto mip = texture._gpuObject.accessStoredMipFace(mipLevel, face);
-    texelFormat = gl::GLTexelFormat::evalGLTexelFormat(texture._gpuObject.getTexelFormat(), mip->getFormat());
-    srcPointer = mip->readData();
-    bytesPerLine = (uint32_t)mip->getSize() / mipDimensions.y;
-    bytesPerPixel = bytesPerLine / mipDimensions.x;
-}
-
-bool TransferState::increment() {
-    const SparseInfo& sparse = texture._sparseInfo;
-    if ((mipOffset.x + sparse.pageDimensions.x) < mipDimensions.x) {
-        mipOffset.x += sparse.pageDimensions.x;
-        return true;
-    }
-
-    if ((mipOffset.y + sparse.pageDimensions.y) < mipDimensions.y) {
-        mipOffset.x = 0;
-        mipOffset.y += sparse.pageDimensions.y;
-        return true;
-    }
-
-    if (mipOffset.z + sparse.pageDimensions.z < mipDimensions.z) {
-        mipOffset.x = 0;
-        mipOffset.y = 0;
-        ++mipOffset.z;
-        return true;
-    }
-
-    // Done with this mip?, move on to the next mip 
-    if (mipLevel + 1 < texture.usedMipLevels()) {
-        mipOffset = uvec3(0);
-        ++mipLevel;
-        updateMip();
-        return true;
-    }
-
-    uint8_t maxFace = (uint8_t)((texture._target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
-    uint8_t nextFace = face + 1;
-    // Done with this face?  Move on to the next
-    if (nextFace < maxFace) {
-        ++face;
-        mipOffset = uvec3(0);
-        mipLevel = 0;
-        updateMip();
-        return true;
-    }
-
-    return false;
-}
-
-void TransferState::populatePage(std::vector<uint8_t>& buffer) {
-    uvec3 pageSize = currentPageSize();
-    auto bytesPerPageLine = bytesPerPixel * pageSize.x;
-    if (0 != (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT)) {
-        bytesPerPageLine += DEFAULT_GL_PIXEL_ALIGNMENT - (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT);
-        assert(0 == (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT));
-    }
-    auto totalPageSize = bytesPerPageLine * pageSize.y;
-    if (totalPageSize > buffer.size()) {
-        buffer.resize(totalPageSize);
-    }
-    uint8_t* dst = &buffer[0];
-    for (uint32_t y = 0; y < pageSize.y; ++y) {
-        uint32_t srcOffset = (bytesPerLine * (mipOffset.y + y)) + (bytesPerPixel * mipOffset.x);
-        uint32_t dstOffset = bytesPerPageLine * y;
-        memcpy(dst + dstOffset, srcPointer + srcOffset, pageSize.x * bytesPerPixel);
-    }
-}
-
-uvec3 TransferState::currentPageSize() const {
-    return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions);
-}
-#endif
 
 GLuint GL45Texture::allocate(const Texture& texture) {
     GLuint result;
@@ -260,17 +183,11 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
 
 GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
     : GLTexture(backend, texture, externalId), _sparseInfo(*this) 
-#if INCREMENTAL_TRANSFER
-, _transferState(*this)    
-#endif
 {
 }
 
 GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
     : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this) 
-#if INCREMENTAL_TRANSFER
-, _transferState(*this)    
-#endif
     {
 
     auto theBackend = _backend.lock();
@@ -316,12 +233,12 @@ GL45Texture::~GL45Texture() {
             });
 
             auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
-            assert(deallocatedPages <= _allocatedPages);
-            _allocatedPages -= deallocatedPages;
+            assert(deallocatedPages <= _sparseInfo.allocatedPages);
+            _sparseInfo.allocatedPages -= deallocatedPages;
         }
 
-        if (0 != _allocatedPages) {
-            qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages;
+        if (0 != _sparseInfo.allocatedPages) {
+            qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages;
         }
 
         auto size = _size;
@@ -365,9 +282,9 @@ void GL45Texture::updateSize() const {
     }
 
     if (_transferrable && _sparseInfo.sparse) {
-        auto size = _allocatedPages * _sparseInfo.pageBytes;
+        auto size = _sparseInfo.getSize();
         Backend::updateTextureGPUSparseMemoryUsage(_size, size);
-        setSize(_allocatedPages * _sparseInfo.pageBytes);
+        setSize(size);
     } else {
         setSize(_gpuObject.evalTotalSize(_mipOffset));
     }
@@ -376,20 +293,16 @@ void GL45Texture::updateSize() const {
 void GL45Texture::startTransfer() {
     Parent::startTransfer();
     _sparseInfo.update();
-#if INCREMENTAL_TRANSFER
-    _transferState.updateMip();
-#endif
 }
 
 bool GL45Texture::continueTransfer() {
-#if !INCREMENTAL_TRANSFER
     size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
     for (uint8_t face = 0; face < maxFace; ++face) {
         for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
             auto size = _gpuObject.evalMipDimensions(mipLevel);
             if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
                 glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
-                _allocatedPages += _sparseInfo.getPageCount(size);
+                _sparseInfo.allocatedPages += _sparseInfo.getPageCount(size);
             }
             if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
                 auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
@@ -413,58 +326,6 @@ bool GL45Texture::continueTransfer() {
         }
     }
     return false;
-#else
-    static std::vector<uint8_t> buffer;
-    if (buffer.empty()) {
-        buffer.resize(DEFAULT_PAGE_BUFFER_SIZE);
-    }
-    const uvec3 pageSize = _transferState.currentPageSize();
-    const uvec3& offset = _transferState.mipOffset;
-
-    if (_sparseInfo.sparse && _transferState.mipLevel <= _sparseInfo.maxSparseLevel) {
-        if (_allocatedPages > _sparseInfo.maxPages) {
-            qCWarning(gpugl45logging) << "Exceeded max page allocation!";
-        }
-        glTexturePageCommitmentEXT(_id, _transferState.mipLevel,
-            offset.x, offset.y, _transferState.face,
-            pageSize.x, pageSize.y, pageSize.z,
-            GL_TRUE);
-        ++_allocatedPages;
-    }
-
-    if (_transferState.srcPointer) {
-        // Transfer the mip data
-        _transferState.populatePage(buffer);
-        if (GL_TEXTURE_2D == _target) {
-            glTextureSubImage2D(_id, _transferState.mipLevel,
-                offset.x, offset.y,
-                pageSize.x, pageSize.y,
-                _transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]);
-        } else if (GL_TEXTURE_CUBE_MAP == _target) {
-            auto target = CUBE_FACE_LAYOUT[_transferState.face];
-            // DSA ARB does not work on AMD, so use EXT
-            // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
-            glTextureSubImage2DEXT(_id, target, _transferState.mipLevel,
-                offset.x, offset.y,
-                pageSize.x, pageSize.y,
-                _transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]);
-        }
-    }
-
-    serverWait();
-    auto currentMip = _transferState.mipLevel;
-    auto result = _transferState.increment();
-    if (_sparseInfo.sparse && _transferState.mipLevel != currentMip && currentMip <= _sparseInfo.maxSparseLevel) {
-        auto mipDimensions = _gpuObject.evalMipDimensions(currentMip);
-        auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions);
-        auto newPages = _allocatedPages - _lastMipAllocatedPages;
-        if (newPages != mipExpectedPages) {
-            qCWarning(gpugl45logging) << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages;
-        }
-        _lastMipAllocatedPages = _allocatedPages;
-    }
-    return result;
-#endif
 }
 
 void GL45Texture::finishTransfer() {
@@ -545,8 +406,8 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
             });
 
             auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
-            assert(deallocatedPages < _allocatedPages);
-            _allocatedPages -= deallocatedPages;
+            assert(deallocatedPages < _sparseInfo.allocatedPages);
+            _sparseInfo.allocatedPages -= deallocatedPages;
         }
         _minMip = newMinMip;
     } else {
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index b79e69a9b7..1e83a874dc 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -272,8 +272,6 @@ void Model::reset() {
 }
 
 bool Model::updateGeometry() {
-    PROFILE_RANGE(render_detail, __FUNCTION__);
-    PerformanceTimer perfTimer("Model::updateGeometry");
     bool needFullUpdate = false;
 
     if (!isLoaded()) {
@@ -1128,7 +1126,9 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
         if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) {
             snapToRegistrationPoint();
         }
-        simulateInternal(deltaTime);
+        // update the world space transforms for all joints
+        glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
+        updateRig(deltaTime, parentTransform);
     }
 }
 
@@ -1138,12 +1138,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
     _rig->updateAnimations(deltaTime, parentTransform);
 }
 
-void Model::simulateInternal(float deltaTime) {
-    // update the world space transforms for all joints
-    glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
-    updateRig(deltaTime, parentTransform);
-}
-
 // virtual
 void Model::updateClusterMatrices() {
     PerformanceTimer perfTimer("Model::updateClusterMatrices");
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 246d67180e..3f673b0250 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -312,7 +312,6 @@ protected:
     void scaleToFit();
     void snapToRegistrationPoint();
 
-    void simulateInternal(float deltaTime);
     virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
 
     /// Restores the indexed joint to its default position.
diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h
index 3f3de89fce..6f56a108bd 100644
--- a/libraries/shared/src/SpatiallyNestable.h
+++ b/libraries/shared/src/SpatiallyNestable.h
@@ -26,6 +26,8 @@ using SpatiallyNestableWeakConstPointer = std::weak_ptr<const SpatiallyNestable>
 using SpatiallyNestablePointer = std::shared_ptr<SpatiallyNestable>;
 using SpatiallyNestableConstPointer = std::shared_ptr<const SpatiallyNestable>;
 
+static const uint16_t INVALID_JOINT_INDEX = -1;
+
 enum class NestableType {
     Entity,
     Avatar,
@@ -184,7 +186,7 @@ protected:
     const NestableType _nestableType; // EntityItem or an AvatarData
     QUuid _id;
     QUuid _parentID; // what is this thing's transform relative to?
-    quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to?
+    quint16 _parentJointIndex { INVALID_JOINT_INDEX }; // which joint of the parent is this relative to?
 
     mutable SpatiallyNestableWeakPointer _parent;
 
diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp
index b4ed462639..a0b7d17e46 100644
--- a/libraries/shared/src/ViewFrustum.cpp
+++ b/libraries/shared/src/ViewFrustum.cpp
@@ -729,7 +729,7 @@ void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const {
 glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar) const {
 
     // make sure range near far make sense
-    assert(rangeNear > 0.0);
+    assert(rangeNear > 0.0f);
     assert(rangeFar > rangeNear);
 
     // recreate a projection matrix for only a range of depth of this frustum.
@@ -738,7 +738,7 @@ glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar
     glm::mat4 rangeProj = _projection;
     
     float A = -(rangeFar + rangeNear) / (rangeFar - rangeNear);
-    float B = -2 * rangeFar*rangeNear / ((rangeFar - rangeNear));
+    float B = -2.0f * rangeFar*rangeNear / ((rangeFar - rangeNear));
 
     rangeProj[2][2] = A;
     rangeProj[3][2] = B;
diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js
index d5f0f4cb06..10970a7749 100644
--- a/scripts/system/html/js/marketplacesInject.js
+++ b/scripts/system/html/js/marketplacesInject.js
@@ -24,7 +24,7 @@
 
     var canWriteAssets = false;
     var xmlHttpRequest = null;
-    var isDownloading = false;  // Explicitly track download request status.
+    var isPreparing = false;  // Explicitly track download request status.
 
     function injectCommonCode(isDirectoryPage) {
 
@@ -139,7 +139,7 @@
             function startAutoDownload() {
 
                 // One file request at a time.
-                if (isDownloading) {
+                if (isPreparing) {
                     console.log("WARNIKNG: Clara.io FBX: Prepare only one download at a time");
                     return;
                 }
@@ -178,7 +178,7 @@
                     var message = this.responseText.slice(responseTextIndex);
                     var statusMessage = "";
 
-                    if (isDownloading) {  // Ignore messages in flight after finished/cancelled.
+                    if (isPreparing) {  // Ignore messages in flight after finished/cancelled.
                         var lines = message.split(/[\n\r]+/);
 
                         for (var i = 0, length = lines.length; i < length; i++) {
@@ -222,33 +222,30 @@
                 xmlHttpRequest.onload = function () {
                     var statusMessage = "";
 
-                    if (!isDownloading) {
+                    if (!isPreparing) {
                         return;
                     }
 
+                    isPreparing = false;
+
                     var HTTP_OK = 200;
                     if (this.status !== HTTP_OK) {
                         statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText;
                         console.log("ERROR: Clara.io FBX: " + statusMessage);
                         EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage);
-                        return;
-                    }
-
-                    if (zipFileURL.slice(-4) !== ".zip") {
+                    } else if (zipFileURL.slice(-4) !== ".zip") {
                         statusMessage = "Error creating zip file for download.";
                         console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL);
                         EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage);
-                        return;
+                    } else {
+                        EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL);
+                        console.log("Clara.io FBX: File download initiated for " + zipFileURL);
                     }
 
-                    EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL);
-                    console.log("Clara.io FBX: File download initiated for " + zipFileURL);
-
                     xmlHttpRequest = null;
-                    isDownloading = false;
                 }
 
-                isDownloading = true;
+                isPreparing = true;
 
                 console.log("Clara.io FBX: Request zip file for " + uuid);
                 EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download");
@@ -301,7 +298,7 @@
     }
 
     function cancelClaraDownload() {
-        isDownloading = false;
+        isPreparing = false;
 
         if (xmlHttpRequest) {
             xmlHttpRequest.abort();