<!-- Generated from reference/binding-builder.mdx; edit the MDX source. -->

Package: @liveroom-tech/react-immersive@5.2.0
Canonical: https://react-immersive.liveroom.dev/docs/reference/binding-builder

# `BindingBuilder`

A design-time helper component for authoring `objectBindings` and `sceneConfig` in the browser. Load a GLB, GLTF, OBJ, FBX, USDZ, STL, or PLY asset, inspect its mesh nodes, edit binding fields, configure the scene, preview the result live, and export as JSON or TypeScript.

Try the public editor at [react-immersive.liveroom.dev/editor](https://react-immersive.liveroom.dev/editor).

## Usage

```tsx
import "@liveroom-tech/react-immersive/binding-builder.css";
import { BindingBuilder } from "@liveroom-tech/react-immersive/binding-builder";

export default function App() {
  return <BindingBuilder licenseKey="your_license_key" />;
}
```

## Props

```ts
type BindingBuilderProps = {
  licenseKey: string;
  dracoDecoderPath?: string | false;
  ktx2TranscoderPath?: string | false;
  meshopt?: boolean;
};
```

`licenseKey` is **required** and is validated the same way as [`ModelViewer`](https://react-immersive.liveroom.dev/docs/reference/model-viewer)'s, see [Licensing & plan tiers](https://react-immersive.liveroom.dev/docs/reference/binding-builder#licensing--plan-tiers) below. While the key is being checked, `BindingBuilder` renders a "Verifying license…" placeholder; if the key is invalid, it renders an error message instead of the editor.

The decoder props are forwarded to the editor preview. Built-in defaults support
Meshopt, DRACO, and KTX2; override them with self-hosted directories or `false`.
See the
[`ModelViewer` decoder setup](https://react-immersive.liveroom.dev/docs/reference/model-viewer#dracodecoderpath--ktx2transcoderpath--meshopt).

## Capabilities

### Object tab

- Upload a `.glb`, standalone/data-URI `.gltf`, `.obj`, `.fbx`, `.usdz`, `.stl`, `.ply`, or `.zip` bundle. ZIP bundles may contain GLTF dependencies, OBJ + MTL/textures, or FBX external textures; USDZ is uploaded directly
- Traverse renderable mesh nodes and generate starter bindings automatically
- For an object without texture coordinates (STL, most PLY), the **Material**
  image map fields are turned off with a note, and the starter actions leave out
  **Change Material** (see [STL and PLY](https://react-immersive.liveroom.dev/docs/reference/model-viewer#stl-and-ply))
- Edit binding fields: identity, label, type, status, booleans, style, actions, metrics, metadata, and saved camera state
- Add decals from the **Decals** panel: upload an image or add text to place it
  automatically at the centre of the camera-facing surface. Set its size,
  rotation, and opacity; **Move** lets you drag it across the object's surface.
  Release to save, or press Escape to cancel. The **Position** arrows move
  the decal in small steps on the visible surface relative to the camera;
  click for a fine adjustment or hold an arrow to keep moving. Each step
  can be undone. Uploaded images are exported
  under `materials/` with the bindings
- Move and rotate the selected object with a geometry-centered pivot, World or
  Local axes, numeric position/rotation fields, and restore-to-authored-transform
  support
- Author a full per-object material in the **Material** panel, a one-click
  preset gallery (wood, metal, chrome, glass, plastic, fabric, ceramic,
  concrete, etc.) plus collapsible sub-panels for PBR maps, roughness,
  emission, opacity, normal & bump, displacement, ambient occlusion,
  clearcoat, sheen, anisotropy, transmission & volume, and faces rendering.
  Each sub-panel has an enable toggle, and every field maps to a
  `style.material` override (see
  [`ObjectBindingMaterial`](https://react-immersive.liveroom.dev/docs/reference/types#objectbindingmaterial))
- Undo/redo the current bindings (toolbar buttons, or `Cmd/Ctrl+Z` /
  `Cmd/Ctrl+Shift+Z` while the Object tab is active), rapid edits like
  slider drags coalesce into a single undo step
- Import a previously exported `objectBindings.json` and merge it back onto
  the current model (matched by `modelObjectId`, so re-imports survive
  hand edits or partial files)
- Export the current bindings as **JSON** or **TypeScript**

  Model ZIPs are limited to 128 MiB compressed, 1,024 entries, 128 MiB per
  expanded entry, 256 MiB expanded in total, and a 200:1 per-entry compression
  ratio. Every GLTF dependency must be included in the archive: absolute,
  root-relative, and other external asset URLs are rejected. `data:` URIs are
  allowed.

### Scene tab

In **Annotations**, edit a marker's title, description, category, image URLs and
alt text, and link URLs and labels. **Capture current view** saves the preview
camera, **Preview** recalls it, and **Clear view** uses automatic tour framing.
New markers use automatic close-up zoom until you explicitly capture a custom
view. Optional minimum and maximum distances hide markers outside the chosen
range (scene units); blank fields mean no limit. These fields persist in exported
`sceneConfig.annotations`.

- Configure scene-wide settings (lighting, environment, background, ground
  shadows, wireframe, post-processing, annotations, per-clip animation options,
  and a cinematic camera path), the same `SceneConfig` shape consumed by
  `ModelViewer`'s `sceneConfig` prop
- Configure scene auto-rotation and speed. These values are exported as
  `sceneConfig.model.autoRotate` and `sceneConfig.model.autoRotateSpeed`
- Isolate a node from the Scene Nodes list (the button at the end of its row)
  to see it on its own in the preview while you edit it. This is only for
  editing and isn't exported
- Set up a section cut under **General → Section Cut** (axis, position, and
  which side stays), exported as `sceneConfig.section`. The preview has the
  section toolbar and a plane you can drag
- Build style rules in the **Rules** sub-tab: pick the objects (all, a group,
  tag, type, or one object), the conditions (metric ranges, status, metadata
  values), and the look (colour, opacity, glow, a colour scale from a metric,
  or a pulse). The form suggests the model's own groups, tags, metric names,
  and metadata keys, shows how many objects each rule matches, and the preview
  updates as you edit. Exported as `sceneConfig.styleRules`; see
  [Style rules](https://react-immersive.liveroom.dev/docs/reference/model-viewer#style-rules)
- Check your changes with **Compare** above the preview: a divider across the
  preview shows the project as it was opened on its left and as it is now on
  its right. Drag it (or focus it and use the arrow keys) to sweep between
  them; orbiting either side moves both. **Pin**, on the "Before" label, makes
  the current state the "before", to compare the next round of changes with it. The
  preview stays live while comparing, and the "before" side is view only. It
  is the same slider as [`CompareViewer`](https://react-immersive.liveroom.dev/docs/reference/compare-viewer)
- Pick which of the model's glTF material variants (`KHR_materials_variants`)
  the scene starts with (**General**, one select per option group, such as
  **Paint** and **Wheels**), exported as `sceneConfig.model.activeVariants`.
  The preview's swatch rows change the same setting
- Position and rotate directional, point, and spot lights with transform
  gizmos; spot lights also expose angle and penumbra controls
- Switch the model renderer between **PBR**, **Matcap**, and **UV Checker**.
  UV Checker overlays a numbered directional chart to diagnose stretching,
  seams, rotation, texel-density differences, and missing UV0 coordinates
- Author a **cinematic camera path** visually in the **Cinematic** sub-tab:
  frame the model in the preview, click **Add waypoint from current view** to
  capture the camera pose, then set its segment duration, reorder,
  fly-to-preview (📍), or delete waypoints. Toggle Enable / Loop / Auto-play and
  the overall glide Duration. The path
  is stored on `sceneConfig.cinematic` and replayed by
  [`ModelViewer`'s `cinematic` prop](https://react-immersive.liveroom.dev/docs/reference/model-viewer#cinematic)
- Undo/redo the scene config independently of the Object tab's bindings
  history
- Import a previously exported `sceneConfig.json`, merged by top-level
  section so a partial file doesn't wipe sections it doesn't include
- Export the current scene config as **JSON** or **TypeScript**

### Preview

- Live preview of the selected node in an embedded `ModelViewer`, including
  loading progress and orbit/pointer-lock camera modes

### Autosave and restoring a session

`BindingBuilder` keeps your work in the browser, so a reload or a closed tab
doesn't lose it:

- Once you change something, the session is saved shortly after each edit, and
  again when the tab is hidden or closed. A session is the model, its object
  bindings, and the scene settings. An uploaded model file is saved with it, so
  it doesn't need uploading again.
- **Restore last session** (in the "⋯" menu above the preview) reopens the
  saved session. One session is kept per browser, and each save replaces
  the last.
- While there are edits on screen, leaving or reloading the page asks for
  confirmation first. Loading or restoring a model without changing it doesn't.
- Sessions live in this browser's IndexedDB, in a database named
  `react-immersive:binding-builder`. They aren't shared across browsers or
  devices, and private browsing may discard them. If the browser refuses to
  store a session, usually for lack of space, `BindingBuilder` says so; export
  your work to keep it.

Autosave is a safety net, not a way to keep or share work: export JSON or
TypeScript to keep bindings and scene settings with your application.

### Moving and rotating objects

Open the **Move & Rotate** panel in the Object tab, then:

1. Turn on **Transform gizmo**. Camera controls pause while editing is enabled.
2. Select a bound mesh in the preview or object list.
3. Drag an arrow to move on one axis, a plane handle to move on two axes, or a
   colored ring to rotate. Move and rotate handles are always shown together.
4. Choose **World** to keep the handles aligned with the scene, or **Local** to
   align them with the selected object's current orientation.
5. Use the horizontal X, Y, and Z fields for precise values. Rotation fields
   display degrees; the exported binding stores radians.

The pivot is placed at the center of the selected object's renderable geometry,
so rotation happens around the object itself even when the source node's
authored origin is elsewhere. This authoring pivot does not rewrite the
GLB/GLTF origin.

Both gizmo and numeric edits are saved to the selected binding's local-space
`transform.position` and `transform.rotation`. Changing World/Local axis space
only changes the direction of the gizmo handles; it does not change the saved
position. Use **Restore original position & rotation** to remove those
overrides and return to the model-authored transform.

## Exporting and Reusing Files

BindingBuilder has separate toolbars for bindings and scene settings, so export
the data from the tab that owns it:

1. Load your model first. The import and export controls become available once
   BindingBuilder has inspected its nodes.
2. Open the **Object** tab to work with `objectBindings`. In that tab's toolbar,
   use **Export → JSON** or **Export → TypeScript**.
3. Open the **Scene** tab to work with `sceneConfig`. Its toolbar has the same
   **Export** menu, producing `sceneConfig.json` or `sceneConfig.ts`.
4. Choose **TypeScript** for a file you will import into your application.
   Choose **JSON** for a portable snapshot that you want to bring back into
   BindingBuilder later.

We recommend keeping TypeScript exports alongside the viewer route, for example:

```text
src/immersive/objectBindings.ts
src/immersive/sceneConfig.ts
public/model.glb
public/materials/... (when the export includes uploaded textures)
```

The TypeScript bindings export already wraps the record in `defineObjectBindings`, so TypeScript validates the complete file without widening its keys or literal IDs. Import the named exports directly and pass them to `ModelViewer`:

```tsx
import { useState } from "react";
import { ModelViewer, type ObjectBinding } from "@liveroom-tech/react-immersive";
import { objectBindings as initialObjectBindings } from "./objectBindings";
import { sceneConfig } from "./sceneConfig";

export default function ProductViewer() {
  const [objectBindings, setObjectBindings] = useState<Record<string, ObjectBinding>>(initialObjectBindings);

  return (
    <ModelViewer
      modelUrl="/model.glb"
      licenseKey="your-license-key"
      objectBindings={objectBindings}
      sceneConfig={sceneConfig}
      onObjectBindingsChange={setObjectBindings}
    />
  );
}
```

### Importing Back into BindingBuilder

Use the **Import** button in the same toolbar as the matching export:

- **Object tab → Import** accepts an `objectBindings.json` file. Load the model
  first; incoming records are matched by `modelObjectId`, so bindings for nodes
  absent from the current model are safely skipped.
- **Scene tab → Import** accepts a `sceneConfig.json` file. Included top-level
  sections are merged into the current scene config, which lets you restore a
  partial scene preset without resetting the other settings.

TypeScript exports are deliberately for your application source code, not for
the editor's import button. Keep a JSON export whenever you expect to reopen or
share the configuration in BindingBuilder.

  **Picking a texture map** stores the durable path `materials/<filename>` in
  the binding (this is what's exported), and shows the image immediately in
  the live preview via an in-memory `blob:` URL, so you can author without
  first copying files into `public/`.

**Exporting bundles the files automatically:** if any material field was
filled by uploading a file, clicking **Export**
downloads a `.zip` instead of a single `.json`/`.ts`, containing the
export file plus a `materials/` folder with every uploaded image. Unzip it
into your app's `public/` folder and the exported bindings resolve as-is.
Two different uploads that happen to share a filename are kept distinct
(`materials/wood.jpg`, `materials/wood-2.jpg`, ...) rather than one
overwriting the other. If nothing was uploaded, Export downloads a plain
`.json`/`.ts` file exactly as before.

## Licensing & plan tiers

`BindingBuilder` validates `licenseKey` the same way `ModelViewer` does, and additionally gates some editor features by the license's plan tier:

| Tier      | Unlocks                                                                                                                                                                                                  |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `free`    | Base color, metalness/roughness sliders, basic scene settings                                                                                                                                            |
| `starter` | + Texture maps, the specular workflow, reflectivity, environment lighting, environment backgrounds, wireframe preview, animation configuration, annotations, cinematic camera paths, style rules, decals |
| `growth`  | + Post-processing effects                                                                                                                                                                                |

A locked feature is shown in place (not hidden) with a message naming the required plan, so the feature stays discoverable while editing. Get a license key at the [React Immersive Console](https://react-immersive.liveroom.dev/console).

## Typical Workflow

1. Mount `<BindingBuilder />` in a dev-only route (e.g. `/dev/bindings`)
2. Upload your `.glb`, `.gltf`, `.obj`, `.fbx`, `.usdz`, `.stl`, `.ply`, or `.zip`
3. Select mesh nodes in the sidebar, edit their binding fields, move or rotate
   them as needed, or apply a material preset
4. Configure scene-wide settings in the **Scene** tab
5. Preview the interactive result in the embedded viewer
6. Click **Export → JSON** or **Export → TypeScript** to download the bindings (and, if you uploaded any textures, a `.zip` with them included)
7. Drop the exported file (and any `materials/` folder) into your app as the initial `objectBindings` / `sceneConfig` state
