diff --git a/tools/dissectors/3-hf-avatar.lua b/tools/dissectors/3-hf-avatar.lua
index bc449770f5..9a74d10a1e 100644
--- a/tools/dissectors/3-hf-avatar.lua
+++ b/tools/dissectors/3-hf-avatar.lua
@@ -5,6 +5,33 @@ p_hf_avatar = Proto("hf-avatar", "HF Avatar Protocol")
 
 -- avatar data fields
 local f_avatar_id = ProtoField.guid("hf_avatar.avatar_id", "Avatar ID")
+
+-- sizes in bytes
+local f_avatar_header_size = ProtoField.int32("hf_avatar.header_size", "Header Size", base.DEC)
+local f_avatar_global_position_size = ProtoField.int32("hf_avatar.global_position_size", "Global Position Size", base.DEC)
+local f_avatar_bounding_box_size = ProtoField.int32("hf_avatar.bounding_box_size", "Bounding Box Size", base.DEC)
+local f_avatar_orientation_size = ProtoField.int32("hf_avatar.orientation_size", "Orientation Size", base.DEC)
+local f_avatar_scale_size = ProtoField.int32("hf_avatar.scale_size", "Scale Size", base.DEC)
+local f_avatar_look_at_position_size = ProtoField.int32("hf_avatar.look_at_position_size", "Look At Position Size", base.DEC)
+local f_avatar_audio_loudness_size = ProtoField.int32("hf_avatar.audio_loudness_size", "Audio Loudness Size", base.DEC)
+local f_avatar_sensor_to_world_size = ProtoField.int32("hf_avatar.sensor_to_world_size", "Sensor To World Matrix Size", base.DEC)
+local f_avatar_additional_flags_size = ProtoField.int32("hf_avatar.additional_flags_size", "Additional Flags Size", base.DEC)
+local f_avatar_parent_info_size = ProtoField.int32("hf_avatar.parent_info_size", "Parent Info Size", base.DEC)
+local f_avatar_local_position_size = ProtoField.int32("hf_avatar.local_position_size", "Local Position Size", base.DEC)
+local f_avatar_face_tracker_info_size = ProtoField.int32("hf_avatar.face_tracker_info_size", "Face Tracker Info Size", base.DEC)
+local f_avatar_joint_data_size = ProtoField.int32("hf_avatar.joint_data_size", "Joint Data Size", base.DEC)
+local f_avatar_default_pose_flags_size = ProtoField.int32("hf_avatar.default_pose_flags_size", "Default Pose Flags Size", base.DEC)
+local f_avatar_grab_joints_size = ProtoField.int32("hf_avatar.grab_joints_size", "Grab Joints Size", base.DEC)
+
+local f_avatar_joint_data_bit_vector_size = ProtoField.int32("hf_avatar.joint_data_bit_vector_size", "Joint Data Bit Vector Size", base.DEC)
+local f_avatar_joint_data_rotations_size = ProtoField.int32("hf_avatar.joint_data_rotations_size", "Joint Data Rotations Size", base.DEC)
+local f_avatar_joint_data_translations_size = ProtoField.int32("hf_avatar.joint_data_translations_size", "Joint Data Translations Size", base.DEC)
+local f_avatar_joint_data_faux_joints_size = ProtoField.int32("hf_avatar.joint_data_faux_joints_size", "Joint Data Faux Joints Size", base.DEC)
+local f_avatar_joint_data_other_size = ProtoField.int32("hf_avatar.joint_data_other_size", "Joint Data Other Size", base.DEC)
+
+local f_avatar_bulk_count = ProtoField.int32("hf_avatar.bulk_count", "Bulk Count", base.DEC)
+
+-- contents
 local f_avatar_data_has_flags = ProtoField.string("hf_avatar.avatar_has_flags", "Has Flags")
 local f_avatar_data_position = ProtoField.string("hf_avatar.avatar_data_position", "Position")
 local f_avatar_data_dimensions = ProtoField.string("hf_avatar.avatar_data_dimensions", "Dimensions")
