diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp
index c9ded2d6fb..1f3f770867 100644
--- a/assignment-client/src/avatars/ScriptableAvatar.cpp
+++ b/assignment-client/src/avatars/ScriptableAvatar.cpp
@@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) {
                 AnimPose& absPose = absPoses[i];
                 if (data.rotation != absPose.rot()) {
                     data.rotation = absPose.rot();
-                    data.rotationSet = true;
+                    data.rotationIsDefaultPose = false;
                 }
                 AnimPose& relPose = poses[i];
                 if (data.translation != relPose.trans()) {
                     data.translation = relPose.trans();
-                    data.translationSet = true;
+                    data.translationIsDefaultPose = false;
                 }
             }
 
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 11496f727e..e93b897013 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -2019,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
         _smoothOrientationTimer = 0.0f;
     }
 
-    getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
-
+    Head* head = getHead();
     auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
     if (headPose.isValid()) {
         glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
@@ -2032,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
         head->setBaseYaw(YAW(euler));
         head->setBasePitch(PITCH(euler));
         head->setBaseRoll(ROLL(euler));
+    } else {
+        head->setBaseYaw(0.0f);
+        head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
+        head->setBaseRoll(0.0f);
     }
 }
 
diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp
index d29ee93e62..4804a2db3d 100644
--- a/interface/src/ui/overlays/ModelOverlay.cpp
+++ b/interface/src/ui/overlays/ModelOverlay.cpp
@@ -564,9 +564,9 @@ void ModelOverlay::animate() {
                 rotationMat * fbxJoints[index].postTransform);
             auto& jointData = jointsData[j];
             jointData.translation = extractTranslation(finalMat);
-            jointData.translationSet = true;
+            jointData.translationIsDefaultPose = false;
             jointData.rotation = glmExtractRotation(finalMat);
-            jointData.rotationSet = true;
+            jointData.rotationIsDefaultPose = false;
         }
     }
     // Set the data in the model
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index 1c9d9a3447..b1b41775a8 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -1705,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
             // rotations are in absolute rig frame.
             glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot();
             data.rotation = _internalPoseSet._absolutePoses[i].rot();
-            data.rotationSet = !isEqual(data.rotation, defaultAbsRot);
+            data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot);
 
             // translations are in relative frame but scaled so that they are in meters,
             // instead of geometry units.
             glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
             data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans();
-            data.translationSet = !isEqual(data.translation, defaultRelTrans);
+            data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans);
         } else {
-            data.translationSet = false;
-            data.rotationSet = false;
+            data.translationIsDefaultPose = true;
+            data.rotationIsDefaultPose = true;
         }
     }
 }
@@ -1739,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
     const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform));
     for (int i = 0; i < numJoints; i++) {
         const JointData& data = jointDataVec.at(i);
-        if (data.rotationSet) {
+        if (data.rotationIsDefaultPose) {
+            rotations.push_back(absoluteDefaultPoses[i].rot());
+        } else {
             // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame
             rotations.push_back(rigToGeometryRot * data.rotation);
-        } else {
-            rotations.push_back(absoluteDefaultPoses[i].rot());
         }
     }
 
@@ -1759,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
         const JointData& data = jointDataVec.at(i);
         _internalPoseSet._relativePoses[i].scale() = Vectors::ONE;
         _internalPoseSet._relativePoses[i].rot() = rotations[i];
-        if (data.translationSet) {
+        if (data.translationIsDefaultPose) {
+            _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
+        } else {
             // JointData translations are in scaled relative-frame so we scale back to regular relative-frame
             _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation;
-        } else {
-            _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
         }
     }
 }
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 94df5bf7f4..9924cc137e 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -39,6 +39,7 @@
 #include <AudioHelpers.h>
 #include <Profile.h>
 #include <VariantMapToScriptValue.h>
+#include <BitVectorHelpers.h>
 
 #include "AvatarLogging.h"
 
@@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
     return totalSize;
 }
 
