Skip to content

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 transformations
  • BufferGeometry - Vertex data and attributes
  • Material - Surface properties and shader parameters
  • Texture - Image references and sampling parameters
  • AnimationClip - Keyframe animation data
  • Skeleton - Bone hierarchies for skinning
SVG
100%

JSON Scene Format

Metadata Structure

Every serialized scene includes a metadata header describing the format version and generator:

FieldTypeDescription
versionnumberFormat version (4.7)
typestring"Object" for scenes, "Geometry", "Material", etc. for individual assets
generatorstringIdentifies the serializing class (e.g., "Object3D.toJSON")

Root Structure

A complete scene serialization produces a hierarchical JSON structure:

SVG
100%

Object3D Serialization

Each Object3D serializes the following properties:

PropertyConditionDescription
uuidAlwaysUnique identifier for object references
typeAlwaysClass name (e.g., "Mesh", "Scene", "Group")
nameIf non-emptyUser-assigned name
matrixAlways16-element array representing local transform
upAlwaysUp vector (default [0,1,0])
pivotIf setPivot point for rotation/scale
castShadowIf trueWhether object casts shadows
receiveShadowIf trueWhether object receives shadows
visibleIf falseVisibility flag
frustumCulledIf falseCulling flag
renderOrderIf non-zeroCustom render sort order
userDataIf non-emptyUser-defined data dictionary
layersAlwaysLayer mask value
matrixAutoUpdateIf falseAuto-update flag
childrenIf has childrenArray 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:

PropertyDescription
uuidUnique identifier
type"BufferGeometry" or subclass
nameOptional name
userDataUser data
dataAttribute 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:

PropertyDescription
uuidUnique identifier
nameOptional name
imageUUID of image in meta.images
mappingTexture coordinate mapping type
channelUV channel index
offset, repeatTransform parameters
wrapWrapping mode (S, T)
format, typeData format
minFilter, magFilterSampling filters

ObjectLoader Pipeline

Loading Flow

The ObjectLoader class orchestrates deserialization through a multi-stage pipeline:

SVG
100%

Parser Methods

Each asset type has a dedicated parser method:

MethodPurposeDependencies
parseShapes()Reconstructs Shape objects for extrusionsNone
parseAnimations()Reconstructs AnimationClip objectsNone
parseGeometries()Reconstructs geometries using BufferGeometryLoaderShapes
parseImages()Loads images via ImageLoader or deserializes dataNone
parseTextures()Reconstructs texturesImages
parseMaterials()Reconstructs materials via MaterialLoaderTextures
parseObject()Recursively reconstructs scene graphAll of the above
parseSkeletons()Reconstructs Skeleton objectsObject 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:

SVG
100%

Helper Function Pattern:

The serialization uses a serialize() helper to check for existing entries:

Image Handling

Images can be serialized in two ways:

  1. URL Reference: String URL to external image file
  2. 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:

SVG
100%

Material Reconstruction

The parseMaterials() method uses MaterialLoader which handles material-specific properties:

Object Hierarchy Reconstruction

The parseObject() method recursively builds the scene graph:

SVG
100%

Skeleton Binding

After the entire object tree is reconstructed, skeletons must be bound to their bones:

  1. Parse Skeletons: Create Skeleton instances from JSON, looking up bone references in the object tree
  2. Bind Skeletons: Associate each SkinnedMesh with its skeleton via UUID reference
  3. 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 LoadingManager with callbacks, images load in background
  • Asynchronous: Uses await with ImageLoader.loadAsync(), waits for all images before returning

Synchronous Path:

Asynchronous Path:

Texture Parsing

The parseTextures() method creates texture instances and configures sampling parameters:

SVG
100%

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 children

Loading 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, isTexture flags for type checking
  • type string 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

  1. Shader Materials: Custom ShaderMaterial and RawShaderMaterial serialize uniforms but not JavaScript functions (e.g., onBeforeCompile)
  2. Node Materials: The native JSON format does not support WebGPU node materials; use NodeMaterialLoader instead
  3. External References: URLs to external images/resources must be accessible at load time
  4. Function References: userData objects cannot contain functions; they will not serialize correctly
  5. Large Geometries: Embedding large vertex buffers as JSON arrays is inefficient; consider using binary formats like GLTF for production