@@ -19,7 +46,6 @@ local f_avatar_data_valid_rotations = ProtoField.string("hf_avatar.avatar_data_v
 local f_avatar_data_valid_translations = ProtoField.string("hf_avatar.avatar_data_valid_translations", "Valid Translations")
 local f_avatar_data_default_rotations = ProtoField.string("hf_avatar.avatar_data_default_rotations", "Valid Default")
 local f_avatar_data_default_translations = ProtoField.string("hf_avatar.avatar_data_default_translations", "Valid Default")
-local f_avatar_data_sizes = ProtoField.string("hf_avatar.avatar_sizes", "Sizes")
 
 -- avatar trait data fields
 local f_avatar_trait_data = ProtoField.bytes("hf_avatar.avatar_trait_data", "Avatar Trait Data")
@@ -31,7 +57,32 @@ local f_avatar_trait_instance_id = ProtoField.guid("hf_avatar.trait_instance_id"
 local f_avatar_trait_binary = ProtoField.bytes("hf_avatar.trait_binary", "Trait Binary Data")
 
 p_hf_avatar.fields = {
-  f_avatar_id, f_avatar_data_parent_id,
+  f_avatar_id,
+  f_avatar_data_parent_id,
+
+  f_avatar_header_size,
+  f_avatar_global_position_size,
+  f_avatar_bounding_box_size,
+  f_avatar_orientation_size,
+  f_avatar_scale_size,
+  f_avatar_look_at_position_size,
+  f_avatar_audio_loudness_size,
+  f_avatar_sensor_to_world_size,
+  f_avatar_additional_flags_size,
+  f_avatar_parent_info_size,
+  f_avatar_local_position_size,
+  f_avatar_face_tracker_info_size,
+  f_avatar_joint_data_size,
+  f_avatar_default_pose_flags_size,
+  f_avatar_grab_joints_size,
+  f_avatar_bulk_count,
+
+  f_avatar_joint_data_bit_vector_size,
+  f_avatar_joint_data_rotations_size,
+  f_avatar_joint_data_translations_size,
+  f_avatar_joint_data_faux_joints_size,
+  f_avatar_joint_data_other_size,
+
   f_avatar_trait_data,
   f_avatar_trait_type, f_avatar_trait_id,
   f_avatar_trait_version, f_avatar_trait_binary,
@@ -55,7 +106,34 @@ function p_hf_avatar.dissector(buf, pinfo, tree)
 
   local avatar_data
 
+  -- sizes
+  local avatar_data_sizes = {
+    header_size = 0,
+    global_position_size = 0,
+    bounding_box_size = 0,
+    orientation_size = 0,
+    scale_size = 0,
+    look_at_position_size = 0,
+    audio_loudness_size = 0,
+    sensor_to_world_size = 0,
+    additional_flags_size = 0,
+    parent_info_size = 0,
+    local_position_size = 0,
+    face_tracker_info_size = 0,
+    joint_data_size = 0,
+    default_pose_flags_size = 0,
+    grab_joints_size = 0,
+    bulk_count = 0,
+    joint_data_bit_vector_size = 0,
+    joint_data_rotations_size = 0,
+    joint_data_translations_size = 0,
+    joint_data_faux_joints_size = 0,
+    joint_data_other_size = 0
+  }
+
+
   local packet_type = packet_type_extractor().value
+  avatar_data_sizes.header_size = avatar_data_sizes.header_size + 1
 
   if packet_type == 6 then
     -- AvatarData packet
@@ -63,12 +141,14 @@ function p_hf_avatar.dissector(buf, pinfo, tree)
     -- uint16 sequence_number
     local sequence_number = buf(i, 2):le_uint()
     i = i + 2
+    avatar_data_sizes.header_size = avatar_data_sizes.header_size + 2
 
     local avatar_data_packet_len = buf:len() - i
-    avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len))
+    avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len), avatar_data_sizes)
     i = i + avatar_data_packet_len
 
     add_avatar_data_subtrees(avatar_data)
+    add_avatar_data_sizes(avatar_data_sizes)
 
   elseif packet_type == 11 then
     -- BulkAvatarData packet
@@ -76,13 +156,18 @@ function p_hf_avatar.dissector(buf, pinfo, tree)
       -- avatar_id is first 16 bytes
       avatar_subtree:add(f_avatar_id, buf(i, 16))
       i = i + 16
+      avatar_data_sizes.header_size = avatar_data_sizes.header_size + 16
 
       local avatar_data_packet_len = buf:len() - i
-      avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len))
-      i = i + avatar_data_packet_len
+      avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len), avatar_data_sizes)
+      i = i + avatar_data.bytes_consumed
 
       add_avatar_data_subtrees(avatar_data)
+      avatar_data_sizes.bulk_count = avatar_data_sizes.bulk_count + 1
     end
+
+    add_avatar_data_sizes(avatar_data_sizes)
+
   elseif packet_type == 100 then
     -- BulkAvatarTraits packet
 