+size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) {
+    const size_t bitVectorSize = calcBitVectorSize((int)numJoints);
+    size_t totalSize = sizeof(uint8_t); // numJoints
+
+    // one set of bits for rotation and one for translation
+    const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2;
+    totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize;
+
+    return totalSize;
+}
 
 AvatarData::AvatarData() :
     SpatiallyNestable(NestableType::Avatar, QUuid()),
@@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
     bool hasFaceTrackerInfo = false;
     bool hasJointData = false;
+    bool hasJointDefaultPoseFlags = false;
 
     if (sendPALMinimum) {
         hasAudioLoudness = true;
@@ -290,6 +302,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
         hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
         hasJointData = sendAll || !sendMinimum;
+        hasJointDefaultPoseFlags = hasJointData;
     }
 
 
@@ -314,7 +327,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
         | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
         | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
         | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
-        | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0);
+        | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0)
+        | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0);
 
     memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
     destinationBuffer += sizeof(packetStateFlags);
@@ -541,14 +555,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
         for (int i = 0; i < _jointData.size(); i++) {
             const JointData& data = _jointData[i];
+            const JointData& last = lastSentJointData[i];
 
-            // The dot product for smaller rotations is a smaller number.
-            // So if the dot() is less than the value, then the rotation is a larger angle of rotation
-            bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT;
+            if (!data.rotationIsDefaultPose) {
+                if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) {
 
-            if (sendAll || lastSentJointData[i].rotation != data.rotation) {
-                if (sendAll || !cullSmallChanges || largeEnoughRotation) {
-                    if (data.rotationSet) {
+                    bool largeEnoughRotation = true;
+                    if (cullSmallChanges) {
+                        // The dot product for smaller rotations is a smaller number.
+                        // So if the dot() is less than the value, then the rotation is a larger angle of rotation
+                        largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
+                    }
+
+                    if (sendAll || !cullSmallChanges || largeEnoughRotation) {
                         validity |= (1 << validityBit);
 #ifdef WANT_DEBUG
                         rotationSentCount++;
@@ -557,8 +576,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
                         if (sentJointDataOut) {
                             localSentJointDataOut[i].rotation = data.rotation;
+                            localSentJointDataOut[i].rotationIsDefaultPose = false;
                         }
-
                     }
                 }
             }
@@ -588,11 +607,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
         float maxTranslationDimension = 0.0;
         for (int i = 0; i < _jointData.size(); i++) {
             const JointData& data = _jointData[i];
-            if (sendAll || lastSentJointData[i].translation != data.translation) {
-                if (sendAll ||
-                    !cullSmallChanges ||
-                    glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
-                    if (data.translationSet) {
+
+            if (!data.translationIsDefaultPose) {
+                if (sendAll || lastSentJointData[i].translation != data.translation) {
+                    if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
                         validity |= (1 << validityBit);
 #ifdef WANT_DEBUG
                         translationSentCount++;
@@ -606,8 +624,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
                         if (sentJointDataOut) {
                             localSentJointDataOut[i].translation = data.translation;
+                            localSentJointDataOut[i].translationIsDefaultPose = false;
                         }
-
                     }
                 }
             }
@@ -655,6 +673,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
         }
     }
 
+    if (hasJointDefaultPoseFlags) {
+        auto startSection = destinationBuffer;
+        QReadLocker readLock(&_jointDataLock);
+
+        // write numJoints
+        int numJoints = _jointData.size();
+        *destinationBuffer++ = (uint8_t)numJoints;
+
+        // write rotationIsDefaultPose bits
+        destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
+            return _jointData[i].rotationIsDefaultPose;
+        });
+
+        // write translationIsDefaultPose bits
+        destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
+            return _jointData[i].translationIsDefaultPose;
+        });
+
+        if (outboundDataRateOut) {
+            size_t numBytes = destinationBuffer - startSection;
+            outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes);
+        }
+    }
+
     int avatarDataSize = destinationBuffer - startPosition;
 
     if (avatarDataSize > (int)byteArraySize) {
@@ -664,6 +706,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
     return avatarDataByteArray.left(avatarDataSize);
 }
