diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 6fef47da8e..b5a938faba 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -5313,3 +5313,15 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
     }
 }
 
+void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const {
+    auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
+    EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
+    if (entityTree) {
+        entityTree->withWriteLock([&] {
+            // force an update packet
+            EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
+            packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityID, properties);
+        });
+    }
+}
+
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 47ace7ba46..984d7b297b 100755
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -1884,6 +1884,7 @@ private:
     bool didTeleport();
     bool getIsAway() const { return _isAway; }
     void setAway(bool value);
+    void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const override;
 
     std::mutex _pinnedJointsMutex;
     std::vector<int> _pinnedJoints;
diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp
index a909a98456..40c7c01b30 100755
--- a/interface/src/avatar/OtherAvatar.cpp
+++ b/interface/src/avatar/OtherAvatar.cpp
@@ -528,6 +528,11 @@ void OtherAvatar::handleChangedAvatarEntityData() {
                 }
             }
             stateItr.value().success = success;
+            if (success) {
+                stateItr.value().hash = newHash;
+            } else {
+                stateItr.value().hash = 0;
+            }
         }
 
         AvatarEntityIDs recentlyRemovedAvatarEntities = getAndClearRecentlyRemovedIDs();
diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h
index 34a36dd235..696e122b30 100644
--- a/interface/src/avatar/OtherAvatar.h
+++ b/interface/src/avatar/OtherAvatar.h
@@ -74,6 +74,16 @@ protected:
     void onAddAttachedAvatarEntity(const QUuid& id);
     void onRemoveAttachedAvatarEntity(const QUuid& id);
 
+    class AvatarEntityDataHash {
+    public:
+        AvatarEntityDataHash(uint32_t h) : hash(h) {};
+        uint32_t hash { 0 };
+        bool success { false };
+    };
+
+    using MapOfAvatarEntityDataHashes = QMap<QUuid, AvatarEntityDataHash>;
+    MapOfAvatarEntityDataHashes _avatarEntityDataHashes;
+
     std::vector<QUuid> _attachedAvatarEntities;
     QUuid _otherAvatarOrbMeshPlaceholderID;
     AvatarMotionState* _motionState { nullptr };
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index daef0e411a..e6881b0efe 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -305,11 +305,6 @@ void Avatar::setTargetScale(float targetScale) {
     }
 }
 
-void Avatar::setAvatarEntityDataChanged(bool value) {
-    AvatarData::setAvatarEntityDataChanged(value);
-    _avatarEntityDataHashes.clear();
-}
-
 void Avatar::removeAvatarEntitiesFromTree() {
     auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
     EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
@@ -368,6 +363,13 @@ bool Avatar::applyGrabChanges() {
                 target->removeGrab(grab);
                 _avatarGrabs.erase(itr);
                 grabAddedOrRemoved = true;
+                if (isMyAvatar()) {
+                    const EntityItemPointer& entity = std::dynamic_pointer_cast<EntityItem>(target);
+                    if (entity && entity->getEntityHostType() == entity::HostType::AVATAR && entity->getSimulationOwner().getID() == getID()) {
+                        EntityItemProperties properties = entity->getProperties();
+                        sendPacket(entity->getID(), properties);
+                    }
+                }
             } else {
                 undeleted.push_back(id);
             }
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
index b43fe012b7..06942a13d8 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
@@ -25,6 +25,7 @@
 #include <render/Scene.h>
 #include <graphics-scripting/Forward.h>
 #include <GLMHelpers.h>
+#include <EntityItem.h>
 
 #include <Grab.h>
 #include <ThreadSafeValueCache.h>
@@ -480,8 +481,6 @@ public:
     virtual void setModelScale(float scale) { _modelScale = scale; }
     virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getModelScale()); }
 
-    virtual void setAvatarEntityDataChanged(bool value) override;
-
     // Show hide the model representation of the avatar
     virtual void setEnableMeshVisible(bool isEnabled);
     virtual bool getEnableMeshVisible() const;
@@ -603,6 +602,7 @@ protected:
 
     // protected methods...
     bool isLookingAtMe(AvatarSharedPointer avatar) const;
+    virtual void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { }
     bool applyGrabChanges();
     void relayJointDataToChildren();
 
@@ -647,9 +647,6 @@ protected:
         bool success { false };
     };
 
-    using MapOfAvatarEntityDataHashes = QMap<QUuid, AvatarEntityDataHash>;
-    MapOfAvatarEntityDataHashes _avatarEntityDataHashes;
-
     uint64_t _lastRenderUpdateTime { 0 };
     int _leftPointerGeometryID { 0 };
     int _rightPointerGeometryID { 0 };
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index d071b74d6e..63396a59ac 100755
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -1147,7 +1147,7 @@ public:
      */
     Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
 
-    virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
+    void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
     AvatarEntityIDs getAndClearRecentlyRemovedIDs();
 
     /**jsdoc
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 7e5be384a7..6df7935b72 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -2170,6 +2170,12 @@ void EntityItem::enableNoBootstrap() {
     if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
         _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
         _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
+
+        // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here
+        // because most enableNoBootstrap() cases are already correctly handled outside this scope
+        // and I didn't want to add redundant work.
+        // TODO: cleanup Grabs & dirtySimulationFlags to be more efficient and make more sense.
+
         forEachDescendant([&](SpatiallyNestablePointer child) {
             if (child->getNestableType() == NestableType::Entity) {
                 EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
@@ -2184,11 +2190,19 @@ void EntityItem::disableNoBootstrap() {
     if (!stillHasGrabActions()) {
         _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
         _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
+
+        EntityTreePointer entityTree = getTree();
+        assert(entityTree);
+        EntitySimulationPointer simulation = entityTree->getSimulation();
+        assert(simulation);
+        simulation->changeEntity(getThisPointer());
+
         forEachDescendant([&](SpatiallyNestablePointer child) {
             if (child->getNestableType() == NestableType::Entity) {
                 EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
                 entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
                 entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
+                simulation->changeEntity(entity);
             }
         });
     }
@@ -3438,7 +3452,7 @@ void EntityItem::addGrab(GrabPointer grab) {
     if (useAction) {
         EntityTreePointer entityTree = getTree();
         assert(entityTree);
-        EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
+        EntitySimulationPointer simulation = entityTree->getSimulation();
         assert(simulation);
 
         auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
@@ -3487,7 +3501,6 @@ void EntityItem::removeGrab(GrabPointer grab) {
         setLocalVelocity(glm::vec3(0.0f));
         setAngularVelocity(glm::vec3(0.0f));
     }
-    markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
 
     QUuid actionID = grab->getActionID();
     if (!actionID.isNull()) {