@@ -209,9 +294,74 @@ function add_avatar_data_subtrees(avatar_data)
   if avatar_data["default_translations"] then
     avatar_subtree:add(f_avatar_data_default_translations, avatar_data["default_translations"])
   end
-  if avatar_data["sizes"] then
-    avatar_subtree:add(f_avatar_data_sizes, avatar_data["sizes"])
+end
+
+function add_avatar_data_sizes(avatar_data_sizes)
+  if avatar_data_sizes.header_size then
+    avatar_subtree:add(f_avatar_header_size, avatar_data_sizes.header_size)
   end
+  if avatar_data_sizes.global_position_size then
+    avatar_subtree:add(f_avatar_global_position_size, avatar_data_sizes.global_position_size)
+  end
+  if avatar_data_sizes.bounding_box_size then
+    avatar_subtree:add(f_avatar_bounding_box_size, avatar_data_sizes.bounding_box_size)
+  end
+  if avatar_data_sizes.orientation_size then
+    avatar_subtree:add(f_avatar_orientation_size, avatar_data_sizes.orientation_size)
+  end
+  if avatar_data_sizes.scale_size then
+    avatar_subtree:add(f_avatar_scale_size, avatar_data_sizes.scale_size)
+  end
+  if avatar_data_sizes.look_at_position_size then
+    avatar_subtree:add(f_avatar_look_at_position_size, avatar_data_sizes.look_at_position_size)
+  end
+  if avatar_data_sizes.audio_loudness_size then
+    avatar_subtree:add(f_avatar_audio_loudness_size, avatar_data_sizes.audio_loudness_size)
+  end
+  if avatar_data_sizes.sensor_to_world_size then
+    avatar_subtree:add(f_avatar_sensor_to_world_size, avatar_data_sizes.sensor_to_world_size)
+  end
+  if avatar_data_sizes.additional_flags_size then
+    avatar_subtree:add(f_avatar_additional_flags_size, avatar_data_sizes.additional_flags_size)
+  end
+  if avatar_data_sizes.parent_info_size then
+    avatar_subtree:add(f_avatar_parent_info_size, avatar_data_sizes.parent_info_size)
+  end
+  if avatar_data_sizes.local_position_size then
+    avatar_subtree:add(f_avatar_local_position_size, avatar_data_sizes.local_position_size)
+  end
+  if avatar_data_sizes.face_tracker_info_size then
+    avatar_subtree:add(f_avatar_face_tracker_info_size, avatar_data_sizes.face_tracker_info_size)
+  end
+  if avatar_data_sizes.joint_data_size then
+    avatar_subtree:add(f_avatar_joint_data_size, avatar_data_sizes.joint_data_size)
+  end
+  if avatar_data_sizes.default_pose_flags_size then
+    avatar_subtree:add(f_avatar_default_pose_flags_size, avatar_data_sizes.default_pose_flags_size)
+  end
+  if avatar_data_sizes.grab_joints_size then
+    avatar_subtree:add(f_avatar_grab_joints_size, avatar_data_sizes.grab_joints_size)
+  end
+  if avatar_data_sizes.bulk_count then
+    avatar_subtree:add(f_avatar_bulk_count, avatar_data_sizes.bulk_count)
+  end
+
+  if avatar_data_sizes.joint_data_bit_vector_size then
+    avatar_subtree:add(f_avatar_joint_data_bit_vector_size, avatar_data_sizes.joint_data_bit_vector_size)
+  end
+  if avatar_data_sizes.joint_data_rotations_size then
+    avatar_subtree:add(f_avatar_joint_data_rotations_size, avatar_data_sizes.joint_data_rotations_size)
+  end
+  if avatar_data_sizes.joint_data_translations_size then
+    avatar_subtree:add(f_avatar_joint_data_translations_size, avatar_data_sizes.joint_data_translations_size)
+  end
+  if avatar_data_sizes.joint_data_faux_joints_size then
+    avatar_subtree:add(f_avatar_joint_data_faux_joints_size, avatar_data_sizes.joint_data_faux_joints_size)
+  end
+  if avatar_data_sizes.joint_data_other_size then
+    avatar_subtree:add(f_avatar_joint_data_other_size, avatar_data_sizes.joint_data_other_size)
+  end
+
 end
 
 function decode_vec3(buf)
@@ -251,16 +401,33 @@ function decode_validity_bits(buf, num_bits)
   return result
 end
 