+
 // NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
 void AvatarData::doneEncoding(bool cullSmallChanges) {
     // The server has finished sending this version of the joint-data to other nodes.  Update _lastSentJointData.
@@ -674,7 +717,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
         if (_lastSentJointData[i].rotation != data.rotation) {
             if (!cullSmallChanges ||
                 fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
-                if (data.rotationSet) {
+                if (!data.rotationIsDefaultPose) {
                     _lastSentJointData[i].rotation = data.rotation;
                 }
             }
@@ -682,7 +725,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
         if (_lastSentJointData[i].translation != data.translation) {
             if (!cullSmallChanges ||
                 glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
-                if (data.translationSet) {
+                if (!data.translationIsDefaultPose) {
                     _lastSentJointData[i].translation = data.translation;
                 }
             }
@@ -730,6 +773,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa
 
 // read data in packet starting at byte offset and return number of bytes parsed
 int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
+
     // lazily allocate memory for HeadData in case we're not an Avatar instance
     lazyInitHeadData();
 
@@ -745,18 +789,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
 
     #define HAS_FLAG(B,F) ((B & F) == F)
 
-    bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION);
-    bool hasAvatarBoundingBox    = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX);
-    bool hasAvatarOrientation    = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION);
-    bool hasAvatarScale          = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE);
-    bool hasLookAtPosition       = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION);
-    bool hasAudioLoudness        = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS);
-    bool hasSensorToWorldMatrix  = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX);
-    bool hasAdditionalFlags      = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS);
-    bool hasParentInfo           = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO);
-    bool hasAvatarLocalPosition  = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
-    bool hasFaceTrackerInfo      = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
-    bool hasJointData            = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
+    bool hasAvatarGlobalPosition  = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION);
+    bool hasAvatarBoundingBox     = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX);
+    bool hasAvatarOrientation     = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION);
+    bool hasAvatarScale           = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE);
+    bool hasLookAtPosition        = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION);
+    bool hasAudioLoudness         = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS);
+    bool hasSensorToWorldMatrix   = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX);
+    bool hasAdditionalFlags       = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS);
+    bool hasParentInfo            = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO);
+    bool hasAvatarLocalPosition   = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
+    bool hasFaceTrackerInfo       = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
+    bool hasJointData             = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
+    bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS);
 
     quint64 now = usecTimestampNow();
 
@@ -1055,7 +1100,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
             if (validRotations[i]) {
                 sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation);
                 _hasNewJointData = true;
-                data.rotationSet = true;
+                data.rotationIsDefaultPose = false;
             }
         }
 
@@ -1090,7 +1135,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
             if (validTranslations[i]) {
                 sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
                 _hasNewJointData = true;
-                data.translationSet = true;
+                data.translationIsDefaultPose = false;
             }
         }
 
@@ -1110,6 +1155,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
         _jointDataUpdateRate.increment();
     }
 
+    if (hasJointDefaultPoseFlags) {
+        auto startSection = sourceBuffer;
+
+        QWriteLocker writeLock(&_jointDataLock);
+
+        PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t));
+        int numJoints = (int)*sourceBuffer++;
+
+        _jointData.resize(numJoints);
+
+        size_t bitVectorSize = calcBitVectorSize(numJoints);
+        PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize);
+        sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
+            _jointData[i].rotationIsDefaultPose = value;
+        });
+
+        PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize);
+        sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
+            _jointData[i].translationIsDefaultPose = value;
+        });
+
+        int numBytesRead = sourceBuffer - startSection;
+        _jointDefaultPoseFlagsRate.increment(numBytesRead);
+        _jointDefaultPoseFlagsUpdateRate.increment();
+    }
+
     int numBytesRead = sourceBuffer - startPosition;
     _averageBytesReceived.updateAverage(numBytesRead);
 
@@ -1146,6 +1217,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
         return _faceTrackerRate.rate() / BYTES_PER_KILOBIT;
     } else if (rateName == "jointData") {
         return _jointDataRate.rate() / BYTES_PER_KILOBIT;
+    } else if (rateName == "jointDefaultPoseFlagsRate") {
+        return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
     } else if (rateName == "globalPositionOutbound") {
         return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
     } else if (rateName == "localPositionOutbound") {
@@ -1170,6 +1243,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
         return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT;
     } else if (rateName == "jointDataOutbound") {
         return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT;
+    } else if (rateName == "jointDefaultPoseFlagsOutbound") {
+        return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
     }
     return 0.0f;
 }
