Scene Serialization & Loading
Purpose and Scope
This document describes Three.js's native JSON serialization and deserialization system for scene graphs, which enables saving and loading complete 3D scenes including object hierarchies, geometries, materials, textures, animations, and skeletal rigs. This system is primarily implemented through the toJSON() methods on various classes and the ObjectLoader class.
For loading external 3D file formats like GLTF or FBX, see GLTF Import & Export and Additional Format Loaders. For runtime geometry generation, see Procedural Geometry Generation.
Serialization Architecture
The toJSON Pattern
Three.js uses a consistent toJSON(meta) pattern across its core classes. Each serializable class implements this method to convert its state into a JSON-compatible object. The meta parameter is a shared object that accumulates all referenced assets during serialization to avoid duplication.
Key Classes with toJSON:
Object3D- Scene graph nodes and transformationsBufferGeometry- Vertex data and attributesMaterial- Surface properties and shader parametersTexture- Image references and sampling parametersAnimationClip- Keyframe animation dataSkeleton- Bone hierarchies for skinning
JSON Scene Format
Metadata Structure
Every serialized scene includes a metadata header describing the format version and generator:
| Field | Type | Description |
|---|---|---|
| version | number | Format version (4.7) |
| type | string | "Object" for scenes, "Geometry", "Material", etc. for individual assets |
| generator | string | Identifies the serializing class (e.g., "Object3D.toJSON") |
Root Structure
A complete scene serialization produces a hierarchical JSON structure:
Object3D Serialization
Each Object3D serializes the following properties:
| Property | Condition | Description |
|---|---|---|
| uuid | Always | Unique identifier for object references |
| type | Always | Class name (e.g., "Mesh", "Scene", "Group") |
| name | If non-empty | User-assigned name |
| matrix | Always | 16-element array representing local transform |
| up | Always | Up vector (default [0,1,0]) |
| pivot | If set | Pivot point for rotation/scale |
| castShadow | If true | Whether object casts shadows |
| receiveShadow | If true | Whether object receives shadows |
| visible | If false | Visibility flag |
| frustumCulled | If false | Culling flag |
| renderOrder | If non-zero | Custom render sort order |
| userData | If non-empty | User-defined data dictionary |
| layers | Always | Layer mask value |
| matrixAutoUpdate | If false | Auto-update flag |
| children | If has children | Array of child object data |
Special Object Types
InstancedMesh
Serializes instance-specific data:
{
"type": "InstancedMesh",
"count": number,
"instanceMatrix": { ... },
"instanceColor": { ... } // optional
}BatchedMesh
Serializes batched rendering data including draw ranges, geometry info, and instance info:
Scene
Serializes scene-specific properties like background, environment, fog, and lighting intensity:
BufferGeometry Serialization
Geometries serialize vertex data and attributes:
| Property | Description |
|---|---|
| uuid | Unique identifier |
| type | "BufferGeometry" or subclass |
| name | Optional name |
| userData | User data |
| data | Attribute data with index, attributes, morphAttributes, groups |
Material Serialization
Materials serialize all shader-relevant properties, blending modes, and texture references (by UUID):
Texture Serialization
Textures serialize sampling parameters and reference their image source:
| Property | Description |
|---|---|
| uuid | Unique identifier |
| name | Optional name |
| image | UUID of image in meta.images |
| mapping | Texture coordinate mapping type |
| channel | UV channel index |
| offset, repeat | Transform parameters |
| wrap | Wrapping mode (S, T) |
| format, type | Data format |
| minFilter, magFilter | Sampling filters |
ObjectLoader Pipeline
Loading Flow
The ObjectLoader class orchestrates deserialization through a multi-stage pipeline:
Parser Methods
Each asset type has a dedicated parser method:
| Method | Purpose | Dependencies |
|---|---|---|
| parseShapes() | Reconstructs Shape objects for extrusions | None |
| parseAnimations() | Reconstructs AnimationClip objects | None |
| parseGeometries() | Reconstructs geometries using BufferGeometryLoader | Shapes |
| parseImages() | Loads images via ImageLoader or deserializes data | None |
| parseTextures() | Reconstructs textures | Images |
| parseMaterials() | Reconstructs materials via MaterialLoader | Textures |
| parseObject() | Recursively reconstructs scene graph | All of the above |
| parseSkeletons() | Reconstructs Skeleton objects | Object tree (for bone lookup) |
Asset Reference Management
UUID-Based Deduplication
Three.js uses UUIDs to ensure each asset is serialized only once, even if referenced multiple times. The meta object acts as a shared cache during serialization:
Helper Function Pattern:
The serialization uses a serialize() helper to check for existing entries:
Image Handling
Images can be serialized in two ways:
- URL Reference: String URL to external image file
- Data URL or Typed Array: Embedded image data
During deserialization, ImageLoader handles URL loading while data arrays are reconstructed directly:
Deserialization Process
Geometry Reconstruction
The parseGeometries() method uses BufferGeometryLoader to recreate geometry instances:
Material Reconstruction
The parseMaterials() method uses MaterialLoader which handles material-specific properties:
Object Hierarchy Reconstruction
The parseObject() method recursively builds the scene graph:
Skeleton Binding
After the entire object tree is reconstructed, skeletons must be bound to their bones:
- Parse Skeletons: Create
Skeletoninstances from JSON, looking up bone references in the object tree - Bind Skeletons: Associate each
SkinnedMeshwith its skeleton via UUID reference - Bind Light Targets: Resolve light target references (for directional/spot lights)
Texture and Image Loading
Synchronous vs Asynchronous Parsing
The ObjectLoader provides both synchronous (parse()) and asynchronous (parseAsync()) methods. The key difference is image loading:
- Synchronous: Uses
LoadingManagerwith callbacks, images load in background - Asynchronous: Uses
awaitwithImageLoader.loadAsync(), waits for all images before returning
Synchronous Path:
Asynchronous Path:
Texture Parsing
The parseTextures() method creates texture instances and configures sampling parameters:
Usage Examples
Serializing a Scene
// Assuming 'scene' is a Scene object with meshes, lights, etc.
const json = scene.toJSON();
// The result contains all assets deduplicated by UUID
console.log(json.geometries); // { uuid1: {...}, uuid2: {...} }
console.log(json.materials); // { uuid1: {...}, uuid2: {...} }
console.log(json.object); // Root scene object with childrenLoading a Scene
const loader = new ObjectLoader();
// Async loading
const scene = await loader.loadAsync('path/to/scene.json');
// Or with callbacks
loader.load('path/to/scene.json', (object) => {
scene.add(object);
});Implementation Details
Type System Integration
The serialization system relies on type flags to distinguish object classes during deserialization:
isObject3D,isBufferGeometry,isMaterial,isTextureflags for type checkingtypestring property for exact class identification (e.g., "Mesh", "PerspectiveCamera")
Matrix Serialization
Transformations are serialized as 16-element arrays representing 4x4 matrices:
object.matrix = [
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44
];During deserialization, matrices are reconstructed using fromArray():
BufferAttribute Serialization
Vertex attributes are serialized with typed array data:
{
"itemSize": 3,
"type": "Float32Array",
"array": [x1, y1, z1, x2, y2, z2, ...],
"normalized": false
}Limitations and Considerations
- Shader Materials: Custom
ShaderMaterialandRawShaderMaterialserialize uniforms but not JavaScript functions (e.g.,onBeforeCompile) - Node Materials: The native JSON format does not support WebGPU node materials; use
NodeMaterialLoaderinstead - External References: URLs to external images/resources must be accessible at load time
- Function References:
userDataobjects cannot contain functions; they will not serialize correctly - Large Geometries: Embedding large vertex buffers as JSON arrays is inefficient; consider using binary formats like GLTF for production