-function decode_avatar_data_packet(buf)
+function validity_bits_to_string(buf, num_bits)
+  -- first pass, decode each bit into an array of booleans
+  local i = 0
+  local bit = 0
+  local booleans = {}
+  for n = 1, num_bits do
+    local value = (bit32.band(buf(i, 1):uint(), bit32.lshift(1, bit)) ~= 0)
+    booleans[#booleans + 1] = (value and 1 or 0)
+    bit = bit + 1
+    if bit == 8 then
+      i = i + 1
+      bit = 0
+    end
+  end
+
+  return table.concat(booleans, "")
+end
+
+function decode_avatar_data_packet(buf, avatar_data_sizes)
 
   local i = 0
   local result = {}
 
-  result["sizes"] = ""
-
   -- uint16 has_flags
   local has_flags = buf(i, 2):le_uint()
   i = i + 2
+  avatar_data_sizes.header_size = avatar_data_sizes.header_size + 2
 
   local has_global_position = (bit32.band(has_flags, 1) ~= 0)
   local has_bounding_box = (bit32.band(has_flags, 2) ~= 0)
@@ -275,6 +442,7 @@ function decode_avatar_data_packet(buf)
   local has_face_tracker_info = (bit32.band(has_flags, 1024) ~= 0)
   local has_joint_data = (bit32.band(has_flags, 2048) ~= 0)
   local has_joint_default_pose_flags = (bit32.band(has_flags, 4096) ~= 0)
+  local has_grab_joints = (bit32.band(has_flags, 8192) ~= 0)
 
   result["has_flags"] = string.format("HasFlags: 0x%x", has_flags)
 
@@ -282,6 +450,7 @@ function decode_avatar_data_packet(buf)
     local position = decode_vec3(buf(i, 12))
     result["position"] = string.format("Position: %.3f, %.3f, %.3f", position[1], position[2], position[3])
     i = i + 12
+    avatar_data_sizes.global_position_size = avatar_data_sizes.global_position_size + 12
   end
 
   if has_bounding_box then
@@ -289,6 +458,7 @@ function decode_avatar_data_packet(buf)
     i = i + 12
     local offset = decode_vec3(buf(i, 12))
     i = i + 12
+    avatar_data_sizes.bounding_box_size = avatar_data_sizes.bounding_box_size + (12 + 12)
     result["dimensions"] = string.format("Dimensions: %.3f, %.3f, %.3f", dimensions[1], dimensions[2], dimensions[3])
     result["offset"] = string.format("Offset: %.3f, %.3f, %.3f", offset[1], offset[2], offset[3])
   end
@@ -296,34 +466,40 @@ function decode_avatar_data_packet(buf)
   if has_orientation then
     -- TODO: orientation is hard to decode...
     i = i + 6
+    avatar_data_sizes.orientation_size = avatar_data_sizes.orientation_size + 6
   end
 
   if has_scale then
     -- TODO: scale is hard to decode...
     i = i + 2
+    avatar_data_sizes.scale_size = avatar_data_sizes.scale_size + 2
   end
 
   if has_look_at_position then
     local look_at = decode_vec3(buf(i, 12))
     i = i + 12
+    avatar_data_sizes.look_at_position_size = avatar_data_sizes.look_at_position_size + 12
     result["look_at_position"] = string.format("Look At Position: %.3f, %.3f, %.3f", look_at[1], look_at[2], look_at[3])
   end
 
   if has_audio_loudness then
     local loudness = buf(i, 1):uint()
     i = i + 1
+    avatar_data_sizes.audio_loudness_size = avatar_data_sizes.audio_loudness_size + 1
     result["audio_loudness"] = string.format("Audio Loudness: %d", loudness)
   end
 
   if has_sensor_to_world_matrix then
     -- TODO: sensor to world matrix is hard to decode
     i = i + 20
+    avatar_data_sizes.sensor_to_world_size = avatar_data_sizes.sensor_to_world_size + 20
   end
 
   if has_additional_flags then
-    local flags = buf(i, 1):uint()
-    i = i + 1
+    local flags = buf(i, 2):uint()
+    i = i + 2
     result["additional_flags"] = string.format("Additional Flags: 0x%x", flags)
+    avatar_data_sizes.additional_flags_size = avatar_data_sizes.additional_flags_size + 2
   end
 
   if has_parent_info then
@@ -331,6 +507,7 @@ function decode_avatar_data_packet(buf)
     i = i + 16
     local parent_joint_index = buf(i, 2):le_int()
     i = i + 2
+    avatar_data_sizes.parent_info_size = avatar_data_sizes.parent_info_size + 18
     result["parent_id"] = parent_id
     result["parent_joint_index"] = string.format("Parent Joint Index: %d", parent_joint_index)
   end
@@ -338,6 +515,7 @@ function decode_avatar_data_packet(buf)
   if has_local_position then
     local local_pos = decode_vec3(buf(i, 12))
     i = i + 12
+    avatar_data_sizes.local_position_size = avatar_data_sizes.local_position_size + 12
     result["local_position"] = string.format("Local Position: %.3f, %.3f, %.3f", local_pos[1], local_pos[2], local_pos[3])
   end
 
@@ -357,6 +535,7 @@ function decode_avatar_data_packet(buf)
       blendshape_coefficients[n] = buf(i, 4):le_float()
       i = i + 4
     end
+    avatar_data_sizes.face_tracker_info_size = avatar_data_sizes.face_tracker_info_size + 17 + (num_blendshape_coefficients * 4)
     -- TODO: insert blendshapes into result
   end
 
@@ -366,30 +545,46 @@ function decode_avatar_data_packet(buf)
 
     local num_joints = buf(i, 1):uint()
     i = i + 1
+    avatar_data_sizes.joint_data_other_size = avatar_data_sizes.joint_data_other_size + 1
+
     local num_validity_bytes = math.ceil(num_joints / 8)
 
     local indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints)