@@ -1236,9 +1311,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
     }
     JointData& data = _jointData[index];
     data.rotation = rotation;
-    data.rotationSet = true;
+    data.rotationIsDefaultPose = false;
     data.translation = translation;
-    data.translationSet = true;
+    data.translationIsDefaultPose = false;
 }
 
 void AvatarData::clearJointData(int index) {
@@ -1294,7 +1369,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co
         auto& jointData = _jointData[index];
         jointData.rotation = rotation;
         jointData.translation = translation;
-        jointData.rotationSet = jointData.translationSet = true;
+        jointData.rotationIsDefaultPose = false;
+        jointData.translationIsDefaultPose = false;
     });
 }
 
@@ -1304,7 +1380,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation
     writeLockWithNamedJointIndex(name, [&](int index) {
         auto& data = _jointData[index];
         data.rotation = rotation;
-        data.rotationSet = true;
+        data.rotationIsDefaultPose = false;
     });
 }
 
@@ -1314,7 +1390,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans
     writeLockWithNamedJointIndex(name, [&](int index) {
         auto& data = _jointData[index];
         data.translation = translation;
-        data.translationSet = true;
+        data.translationIsDefaultPose = false;
     });
 }
 
@@ -1328,7 +1404,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
     }
     JointData& data = _jointData[index];
     data.rotation = rotation;
-    data.rotationSet = true;
+    data.rotationIsDefaultPose = false;
 }
 
 void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
@@ -1341,7 +1417,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
     }
     JointData& data = _jointData[index];
     data.translation = translation;
-    data.translationSet = true;
+    data.translationIsDefaultPose = false;
 }
 
 void AvatarData::clearJointData(const QString& name) {
@@ -1397,7 +1473,7 @@ void AvatarData::setJointRotations(const QVector<glm::quat>& jointRotations) {
     for (int i = 0; i < size; ++i) {
         auto& data = _jointData[i];
         data.rotation = jointRotations[i];
-        data.rotationSet = true;
+        data.rotationIsDefaultPose = false;
     }
 }
 
@@ -1419,7 +1495,7 @@ void AvatarData::setJointTranslations(const QVector<glm::vec3>& jointTranslation
     for (int i = 0; i < size; ++i) {
         auto& data = _jointData[i];
         data.translation = jointTranslations[i];
-        data.translationSet = true;
+        data.translationIsDefaultPose = false;
     }
 }
 
@@ -1996,9 +2072,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) {
     if (json.isArray()) {
         QJsonArray array = json.toArray();
         result.rotation = quatFromJsonValue(array[0]);
-        result.rotationSet = true;
+        result.rotationIsDefaultPose = false;
         result.translation = vec3FromJsonValue(array[1]);
-        result.translationSet = true;
+        result.translationIsDefaultPose = false;
     }
     return result;
 }
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 00cc760658..92c505f5c3 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -113,18 +113,19 @@ namespace AvatarDataPacket {
     // Packet State Flags - we store the details about the existence of other records in this bitset:
     // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of
     using HasFlags = uint16_t;
-    const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0;
-    const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX    = 1U << 1;
-    const HasFlags PACKET_HAS_AVATAR_ORIENTATION     = 1U << 2;
-    const HasFlags PACKET_HAS_AVATAR_SCALE           = 1U << 3;
-    const HasFlags PACKET_HAS_LOOK_AT_POSITION       = 1U << 4;
-    const HasFlags PACKET_HAS_AUDIO_LOUDNESS         = 1U << 5;
-    const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6;
-    const HasFlags PACKET_HAS_ADDITIONAL_FLAGS       = 1U << 7;
-    const HasFlags PACKET_HAS_PARENT_INFO            = 1U << 8;
-    const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION  = 1U << 9;
-    const HasFlags PACKET_HAS_FACE_TRACKER_INFO      = 1U << 10;
-    const HasFlags PACKET_HAS_JOINT_DATA             = 1U << 11;
+    const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION   = 1U << 0;
+    const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX      = 1U << 1;
+    const HasFlags PACKET_HAS_AVATAR_ORIENTATION       = 1U << 2;
+    const HasFlags PACKET_HAS_AVATAR_SCALE             = 1U << 3;
+    const HasFlags PACKET_HAS_LOOK_AT_POSITION         = 1U << 4;
+    const HasFlags PACKET_HAS_AUDIO_LOUDNESS           = 1U << 5;
+    const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX   = 1U << 6;
+    const HasFlags PACKET_HAS_ADDITIONAL_FLAGS         = 1U << 7;
+    const HasFlags PACKET_HAS_PARENT_INFO              = 1U << 8;
+    const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION    = 1U << 9;
+    const HasFlags PACKET_HAS_FACE_TRACKER_INFO        = 1U << 10;
+    const HasFlags PACKET_HAS_JOINT_DATA               = 1U << 11;
+    const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12;
     const size_t AVATAR_HAS_FLAGS_SIZE = 2;
 
     using SixByteQuat = uint8_t[6];
@@ -256,6 +257,15 @@ namespace AvatarDataPacket {
     };
     */
     size_t maxJointDataSize(size_t numJoints);
+
+    /*
+    struct JointDefaultPoseFlags {
+       uint8_t numJoints;
+       uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)];
+       uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)];
+    };
+    */
+    size_t maxJointDefaultPoseFlagsSize(size_t numJoints);
 }
 
 const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
