Roblox Binary Model Format, Version 0
This is unofficial documentation for Roblox’s binary model format. The binary model format is used for places (.rbxl files), models (.rbxm files), and many objects uploaded to Roblox’s asset storage.
The binary model format intended to supersede Roblox’s older XML model format.
This document is based on:
- ROBLOX File Format by Gregory Comer
- LibRbxl by Gregory Comer
- rbxfile by Anaminus
- Roblox-File-Format by CloneTrooper1019
- Observing
rbxmandrbxloutput from Roblox Studio - Information directly communicated by Roblox when necessary
Contents
Document Conventions
This document assumes a basic understanding of Rust’s conventions for numeric types. For example:
u16is an unsigned 16-bit integeri32is a signed 32-bit integer
Integers are assumed to be little endian and 2’s complement unless otherwise specified. The presence of big endian integers and integers with interesting transformations are explicitly noted.
The data contained in a chunk may be compressed. The term “chunk data” refers to the decompressed contents.
File Structure
Binary model files consist of a short header, followed by a series of chunks. Each chunk has the same framing, enabling consumers to partialy decode a file.
- File Header
- Chunks
- Zero or one
METAchunks - Zero or one
SSTRchunks - Zero or more
INSTchunk - Zero or more
PROPchunks - One
PRNTchunk - One
ENDchunk
- Zero or one
File Header
Every file starts with a 32 byte header.
| Field Name | Format | Value |
|---|---|---|
| Magic Number | 8 bytes | Always <roblox! |
| Signature | 6 bytes | Always 89 ff 0d 0a 1a 0a |
| Version | u16 |
Always 0 |
| Class Count | i32 |
Number of distinct classes in the file (i.e. the number of INST chunks) |
| Instance Count | i32 |
Number of instances in the file |
| Reserved | 8 bytes | Always 0 |
Chunks
Every chunk starts with a 16 byte header followed by the chunk’s data.
| Field Name | Format | Value |
|---|---|---|
| Chunk Name | 4 bytes | The chunk’s name, like META or INST |
| Compressed Length | u32 |
Length of the chunk in bytes, if it is compressed |
| Uncompressed Length | u32 |
Length of the chunk’s data after decompression |
| Reserved | 4 bytes | Always 0 |
| Chunk Data | Variable | The data contained in the chunk |
If Chunk Name is less than four bytes, the remainder is filled with zeros.
If Compressed Length is zero, Chunk Data contains Uncompressed Length bytes of data for the chunk.
If Compressed Length is nonzero, Chunk Data will either contain an LZ4 or ZSTD compressed block. This compressed body is Compressed Length bytes long and will expand to Uncompressed Length bytes when decompressed.
Which of the compression algorithms is used is indicated by the first several bytes of the compressed chunk body.
- If the first 4 bytes of the block are the literal sequence
28 b5 2f fd, the block is compressed using the ZSTD algorithm. - Otherwise, the block is compressed using the LZ4 algorithm.
When a chunk is compressed using ZSTD, there is also a ZSTD frame present following the magic number that must be read by a decompressor. When it is compressed using LZ4, there is no frame and the compressed data begins immediately after the header.
The data contained in Chunk Data varies in formatting based on the value of Chunk Name. Chunks used by Roblox are documented below:
META Chunk
The META chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Number of Metadata Entries | u32 |
The number of metadata entries in the chunk |
| Metadata Entries | Array(Entries) | The actual metadata entries |
Each metadata entry has the following format:
| Field Name | Format | Value |
|---|---|---|
| Metadata Key | String |
The metadata key, which should be unique |
| Metadata Value | String |
The value for this metadata key |
The Metadata chunk (META) is a map of strings to strings. It represents metadata about the model, such as whether it was authored with ExplicitAutoJoints enabled.
There should be zero or one META chunks.
Observed metadata entries and their values:
ExplicitAutoJoints:trueorfalse
SSTR Chunk
The SSTR chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Version | u32 |
The version of the SSTR chunk (always 0) |
| Shared String Count | u32 |
The number of SharedStrings in the chunk |
| Strings | Array(Shared Strings) | The actual shared string entries |
A shared string entry looks like this:
| Field Name | Format | Value |
|---|---|---|
| MD5 Hash | 16 bytes | An MD5 hash of the Shared String |
| Shared String | String |
The string that’s used by a later PROP chunk |
The Shared String chunk (SSTR) is an array of strings. It’s used to reduce the overall size of a file by allowing large strings to be reused in PROP chunks. The MD5 Hash isn’t used by Roblox Studio when loading the file.
There should be zero or one SSTR chunks.
INST Chunk
The INST chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Class ID | u32 |
An arbitrarily-chosen ID referring to a Roblox class |
| Class Name | String |
The class name, like Folder or Part |
| Object Format | u8 |
1 if the class is a service, otherwise 0 |
| Instance Count | u32 |
The number of instances belonging to the class |
| Referents | Array(Referent) |
The referents of instances belonging to the class |
| Service Markers | Array(u8) |
1 for each instance if the class is a service, otherwise not present |
There should be one INST chunk for each type of instance defined.
There are two forms of the INST chunk determined by the Object Format field:
0: regular1: service
If the Object Format is regular, the service markers section will not be present.
If the Object Format is service, the service markers section contains 1 repeated for the number of instances of that type in the file. If this field is not set, Roblox may create duplicate copies of services, like in rojo-rbx/rbx-dom#11.
Class ID must be unique and ideally sorted monotonically among all INST chunks. It’s used later in the file to refer to this type.
Class Name should match the ClassName specified on an instance in Roblox.
The length of Referents must equal Instance Count.
PROP Chunk
The PROP chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Class ID | u32 |
The class ID assigned in the INST chunk |
| Property Name | String |
The name of the property, like CFrame |
| Type ID | u8 |
The Data Type of the property |
| Values | Array(Value) | A list of values whose type is determined by Type ID |
The property chunk (PROP) defines a single property for a single instance type.
There should be one PROP chunk per property per instance type.
Because of the shape of this chunk, every instance of a given class must have the same properties specified with the same times. Put another way, if an instance in the file defines a property, all other instances of the same class must also specify that property!
Class ID defines the class that this property applies to as defined in a preceding INST chunk.
Property Name defines the serializable name of the property. Note that this is not necessarily the same as the name reflected to Lua, which is sometimes referred to as the canonical name.
Type ID corresponds to a Data Type’s Type ID.
Values contains an array of values whose type is determined by Type ID and whose length is equal to the number of instances belonging to Class ID.
PRNT Chunk
The PRNT chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Version | u8 |
Always 0 |
| Instance Count | u32 |
Number of instances described in this chunk |
| Child Referents | Array(Referent) |
Referents of child instances |
| Parent Referents | Array(Referent) |
Referents of parent instances |
The parent chunk (PRNT) defines the hierarchy relationship between every instance in the file.
There should be exactly one PRNT chunk.
Version field should currently always be zero.
Instance Count should be equal to the number of instances in the file header chunk, since each object should have a parent.
Child Referents and Parent Referents should both have length equal to Instance Count. The parent of the ID at position N in Child Referents is a child of the ID at position N in Parent Referents.
A null parent referent (-1) indicates that the object is a root instance. In a place, that means the object is a child of DataModel. In a model, that means the object should be placed directly under the object the model is being inserted into.
END Chunk
The END chunk has this layout:
| Field Name | Format | Value |
|---|---|---|
| Magic Value | 9 bytes | Always </roblox> |
The ending chunk (END) signifies the end of the file.
The END chunk must not be compressed. It is used as a rough form of file validation when uploading places to the Roblox website.
Data Types
String
Type ID 0x01
The String type is stored as a length-prefixed sequence of bytes. The length is stored as an untransformed 32-bit integer. String values are UTF-8 encoded.
| Field Name | Format | Value |
|---|---|---|
| Length | u32 |
The length of the string |
| Data | Array(Bytes) | The actual bytes that make up the string |
When an array of String values is present, they are stored in sequence without any modification.
Bool
Type ID 0x02
The Bool type is stored as a single byte. If the byte is 0x00, the bool is false. If it is 0x01, it is true.
When an array of Bool values is present, they are stored in sequence.
Int32
Type ID 0x03
The Int32 type is stored as a big-endian transformed 32-bit integer.
When an array of Int32 values is present, the bytes of the integers are subject to byte interleaving.
Float32
Type ID 0x04
The Float32 type is stored using the Roblox float format and is big-endian. This datatype is also called float or single.
When an array of Float32 values is present, the bytes of the floats are subject to byte interleaving.
Float64
Type ID 0x05
The Float64 type is stored using the IEEE-754 format and is little-endian. This datatype is also called double.
When an array of Float64 values is present, they are in sequence with no transformations.
UDim
Type ID 0x06
The UDim type is stored as a struct composed of a Float32 and an Int32:
| Field Name | Format | Value |
|---|---|---|
| Scale | Float32 |
The Scale component of the UDim |
| Offset | Int32 |
The Offset component of the UDim |
When an array of UDim values is present, the bytes of each individual components are stored as arrays, meaning their bytes are subject to byte interleaving.
Two UDim values {1, 2} and {3, 4} look like this: 7f 80 00 80 00 00 00 00 00 00 00 00 00 00 04 08.
The first 8 bytes (7f 80 00 80 00 00 00 00) represent the Scale values of the UDim values. The latter 8 bytes (00 00 00 00 00 00 04 08) represent the Offset values. From there, the values are paired off, so that the first value in each array make up the components of the first UDim, and so on.
UDim2
Type ID 0x07
The UDim2 type is a struct composed of two UDim values, one for each axis:
| Field Name | Format | Value |
|---|---|---|
| X | UDim |
The X component of the UDim2 |
| Y | UDim |
The Y component of the UDim2 |
UDim2 is stored as four arrays of component values in the order X.Scale, Y.Scale, X.Offset, Y.Offset. Each array is separately byte interleaved.
An encoded UDim2 with value {0.75, -30, -1.5, 60} looks like this: 7e 80 00 00 7f 80 00 01 00 00 00 3b 00 00 00 78.
Ray
Type ID 0x08
The Ray type is a struct composed of six little-endian f32 values, making up the components of the Origin and then the Direction of the Ray:
| Field Name | Format | Value |
|---|---|---|
| Origin X | f32 |
The X component of the Origin of the Ray |
| Origin Y | f32 |
The Y component of the Origin of the Ray |
| Origin Z | f32 |
The Z component of the Origin of the Ray |
| Direction X | f32 |
The X component of the Direction of the Ray |
| Direction Y | f32 |
The Y component of the Direction of the Ray |
| Direction Z | f32 |
The Z component of the Direction of the Ray |
The components are stored in order without any additional transformations. When an array of Ray values is present, they’re stored in order but otherwise without transformation.
Faces
Type ID 0x09
The Faces type is a single byte used as a bit field. The low 6 bits represent the Front, Bottom, Left, Back, Top, and Right faces, in that order. The remaining two bits have no meaning. Faces is stored as an array of bytes with no transformations or interleaving.
Three encoded Faces with values Front, Back, Top and Bottom, Left, Right looks like this: 01 18 26.
Axes
Type ID 0x0a
The Axes type is a single byte used as a bit field. The low three bits represent the X, Y, and Z axes, in that order. The remaining five bits have no meaning. Axes is stored as an array of bytes with no transformations or interleaving.
Three encoded Axes with values X, X Y, and X Z look like this: 01 03 05.
BrickColor
Type ID 0x0b
The BrickColor type is a single untransformed big-endian u32 that represents the Number of a BrickColor. When an array of BrickColor values is present, the Numbers are byte interleaved but otherwise are unchanged.
As an example, three encoded BrickColor values Really red (1004), Bright green (37), and Really blue (1010) look like this: 00 00 00 00 00 00 03 00 03 EC 25 F2.
Color3
Type ID 0x0c
The Color3 type is a struct composed of three Float32 values:
| Field Name | Format | Value |
|---|---|---|
| R | Float32 |
The R component of the Color3 |
| G | Float32 |
The G component of the Color3 |
| B | Float32 |
The B component of the Color3 |
Color3 is stored as three arrays of components in the order R, G, B. Each array is separately byte interleaved.
An encoded Color3 with RGB value 255, 180, 20 looks like this: 7f 00 00 00 7e 69 69 6a 7b 41 41 42.
Vector2
Type ID 0x0d
The Vector2 type is a struct composed of two Float32 values:
| Field Name | Format | Value |
|---|---|---|
| X | Float32 |
The X component of the Vector2 |
| Y | Float32 |
The Y component of the Vector2 |
Vector2 is stored as two arrays of components in the order X, Y. Each array is separately byte interleaved.
Two encoded Vector2 values -100.80, 200.55, 200.55, -100.80 look like this: 85 86 93 91 33 19 35 9a 86 85 91 93 19 33 9a 35
Vector3
Type ID 0x0e
The Vector3 type is a struct composed of three Float32 values:
| Field Name | Format | Value |
|---|---|---|
| X | Float32 |
The X component of the Vector3 |
| Y | Float32 |
The Y component of the Vector3 |
| Z | Float32 |
The Z component of the Vector3 |
Vector3 is stored as three arrays of components in the order X, Y, Z. Each array is separately byte interleaved.
Two encoded Vector3 values 1, 2, 3 and -1, -2, -3 look like this: 7F 7F 00 00 00 00 00 01 80 80 00 00 00 00 00 01 80 80 80 80 00 00 00 01.
CFrame
Type ID 0x10
The CFrame type is more complicated than other types. To save space, there are 24 special cases where only the CFrame’s position is saved. The special case’s ID is written as a single byte.
If the byte is 00, a CFrame looks like this:
| Field Name | Format | Value |
|---|---|---|
| ID | u8 |
Always 00 in this case. |
| Orientation | Array of 9 f32 values |
The rotation matrix of the CFrame. Contains the components R00 R01 R02 R10 R11 R12 R20 R21 R22, in that order. |
| Position | Vector3 |
The position of the CFrame. |
In this case, the Orientation field is stored as nine untransformed IEEE-754 standard 32-bit floats.
If the ID is not 00, it will be a value from the following table. In this case, the Orientation field isn’t present and is instead equivalent to the angles paired with the ID in the table. Rotations in this table are in degrees and are applied in the order Y -> X -> Z.
| ID | Rotation | ID | Rotation |
|---|---|---|---|
02 |
(0, 0, 0) | 14 |
(0, 180, 0) |
03 |
(90, 0, 0) | 15 |
(-90, -180, 0) |
05 |
(0, 180, 180) | 17 |
(0, 0, 180) |
06 |
(-90, 0, 0) | 18 |
(90, 180, 0) |
07 |
(0, 180, 90) | 19 |
(0, 0, -90) |
09 |
(0, 90, 90) | 1b |
(0, -90, -90) |
0a |
(0, 0, 90) | 1c |
(0, -180, -90) |
0c |
(0, -90, 90) | 1e |
(0, 90, -90) |
0d |
(-90, -90, 0) | 1f |
(90, 90, 0) |
0e |
(0, -90, 0) | 20 |
(0, 90, 0) |
10 |
(90, -90, 0) | 22 |
(-90, 90, 0) |
11 |
(0, 90, 180) | 23 |
(0, -90, 180) |
When an array of CFrame values is present, for each value the ID is stored followed by the Rotation field if it’s present. Then, an array of Vector3 values that represent the Position field of each CFrame.
Two CFrame values with the components CFrame.new(1, 2, 3) and CFrame.new(4, 5, 6)*CFrame.Angles(7, 8, 9) are stored as 02 00 4B C0 07 3E 08 9C 75 3D 95 46 7D 3F 1D 25 90 BE 58 6C 74 BF 84 C5 C3 3D 1E 4A 73 3F 6F 19 95 BE 9F A6 E0 BD 7F 81 00 00 00 00 00 00 80 7F 00 22 00 D4 00 B2 80 81 80 80 00 00 00 00.
The first part (the ID and Rotation array) is: 02 00 4B C0 07 3E 08 9C 75 3D 95 46 7D 3F 1D 25 90 BE 58 6C 74 BF 84 C5 C3 3D 1E 4A 73 3F 6F 19 95 BE 9F A6 E0 BD, which is an split into 02 and 00 4B C0 07 3E 08 9C 75 3D 95 46 7D 3F 1D 25 90 BE 58 6C 74 BF 84 C5 C3 3D 1E 4A 73 3F 6F 19 95 BE 9F A6 E0 BD.
The second part (the Position array) is: 7F 81 00 00 00 00 00 00 80 7F 00 22 00 D4 00 B2 80 81 80 80 00 00 00 00.
Enum
Type ID 0x12
The Enum type is an unsigned 32-bit integer. It is stored as big endian and is subject to byte interleaving.
Referent
Type ID 0x13
The Referent type represents a specific Instance in the file and is stored as an Int32. After untransforming a Referent, a value of -1 represents the so-called ‘null referent’. In a PROP chunk, a null referent represents a property with no set value: for example, the default value of ObjectValue.Value.
An array of Referent values is stored as an array of Int32 values, and as a result they are subject to byte interleaving. When reading an array of Referent values, they must be read accumulatively. That is to say that the ‘actual’ value of the Referent is the value of the read value plus the preceding one.
Without accumulation, Referent values read from a file may look like this. This is incorrect:
| Referent 1 | Referent 2 | Referent 3 | Referent 4 | Referent 5 | Referent 6 |
|---|---|---|---|---|---|
| 1619 | 1 | 4 | 2 | 3 | 5 |
The correct interpretation of this data, with accumulation, is:
| Referent 1 | Referent 2 | Referent 3 | Referent 4 | Referent 5 | Referent 6 |
|---|---|---|---|---|---|
| 1619 | 1620 | 1624 | 1626 | 1629 | 1634 |
Vector3int16
Type ID 0x14
The Vector3int16 type is stored as three little-endian i16 values:
| Field Name | Format | Value |
|---|---|---|
| X | i16 |
The X component of the Vector3int16 |
| Y | i16 |
The Y component of the Vector3int16 |
| Z | i16 |
The Z component of the Vector3int16 |
Multiple Vector3int16 values are stored in sequence without any transformations or interleaving. Two Vector3int16 values 1, 2, 3 and -1, -2, -3 are stored like this: 00 01 00 02 00 03 FF FF FE FF FD FF.
NumberSequence
Type ID 0x15
The NumberSequence type is stored as a u32 indicating how many NumberSequenceKeypoint values are in the sequence followed by an array of NumberSequenceKeypoint values:
| Field Name | Format | Value |
|---|---|---|
| Keypoint count | u32 |
The number of keypoints in the sequence |
| Keypoints | Array(NumberSequenceKeypoint) |
The data for the keypoints |
NumberSequenceKeypoint is a struct composed of the following fields:
| Field Name | Format | Value |
|---|---|---|
| Time | f32 |
The time for the keypoint |
| Value | f32 |
The value of the keypoint |
| Envelope | f32 |
The envelope for the keypoint |
When multiple NumberSequence values are present, they are stored in sequence with no transformation or interleaving. Two NumberSequence values
NumberSequence.new(
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.5, 1),
NumberSequenceKeypoint.new(1, 1, 0.5)
)
NumberSequence.new(
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.5, 0.5, 0.5),
NumberSequenceKeypoint.new(1, 0.5)
)
look like this: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 00 00 80 3f 00 00 00 00 00 00 80 3f 00 00 80 3f 00 00 00 3f 03 00 00 00 00 00 00 00 00 00 80 3f 00 00 00 00 00 00 00 3f 00 00 00 3f 00 00 00 3f 00 00 80 3f 00 00 00 3f 00 00 00 00
ColorSequence
Type ID 0x16
The ColorSequence type is stored as a u32 indicating how many ColorSequenceKeypoint values are in the ColorSequence followed by an array of ColorSequenceKeypoint values:
| Field Name | Format | Value |
|---|---|---|
| Keypoint count | u32 |
The number of keypoints in the sequence |
| Keypoints | Array(ColorSequenceKeypoint) |
The data for each keypoint |
ColorSequenceKeypoint is a struct composed of the following fields:
| Field Name | Format | Value |
|---|---|---|
| Time | f32 |
The time value of the ColorSequenceKeypoint |
| R | f32 |
The red component of the keypoint’s color value. |
| G | f32 |
The green component of the keypoint’s color value. |
| B | f32 |
The blue component of the keypoint’s color value. |
| Envelope (unused) | f32 |
n/a; serialized, but not used |
When multiple ColorSequence values are present, they are stored in sequence with no transformation or interleaving. Two ColorSequence values
ColorSequence.new(
ColorSequenceKeypoint.new(0, Color3.FromRGB(255, 255, 255)),
ColorSequenceKeypoint.new(0.5, Color3.FromRGB(0, 0, 0)),
ColorSequenceKeypoint.new(1, Color3.FromRGB(255, 255, 255))
)
ColorSequence.new(
ColorSequenceKeypoint.new(0, Color3.FromRGB(255, 0, 0)),
ColorSequenceKeypoint.new(0.5, Color3.FromRGB(0, 255, 0))
ColorSequenceKeypoint.new(1, Color3.FromRGB(0, 0, 255))
)
look like this: 03 00 00 00 00 00 00 00 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 00 00 00 00 00 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 00 00 03 00 00 00 00 00 00 00 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 00 00 00 00 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 80 3f 00 00 00 00.
NumberRange
Type ID 0x17
The NumberRange type is stored as two little-endian floats:
| Field Name | Format | Value |
|---|---|---|
| Min | f32 |
The minimum value of the range |
| Max | f32 |
The maximum value of the range |
Multiple NumberRange values are stored in sequence with no transformation or interleaving. Two NumberRange values NumberRange.new(0, 0.5) and NumberRange.new(0.5, 1) look like this: 00 00 00 00 00 00 00 3f 00 00 00 3f 00 00 80 3f.
Rect
Type ID 0x18
The Rect type is a struct composed of two Vector2 values:
| Field Name | Format | Value |
|---|---|---|
| Min | Vector2 |
The minimum value of the Rect |
| Max | Vector2 |
The maximum value of the Rect |
Rect is stored as four arrays of Float32s in the order Min.X, Min.Y, Max.X, Max.Y. Each array is subject to byte interleaving.
Two encoded Rect values with values Rect.new(-1, -10, 8, 9) and Rect.new(0, 1, 5, 6) look like this: 7f 00 00 00 00 00 01 00 82 7f 40 00 00 00 01 00 82 81 00 40 00 00 00 00 82 81 20 80 00 00 00 00.
PhysicalProperties
Type ID 0x19
The PhysicalProperties type contains a bitfield which indicates whether the value is custom, along with whether it has an AudioAbsorption field. This flag is a single byte. These bits are described below, where bit 0 is the least-significant bit. When there are multiple PhysicalProperties present, they are stored in sequence with no transformations or interleaving.
| Bit Number | Purpose |
|---|---|
0 |
Whether the value is custom or not |
1 |
Whether the value has AcousticAbsorption or not |
If bit 0 is set, then the value is followed by a CustomPhysicalProperties. This is a struct of 6 f32 values:
| Field Name | Format | Value |
|---|---|---|
| Density | f32 |
The density set for the custom physical properties |
| Friction | f32 |
The friction set for the custom physical properties |
| Elasticity | f32 |
The elasticity set for the custom physical properties |
| FrictionWeight | f32 |
The friction weight set for the custom physical properties |
| ElasticityWeight | f32 |
The elasticity weight set for the custom physical properties |
| AcousticAbsorption | f32 |
The acoustic absorption set for the custom physical properties |
The AcousticAbsorption field is only present if bit 1 is set. Otherwise, it is left out and may be assumed to be 1.0 when deserializing a CustomPhysicalProperties.
If bit 0 is not set, but bit 1 is set, there is no CustomPhysicalProperties present.
The PhysicalProperties type is stored as a u8 representing the bitfield, followed by a CustomPhysicalProperties if bit 0 is set.
If you had 4 physical properties with the following characteristics:
- No custom properties set, no
AcousticAbsorption PhysicalProperties.new(0.7, 0.3, 0.5, 1, 1), noAcousticAbsorption- No custom properties set, has
AcousticAbsorption PhysicalProperties.new(0.25, 0.5, 0.125, 1, 0.25, 0.5), hasAcousticAbsorption
They would be serialized as follows: 00 01 33 33 33 3f 9a 99 99 3e 00 00 00 3f 00 00 80 3f 00 00 80 3f 02 03 00 00 80 3e 00 00 00 3f 00 00 00 3e 00 00 80 3f 00 00 80 3e 00 00 00 3f.
- is represented as
00 - is represented as
01 33 33 33 3f 9a 99 99 3e 00 00 00 3f 00 00 80 3f 00 00 80 3f - is represented as
02 - is represented as
03 00 00 80 3e 00 00 00 3f 00 00 00 3e 00 00 80 3f 00 00 80 3e 00 00 00 3f
Color3uint8
Type ID 0x1a
The Color3uint8 type is a struct made up of three bytes, one for each component:
| Field Name | Format | Value |
|---|---|---|
| R | u8 |
The R component of the Color3uint8 |
| G | u8 |
The G component of the Color3uint8 |
| B | u8 |
The B component of the Color3uint8 |
Color3uint8 is stored as three consecutive arrays of components in the order R, G, B. It is not subject to any transformation or byte interleaving.
Two Color3uint8 values with the values 0, 255, 255 and 63, 0, 127, respectively, look like this: 00 3f ff 00 ff 7f.
Int64
Type ID 0x1b
The Int64 type is stored as a big-endian transformed 64-bit integer.
When an array of Int64 values is present, the bytes of the integers are subject to byte interleaving.
SharedString
Type ID 0x1c
SharedString values are stored as an Interleaved Array of big-endian u32 values that represent indices in the SSTR string array.
NetAssetRef properties are stored identically to this format and use the same type ID.
Bytecode
Type ID 0x1d
Bytecode values are stored identically to String properties but contain precompiled Luau bytecode instructions rather than string data.
This data type is disregarded by Roblox Studio and is only loaded by Roblox clients when cryptographically signed. Given that by design it is impossible to generate these signatures, the signing method is not documented in this spec file. For posterity however, the chunk used by Roblox is named SIGN. It is disregarded when loaded by Roblox Studio.
It is highly recommended that implementations do not modify or interpret Bytecode data they encounter and instead simply read and write the data as-is.
Implementors should be aware that running unsigned Bytecode is incredibly unsafe and should not be done unless the source can be validated somehow. Doing so is the equivalent to giving the author of the Bytecode unrestricted access to the system it is ran on.
OptionalCoordinateFrame
Type ID 0x1e
OptionalCoordinateFrame is stored the same way as CFrame, but with a couple interesting differences:
- Immediately following the type ID for
OptionalCoordinateFrameis the type ID forCFrame(10); - At the end of the chunk there is an array of
Boolvalues (preceded by the respective type ID,02) that indicates whichOptionalCoordinateFramevalues have a value.
An OptionalCoordinateFrame with value CFrame.new(0, 0, 1, 0, -1, 0, 1, 0, 0, 0, 0, 1) followed by an OptionalCoordinateFrame with no value looks like this: 10 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7f 00 00 00 00 00 00 00 02 01 00. Note that the valueless OptionalCoordinateFrame is written as the identity CFrame with its corresponding boolean 00 codifying its valuelessness.
UniqueId
Type ID 0x1f
UniqueId is represented as a struct of three numbers in the following order:
| Field Name | Format | Value |
|---|---|---|
| Index | u32 |
A sequential value that’s incremented when a new UniqueId is generated |
| Time | u32 |
The number of seconds since 01-01-2021 |
| Random | i64 |
A psuedo-random number that’s generated semiregularly when initializing a UniqueId |
This struct is stored in the order as written above with no modifications in the binary format.
When interacting with the XML format, care must be taken because UniqueId is stored in a different order and the Random field is modified slightly. For more information, see the relevant documentation in the XML file spec.
When an array of UniqueId values is present, the bytes are subject to byte interleaving.
Font
Type ID 0x20
The Font type is a struct composed of two String values, a u8, and a u16:
| Field Name | Format | Value |
|---|---|---|
| Family | String |
The font family content URI |
| Weight | u16 |
The weight of the font |
| Style | u8 |
The style of the font |
| CachedFaceId | String |
The cached content URI of the TTF file |
The Weight and Style fields are stored as little-endian unsigned integers. These are usually treated like enums, and to assign them in Roblox Studio an Enum is used. Interestingly, the Weight is always stored as a number in binary and XML, but Style is stored as a number in binary and as text in XML.
The CachedFaceId field is always present, but is allowed to be an empty string (a string of length 0). When represented in XML, this property will be omitted if it is an empty string. This property is not visible via any user APIs in Roblox Studio.
Content
Type ID 0x22
Note that the Content type should not be confused with the legacy ContentId type, which was renamed from Content in Roblox release 645.
The Content type is a struct composed of the following fields:
| Field Name | Format | Value |
|---|---|---|
| SourceTypes | Array(Enum) |
A list of types for items in this chunk |
| UriCount | u32 |
Indicates how many items in this chunk are Uris |
| Uris | Array(String) |
A list of the URIs for items in this chunk with that type |
| ObjectCount | u32 |
Indicates how many items in this chunk are Objects |
| ObjectRefs | Array(Ref) |
A list of referents for Objects for items in this chunk |
| ExternalObjectCount | u32 |
Indicates how many items in this chunk are Objects that are external to this file |
| ExternalObjectRefs | Array(Ref) |
A list of referents for Objects for items in this chunk that are external to this file |
SourceTypes is a series of values representing a type for a given Content. At this moment it can have one of three values:
| Name | Value |
|---|---|
None |
0 |
Uri |
1 |
Object |
2 |
Uris is an array UriCount elements long which contains a sequential list of every Uri included in SourceTypes.
ObjectRefs is a referent array that is ObjectCount elements long and contains a list of referents pointing to Objects that can be used for Content, such as EditableImage. ObjectRefs are not populated outside of copy-and-pasting within Studio, but this may change in the future.
ExternalObjectRefs is similar in purpose to ObjectRefs but contains a list of referents that are external to this file. This is used internally by Roblox to minimize memory overhead when copy-and-pasting. Both ExternalObjectCount and ExternalObjectRefs are not applicable to implementors because referents are not static outside of files. They are included for completeness’s sake.
Data Storage Notes
Integer Transformations
Some integers may be subject to a transformation to make them more compressable.
To transform an integer: if x greater than or equal to zero, transform it with 2 * x. Otherwise, use 2 * |x| - 1. In most compilers this is equivalent to (x << 1) ^ (x >> 31) for 32-bit integers. For 64-bit integers, the same format is used but with 63 instead of 31.
To untransform one: if x is divisible by 2, untransform it with x / 2. Otherwise, use -(x + 1) / 2. This is equivalent to (x >> 1) ^ -(x & 1).
Untransforming with bitwise operators requires casting to an unsigned integer in some cases because x >> 1 will result in a negative number if x is negative.
Byte Interleaving
When stored as arrays, some data types have their bytes interleaved to help with compression. Cases where byte interleaving is present are explicitly noted.
When the bytes of an array are interleaved, they’re stored with the first bytes all in sequence, then the second bytes, then the third, and so on. As an example, the sequence A0 A1 B0 B1 C0 C1 is stored as A0 B0 C0 A1 B1 C1.
Viewed another way, it means that the bytes are effectively stored in ‘columns’ rather than ‘rows’. If an array of four 32-bit integers were viewed as a 4x4 matrix, for example, it would normally look like this:
| Column 1 | Column 2 | Column 3 | Column 4 | |
|---|---|---|---|---|
| Row 1 | A0 |
A1 |
A2 |
A3 |
| Row 2 | B0 |
B1 |
B2 |
B3 |
| Row 3 | C0 |
C1 |
C2 |
C3 |
| Row 4 | D0 |
D1 |
D2 |
D3 |
When interleaved, the same array would instead look like this:
| Column 1 | Column 2 | Column 3 | Column 4 | |
|---|---|---|---|---|
| Row 1 | A0 |
B0 |
C0 |
D0 |
| Row 2 | A1 |
B1 |
C1 |
D1 |
| Row 3 | A2 |
B2 |
C2 |
D2 |
| Row 4 | A3 |
B3 |
C3 |
D3 |
Roblox Float Format
Some data types do not follow the IEEE-754 standard format for 32-bit floating point numbers. Instead, they use a proprietary format where the sign bit is after the mantissa.
| Format | Bit Layout |
|---|---|
| Standard | seeeeeee emmmmmmm mmmmmmmm mmmmmmmm |
| Roblox | eeeeeeee mmmmmmmm mmmmmmmm mmmmmmms |
Where s is the sign bit, e is an exponent bit, and m is a mantissa bit.
As a practical example, below is a comparison of how -0.15625 is stored:
| Format | Binary View | Byte View |
|---|---|---|
| Standard | 10111110 00100000 00000000 00000000 |
be 20 00 00 |
| Roblox | 01111100 01000000 00000000 00000001 |
7c 40 00 01 |