+    local s = validity_bits_to_string(buf(i, num_validity_bytes), num_joints)
     i = i + num_validity_bytes
-    result["valid_rotations"] = "Valid Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}"
+    avatar_data_sizes.joint_data_bit_vector_size = avatar_data_sizes.joint_data_bit_vector_size + num_validity_bytes
+
+    result["valid_rotations"] = "Valid Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. s .. "}"
 
     -- TODO: skip rotations for now
-    i = i + #indices * 6
+    i = i + (#indices * 6)
+    avatar_data_sizes.joint_data_rotations_size = avatar_data_sizes.joint_data_rotations_size + (#indices * 6)
 
     indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints)
+    s = validity_bits_to_string(buf(i, num_validity_bytes), num_joints)
     i = i + num_validity_bytes
-    result["valid_translations"] = "Valid Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}"
+    avatar_data_sizes.joint_data_bit_vector_size = avatar_data_sizes.joint_data_bit_vector_size + num_validity_bytes
+    result["valid_translations"] = "Valid Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. s .. "}"
 
     -- TODO: skip maxTranslationDimension
     i = i + 4
+    avatar_data_sizes.joint_data_other_size = avatar_data_sizes.joint_data_other_size + 4
 
     -- TODO: skip translations for now
-    i = i + #indices * 6
+    i = i + (#indices * 6)
+    avatar_data_sizes.joint_data_translations_size = avatar_data_sizes.joint_data_translations_size + (#indices * 6)
 
-    -- TODO: skip hand controller data
-    i = i + 24
+    -- TODO: skip faux joint data
+    i = i + (2 * 12)
+    avatar_data_sizes.joint_data_faux_joints_size = avatar_data_sizes.joint_data_faux_joints_size + (2 * 12)
 
-    result["sizes"] = result["sizes"] .. " Poses: " .. (i - joint_poses_start)
+    avatar_data_sizes.joint_data_size = avatar_data_sizes.joint_data_size + (i - joint_poses_start)
+  end
 
+  if has_grab_joints then
+    -- TODO: skip grab joints
+    i = i + 84
+    avatar_data_sizes.grab_joints_size = avatar_data_sizes.grab_joints_size + 84
   end
 
   if has_joint_default_pose_flags then
@@ -398,15 +593,19 @@ function decode_avatar_data_packet(buf)
     local num_validity_bytes = math.ceil(num_joints / 8)
 
     local indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints)
+    local s = validity_bits_to_string(buf(i, num_validity_bytes), num_joints)
     i = i + num_validity_bytes
-    result["default_rotations"] = "Default Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}"
+    result["default_rotations"] = "Default Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. s .. "}"
 
     indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints)
+    s = validity_bits_to_string(buf(i, num_validity_bytes), num_joints)
     i = i + num_validity_bytes
-    result["default_translations"] = "Default Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}"
+    result["default_translations"] = "Default Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. s .. "}"
+
+    avatar_data_sizes.default_pose_flags_size = avatar_data_sizes.default_pose_flags_size + 2 * num_validity_bytes
   end
 
-  result["sizes"] = result["sizes"] .. " Total: " .. i
+  result["bytes_consumed"] = i
 
   return result
 end