@@ -321,6 +331,7 @@ public:
     RateCounter<> parentInfoRate;
     RateCounter<> faceTrackerRate;
     RateCounter<> jointDataRate;
+    RateCounter<> jointDefaultPoseFlagsRate;
 };
 
 class AvatarPriority {
@@ -810,6 +821,7 @@ protected:
     RateCounter<> _parentInfoRate;
     RateCounter<> _faceTrackerRate;
     RateCounter<> _jointDataRate;
+    RateCounter<> _jointDefaultPoseFlagsRate;
 
     // Some rate data for incoming data updates
     RateCounter<> _parseBufferUpdateRate;
@@ -825,6 +837,7 @@ protected:
     RateCounter<> _parentInfoUpdateRate;
     RateCounter<> _faceTrackerUpdateRate;
     RateCounter<> _jointDataUpdateRate;
+    RateCounter<> _jointDefaultPoseFlagsUpdateRate;
 
     // Some rate data for outgoing data
     AvatarDataRate _outboundDataRate;
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index fc1688974c..2e39e2e498 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -1050,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
         return;
     }
 
-    QVector<JointData> jointsData;
+    QVector<EntityJointData> jointsData;
 
     const QVector<FBXAnimationFrame>&  frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
     int frameCount = frames.size();
diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp
index 8996665262..a615beefa9 100644
--- a/libraries/entities/src/ModelEntityItem.cpp
+++ b/libraries/entities/src/ModelEntityItem.cpp
@@ -452,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) {
     });
 }
 
