Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch
Embeds

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 whose parentRevisionId is the revision it came from. Each rendered MP4 uses one exact revision.
  • Versions. Read schemaVersion before 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/capabilities tells 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:

ExportWhat it is
Scene3DPlanV1, Scene3DPlanV2The type of each version.
Scene3DPlanThe union of both versions.
scene3DAnyPlanSchemaA validator that accepts both versions.
scene3DPlanSchemaA validator for version 1 only.
scene3DPlanV2SchemaA 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.
LimitValue
Objects per scene100
Keyframes per object or per camera track240
Length of an object id64 characters
Frame width and height100 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:

OperationFields
set-objectobjectId, and changes to any object field other than the id
add-objectobject
remove-objectobjectId
set-camerachanges
set-lightingchanges
set-backgroundcolor

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 nodeName binds 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 space says in which frame of reference its values are read:
    • local means the entity's own parent frame, baked placement included. The values stay constant there, and the edit travels with a moving parent.
    • world means 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 limitValue
Semantic entities100
Mesh nodes2,000
Triangles200,000
Shots32
Assets64
Overrides200
Playback asset bytes64 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:

MethodPathWhat it returns
GET/v1/3d-scene/revisions/:revisionIdThe scene manifest and its asset descriptors.
GET/v1/3d-scene/revisions/:revisionId/assets/:assetIdOne playback asset, such as a GLB or the camera track.
GET/v1/3d-scene/revisions/:revisionId/sourceThe 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 carries Cache-Control: no-store.
  • In the SDK, client.scene3d.assetBytes(revisionId, asset) and client.scene3d.sourceBytes(revisionId) return an ArrayBuffer, and check the declared length.

See the 3D scenes API for the generation, edit and render endpoints.

Frequently asked questions

Last updated on

On this page