Vector2
Type<Vector2>
Two
f32 components. 8 bytes.Vector3
Type<Vector3>
Three
f32 components. 12 bytes.f32 gives roughly 7 significant digits, so world coordinates keep sub-millimetre precision
anywhere inside Roblox’s playable volume. The dynamic encoder would have used f64 and
spent 24 bytes plus a tag for the same value.CFrame
Type<CFrame>
Position, then rotation as an axis vector plus an angle, all
f32. 28 bytes.Color3
Type<Color3>
Three
u8 components. 3 bytes.Instance
Type<Instance>
A varint index into the packet’s instance side-array. 1 to 3 bytes.
Instances never enter the buffer. They are appended to the writer’s
insts array and passed
as the remote’s second argument, so Roblox performs the reference translation natively. Only
the index is serialised, which means a deeply nested instance costs the same as a top-level
one.Within one batch, repeat references to the same instance are deduplicated to a single array
entry.Enum
Type<EnumItem>
A single
u8 index into the enum’s item list. 1 byte.Enum
required
The enum class, not an item:
Enum.KeyCode, not Enum.KeyCode.Space.enum:GetEnumItems() at build time and maps each item to its
zero-based position, so lookups on both ends are a single table index.
Size comparison
What each of these costs dynamically against schematised:The vector and CFrame constructors are already
f32-based in both paths, so the schema only
removes the tag byte. Enums are the outlier, and Float32 over an implicitly-f64 bare
number is the other.Next steps
Composites
Nesting these into optionals, arrays and structs.
Serialization
Every lossy conversion in one place.