-void ModelEntityItem::setAnimationJointsData(const QVector<JointData>& jointsData) {
+void ModelEntityItem::setAnimationJointsData(const QVector<EntityJointData>& jointsData) {
     resizeJointArrays(jointsData.size());
     _jointDataLock.withWriteLock([&] {
         for (auto index = 0; index < jointsData.size(); ++index) {
diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h
index 6169039f52..c2109ba51f 100644
--- a/libraries/entities/src/ModelEntityItem.h
+++ b/libraries/entities/src/ModelEntityItem.h
@@ -124,7 +124,7 @@ public:
     virtual void setJointTranslations(const QVector<glm::vec3>& translations);
     virtual void setJointTranslationsSet(const QVector<bool>& translationsSet);
 
-    virtual void setAnimationJointsData(const QVector<JointData>& jointsData);
+    virtual void setAnimationJointsData(const QVector<EntityJointData>& jointsData);
 
     QVector<glm::quat> getJointRotations() const;
     QVector<bool> getJointRotationsSet() const;
@@ -150,7 +150,7 @@ protected:
     bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations
 
     struct ModelJointData {
-        JointData joint;
+        EntityJointData joint;
         bool rotationDirty { false };
         bool translationDirty { false };
     };
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 711aeb2cc2..c48c6bfc0b 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
         case PacketType::AvatarData:
         case PacketType::BulkAvatarData:
         case PacketType::KillAvatar:
-            return static_cast<PacketVersion>(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar);
+            return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags);
         case PacketType::MessagesData:
             return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
         case PacketType::ICEServerHeartbeat:
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 458666571c..0f69691bd5 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
     AvatarIdentitySequenceFront,
     IsReplicatedInAvatarIdentity,
     AvatarIdentityLookAtSnapping,
-    UpdatedMannequinDefaultAvatar
+    UpdatedMannequinDefaultAvatar,
+    AvatarJointDefaultPoseFlags
 };
 
 enum class DomainConnectRequestVersion : PacketVersion {
diff --git a/libraries/shared/src/BitVectorHelpers.h b/libraries/shared/src/BitVectorHelpers.h
new file mode 100644
index 0000000000..71826036eb
--- /dev/null
+++ b/libraries/shared/src/BitVectorHelpers.h
@@ -0,0 +1,58 @@
+//
+//  BitVectorHelpers.h
+//  libraries/shared/src
+//
+//  Created by Anthony Thibault on 1/19/18.
+//  Copyright 2018 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_BitVectorHelpers_h
+#define hifi_BitVectorHelpers_h
+
+size_t calcBitVectorSize(int numBits) {
+    return ((numBits - 1) >> 3) + 1;
+}
+
+// func should be of type bool func(int index)
+template <typename F>
+size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) {
+    size_t totalBytes = ((numBits - 1) >> 3) + 1;
+    uint8_t* cursor = destinationBuffer;
+    uint8_t byte = 0;
+    uint8_t bit = 0;
+
+    for (int i = 0; i < numBits; i++) {
+        if (func(i)) {
+            byte |= (1 << bit);
+        }
+        if (++bit == BITS_IN_BYTE) {
+            *cursor++ = byte;
+            byte = 0;
+            bit = 0;
+        }
+    }
+    return totalBytes;
+}
+
+// func should be of type 'void func(int index, bool value)'
+template <typename F>
+size_t readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) {
+    size_t totalBytes = ((numBits - 1) >> 3) + 1;
+    const uint8_t* cursor = sourceBuffer;
+    uint8_t bit = 0;
+
+    for (int i = 0; i < numBits; i++) {
+        bool value = (bool)(*cursor & (1 << bit));
+        func(i, value);
+        if (++bit == BITS_IN_BYTE) {
+            cursor++;
+            bit = 0;
+        }
+    }
+    return totalBytes;
+}
+
+#endif
diff --git a/libraries/shared/src/JointData.h b/libraries/shared/src/JointData.h
index a05b5c649a..f4c8b89e7a 100644
--- a/libraries/shared/src/JointData.h
+++ b/libraries/shared/src/JointData.h
@@ -5,14 +5,27 @@
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
 
-// Used by the avatar mixer to describe a single joint
-// These are relative to their parent and translations are in meters
-class JointData {
+class EntityJointData {
 public:
     glm::quat rotation;
+    glm::vec3 translation;
     bool rotationSet = false;
-    glm::vec3 translation;  // meters
     bool translationSet = false;
 };
 
+// Used by the avatar mixer to describe a single joint
+// Translations relative to their parent and are in meters.
+// Rotations are absolute (i.e. not relative to parent) and are in rig space.
+class JointData {
+public:
+    glm::quat rotation;
+    glm::vec3 translation;
+
+    // This indicates that the rotation or translation is the same as the defaultPose for the avatar.
+    // if true, it also means that the rotation or translation value in this structure is not valid and
+    // should be replaced by the avatar's actual default pose value.
+    bool rotationIsDefaultPose = true;
+    bool translationIsDefaultPose = true;
+};
+
 #endif