3D scene format
How a Nodaro 3D scene plan is built, from version 1 primitives and keyframes to version 2 GLB assets, semantic entities, camera tracks, shots and edit overlays.
A 3D scene plan is the data Nodaro stores for an editable 3D scene: a composition with planType: "3d-scene" and an explicit schemaVersion. Generate 3D Scene and Edit 3D Scene jobs return one in output_data.scenePlan, Render Video turns one into an MP4, and the 3D scene viewport draws one. This page explains how a plan is organized, so your code can store, validate, show and edit plans.
Rules for every version
- Units and axes. Positions are in meters, in a right-handed world with Y pointing up. Euler rotations are in radians.
- Time. Frames are numbered from zero. A scene lasts from 1 to 60 seconds, at 15 to 60 frames per second; the default is 4 seconds at 24 fps.
- Revisions. Every plan has a
revisionId. An edit never changes a plan: it creates a new revision whoseparentRevisionIdis the revision it came from. Each rendered MP4 uses one exact revision. - Versions. Read
schemaVersionbefore you read any field that belongs to one version. Version 1 describes primitive geometry; version 2 adds baked geometry and shots. - Discovery.
GET /v1/3d-scene/capabilitiestells you which authoring engines, and so which scene versions, a deployment supports. Optional engines depend on the deployment's configuration. The default engine, Basic, produces version 1.
Types and schemas
The @nodaro/shared package exports the types and the validators:
| Export | What it is |
|---|---|
Scene3DPlanV1, Scene3DPlanV2 | The type of each version. |
Scene3DPlan | The union of both versions. |
scene3DAnyPlanSchema | A validator that accepts both versions. |
scene3DPlanSchema | A validator for version 1 only. |
scene3DPlanV2Schema | A validator for version 2 only. |
The validators check the structure and the rules across fields: parent cycles, missing parents, keyframes after the last frame and the duration ceiling. A plan that fails is refused whole.
Version 1: primitives and keyframes
A version 1 scene is self-contained. It describes bounded primitive shapes and groups, including simple character stand-ins, with sparse keyframes for each object and for the camera. It does not reconstruct detailed textured meshes or physical simulations.
A version 1 plan stores:
- object ids, transforms and dimensions;
- the camera's position, target and lens;
- the lighting and the background;
- the keyframes of each object and of the camera.
| Limit | Value |
|---|---|
| Objects per scene | 100 |
| Keyframes per object or per camera track | 240 |
| Length of an object id | 64 characters |
| Frame width and height | 100 to 2,560 px on each axis |
Edit a version 1 scene
POST /v1/3d-scene/edit takes a scenePlan and an expectedRevisionId, plus either an instruction prompt or a list of operations. A revision mismatch is refused. Operations run without calling a model:
| Operation | Fields |
|---|---|
set-object | objectId, and changes to any object field other than the id |
add-object | object |
remove-object | objectId |
set-camera | changes |
set-lighting | changes |
set-background | color |
An operation changes exactly the fields you supply. To change a pose that has keyframes, include the keyframe changes. The whole edited scene is validated again, so an edit cannot leave orphaned parents or references. Use lockedObjectIds to keep objects unchanged during an instruction edit. See Edit 3D Scene.
Version 2: baked geometry
A version 2 scene adds four things to the concepts of version 1:
- Retained GLB geometry, stored as immutable assets.
- Semantic entities, the named parts of the scene that users select and edit.
- A dense camera track, with a camera pose for every frame.
- Contiguous shots, ranges of frames with exact cuts between them.
Assets
A version 2 asset reference holds an opaque assetId, a kind, a role, a byte length and a SHA-256 digest. It holds no storage credential and no download URL.
- A reader needs exactly the referenced bytes. It refuses a missing, oversized or changed asset before it draws anything.
- Playback loads the geometry and the camera data. The native source files of a scene are a separate download, with their own permission.
- Each retained revision pins all its assets, including bytes it reuses from an earlier revision.
Entities
Semantic entities name roots in the GLB geometry and the material roles of those roots, for selection and editing.
- Materials. Every editable material role names a material inside the entity's own geometry. Clay shading keeps each material's base color, so changing a vehicle's body paint leaves its tires unchanged.
- Parents. An entity may declare a parent. Its exported root is then nested inside the parent's root, and its node transform is relative to the parent. The parent's placement and baked animation reach the child through the file itself.
- Agreement. The plan and the GLB file must describe the same parentage. A reader refuses a scene where they disagree.
- Ownership. Geometry, materials and selection belong to the entity that declares them. Recoloring a parent never reaches an entity nested inside it.
- Organizational roots. A parent entity may own no geometry at all, and only carry a baked, possibly animated transform for the entities nested inside it. A reader accepts it as long as something is nested there.
Anchors
An entity anchor is a named point: a stable name and a position in the entity's local space.
- For an asset entity, an optional
nodeNamebinds the anchor to a raw GLB node inside that entity's own root. The position and the optional rotation then use that node's local coordinates, and follow the node's animation, its ancestors and manual edits of the entity. - For example,
{ "name": "door.tip", "nodeName": "car/door.hinge", "position": [1, 0, 0] }places a point one meter along the X axis of the hinge. - A missing node, or a binding into a nested child entity, is refused before playback. Anchors of primitives and groups stay in the entity's local space.
- Whether an authoring engine can create these bindings depends on the engine.
Camera and animation
The dense camera track holds a position, a rotation and a projection for every frame. A reader keeps those values exactly, including roll and exact cuts between shots. Animation is sampled from the requested frame, so scrubbing backward and rendering any single frame give the same pose.
Visibility
An entity's optional visible flag records its baked visibility; when it is missing, the entity is visible.
- Hidden geometry stays loaded and its animation keeps running, so an overlay can show it again at once.
- An overlay takes precedence over the baked value, and removing the overlay restores the baked value.
- A hidden parent also hides everything nested inside it.
- Hidden objects do not catch clicks in the preview. Use the entity list to select them and show them.
- The baked visibility is part of the revision's content digest.
Edit a version 2 scene with overlays
Version 2 edits are immutable overlays above the baked scene. There are four kinds: transform, material color, visibility, and shot camera offset. An overlay never touches the base geometry or the camera bytes.
- A transform overlay applies above the baked placement of the entity and of its ancestors. Its
spacesays in which frame of reference its values are read:localmeans the entity's own parent frame, baked placement included. The values stay constant there, and the edit travels with a moving parent.worldmeans the scene's axes. The values are the entity's world position, rotation and scale; a rotated, scaled or animated ancestor changes where the entity ends up, never what the numbers mean. The entity stays parented, keeps its own baked animation, and only the channels the edit names are held.- A reader refuses the one case neither space can express: an ancestor with non-uniform scale under a rotation, which leaves a transform that is not a position, a rotation and a scale.
- A visibility overlay hides the entity and everything nested inside it. Each entity keeps its own visibility, so showing the ancestor again restores exactly the descendants that were not hidden in their own right.
- Every edit creates a new revision, with a parent revision and a new content digest. Locks on entities are enforced, and an edit made against an outdated revision or digest is refused.
- Derived files are not carried over. Posters, validation reports and native downloads are made again for the edited revision before they are attached to it.
To save overlays without a generation job and without an LLM charge, call POST /v1/3d-scene/revisions/:revisionId/edits with a newRevisionId, the base expectedContentHash, the operations and optional lockedObjectIds, or use client.scene3d.applyEdits() in the SDK. For OAuth apps, it needs the workflows:write scope. The 3D scene viewport produces these operations for you.
Rendering
The browser preview and the MP4 render use the same reader, so a scene looks the same in both. Version 2 accepts clay geometry and rigid animation; textured, skinned and morph-target assets are refused.
| Version 2 limit | Value |
|---|---|
| Semantic entities | 100 |
| Mesh nodes | 2,000 |
| Triangles | 200,000 |
| Shots | 32 |
| Assets | 64 |
| Overrides | 200 |
| Playback asset bytes | 64 MiB |
The schema also bounds the timing, the dimensions, the depth of the hierarchy and the size of the manifest. A render's price depends on the frame size of the plan; see Render Video.
Imported GLBs
A new scene can start from GLB files that already exist in retained revisions. On POST /v1/3d-scene/generate, inputAssets selects up to eight of them, each as { id, revisionId, assetId, label? }. Image and video references stay a separate field.
- Imports need an authoring engine with import support. Basic, and engines without imports, refuse them before charging.
- You send only ids. Nodaro checks your access to the exact revision, and supplies the digest and byte length itself. A URL or a receipt from the caller is refused.
- When the engine reads an imported GLB, Nodaro checks access again and issues a short-lived download grant. Grants are transport credentials: they are never part of the scene plan.
- A saved revision can keep private copies of the files it was built from. They belong to the revision's owner, stay pinned through manual edits, and never appear among the playback assets or the public downloads.
Read the binary files over the API
A version 2 manifest names its assets, but the bytes come from the authenticated API, with ordinary bearer authentication:
| Method | Path | What it returns |
|---|---|---|
GET | /v1/3d-scene/revisions/:revisionId | The scene manifest and its asset descriptors. |
GET | /v1/3d-scene/revisions/:revisionId/assets/:assetId | One playback asset, such as a GLB or the camera track. |
GET | /v1/3d-scene/revisions/:revisionId/source | The editable native source, when the revision retains one. |
- Playback assets need permission to view the revision's workflow. The native source needs permission to edit it. Personal revisions are readable by their owner only.
- A deleted or inaccessible revision answers
404, and every response carriesCache-Control: no-store. - In the SDK,
client.scene3d.assetBytes(revisionId, asset)andclient.scene3d.sourceBytes(revisionId)return anArrayBuffer, and check the declared length.
See the 3D scenes API for the generation, edit and render endpoints.
Frequently asked questions
Related
Embed the 3D scene viewport
Generate 3D Scene
Edit 3D Scene
3D scenes
Last updated on
Embed the 3D scene viewport
Frame Nodaro's 3D scene viewport in your app and drive it with postMessage, from the handshake and state messages to edit events, asset transport and limits.
External wallets
Connect a dedicated Nodaro Cloud deployment to your own shared wallet, which reserves, settles and reports each customer's credits across all your products.