Introduction
@liveroom-tech/react-immersive is a React-based 3D model viewer for interactive GLB/GLTF assets. It renders a model with React Three Fiber, lets users click named meshes, shows a built-in side panel for the selected object, and supports per-object actions such as color changes, texture uploads, and visibility toggles.
Why This Library Exists
Rendering a GLB/GLTF model in Three.js/React Three Fiber is the easy part - useGLTF plus a <primitive> gets you a static model in a few lines. The hard part is everything layered on top once a model needs to be interactive and configurable:
- Click a part of the model to show details about it
- Let a user change a part’s color or swap its texture
- Toggle parts on/off (e.g. show/hide furniture, accessories, room walls)
- Save a “good viewing angle” for each part and snap the camera to it
- Track hover/selection state, animations, loading state, and camera state - all the bookkeeping around the 3D scene
Every project that does this ends up rebuilding the same scaffolding: material cloning so edits don’t bleed across objects, texture caching/disposal, raycasting and highlighting, side panels, color pickers, popovers, and animation mixers. react-immersive packages all of that once so consuming apps don’t have to reinvent it.
Why objectBindings Exists
objectBindings is the answer to one core problem: a GLB/GLTF file is just geometry, it has no concept of “this mesh is configurable, has these actions, and looks like this.” Something has to bridge the gap between raw mesh node names (e.g. "CarBody", "BrickWall") and your application’s domain model (a car’s body panel, a room’s wall, etc.).
objectBindings is that bridge, a plain JS/TS record, keyed by mesh node name, that:
- Declares which meshes are interactive (
selectable,hoverable,visible), meshes not listed aren’t part of the interactive flow at all - Carries live render state (
visible, localtransform.position/transform.rotation, plusstyle.material.*overrides likebaseColor,texture.path,opacity,metalness,roughness,emissive, and the full PBR/texture-map set) so the viewer can apply overrides without you touching Three.js objects directly - Defines per-object actions (
change-color,change-material,toggle-visibility, etc.) that drive the built-in side panel buttons - Stores domain data (
metrics,metadata) for display alongside the 3D view - Saves camera framing (
cameraState) so clicking an object can snap to a curated view instead of a generic “fit to box”
Because it’s just a serializable record, it can be authored by hand, generated automatically (via BindingBuilder, which traverses a GLB/GLTF asset and produces starter bindings), stored in a database, and round-tripped through onObjectBindingsChange to keep your app state and the 3D scene in sync. objectBindings is the single source of truth, so React’s normal data flow (“state in, callbacks out”) works for a 3D scene the same way it would for any other component.
Why It Is Worth The Setup
At first glance, objectBindings can look like extra work compared to simply
loading a model and rendering it. That instinct is understandable, but it only
holds if the model is purely visual.
If the model is part of a real product experience, the “extra work” is actually the structure that prevents everything else from turning into ad-hoc scene mutation:
- Without bindings, a model is just a rendered asset
- With bindings, the model becomes part of your application state
That distinction is what makes it possible to build maintainable 3D product flows. Instead of scattering logic across mesh refs, custom raycasters, material mutations, and one-off UI handlers, you keep the important decisions in a single serializable record.
In other words, objectBindings is not extra work for rendering. It is the
abstraction that makes interaction, customization, persistence, and app-level
logic manageable after rendering.
If you only need to display a model, you probably do not need
objectBindings. If you need the model to behave like part of your
app, bindings are the right tradeoff.
Why sceneConfig Exists
objectBindings describe what individual model nodes mean. sceneConfig
describes the experience around the whole model: the initial camera view,
lighting, environment, shadows, post-processing, animation behavior, and
annotation markers. It keeps scene-wide decisions out of component props,
ad-hoc Three.js setup, and one-off effects.
That separation matters because not every interactive experience is about a
specific mesh. A guided showroom tour, for example, can use annotation markers
and saved camera positions to lead a visitor through a scene without binding a
single exhibit. The Guided Tour demo uses exactly
this pattern.
- Guided tours and onboarding - define named stops and camera positions for a repeatable walkthrough
- Showrooms and product launches - set the intended lighting, environment, and opening view for a model
- Architecture and real estate - ship curated viewpoints, daylight mood, and annotations with a building or room
- Training and safety flows - combine markers, camera positions, and animation playback into a consistent lesson
- XR and device experiences - keep scene-level presentation and interaction settings together as the experience evolves
- Reusable model presets - save a serializable scene setup that can be versioned, stored, and reused across applications
In short: objectBindings make parts of a model behave like application data;
sceneConfig makes the model behave like a designed 3D experience. Use one or
both depending on what the experience needs.
Use sceneConfig whenever the default viewer presentation is not
the product you want to ship. It is valuable on its own for tours and curated
scenes, and it complements objectBindings when individual parts
also need application behavior.
When This Package Fits
- Product configurators - let users recolor or retexture parts of a car, piece of furniture, or apparel
- Interactive room/scene tours - click walls, furniture, or fixtures to see info or toggle visibility
- Asset inspection tools -
SimpleModelViewerfor quickly browsing a GLB/GLTF mesh hierarchy without any binding setup, including an optional local upload mode for ad-hoc inspection - Anything needing “click a 3D part to show a panel with data and actions” without hand-rolling raycasting, highlighting, and UI panels
Example Applications Where Bindings Shine
- Product configurators - cars, furniture, kitchens, appliances, apparel, or footwear where each part can be selected, recolored, retextured, upgraded, or hidden
- E-commerce customization flows - “build your own” experiences where a mesh maps to a product option, add-on, or purchasable variant
- CPQ and sales tools - enterprise configuration flows where 3D parts correspond to real options, compatibility rules, and pricing decisions
- Interior design and real estate tools - rooms, walls, fixtures, finishes, and furniture that need labels, toggles, metadata, and saved viewpoints
- Industrial and engineering viewers - machines, assemblies, equipment, and plant layouts where users isolate parts, inspect metadata, and follow maintenance flows
- Training and guided walkthroughs - medical, hardware, aviation, or manufacturing education where each part can own annotations, steps, and camera presets
- Digital twins and operational dashboards - buildings, assets, or devices where visual parts correspond to live entities with status, metrics, and alerts
- Smart building controls - building models where light switches, room sensors, HVAC units, and doors are bound to real-time controls and telemetry
- Room-level control logic - a room where one switch controls two lights and another switch controls two different lights, with the model reflecting application state instead of hard-coded mesh behavior
The common pattern is simple: if a user needs to say “this exact part of the model does something special,” bindings usually make sense.
When It Probably Doesn’t Fit
- Pure visual/cinematic scenes with no interactivity, a plain
<Canvas><primitive object={scene} /></Canvas>is simpler - Highly custom 3D UIs where the built-in panels/popovers would just be ripped out anyway, though
customObjectBindingDataPanelandcustomSceneObjectsPanelcover a lot of that middle ground
How It Works
At a high level, the viewer does the following:
- Creates a Three.js canvas;
ModelVieweronly defaults thefov(50), the camera position falls back to React Three Fiber’s ownCanvasdefault ([0, 0, 5]) unless you passcamera={{ position: [...] }} - Loads the GLB/GLTF asset with
useGLTF - Looks up meshes by the keys in
objectBindings(resolvingmodelObjectIdto find the actual model node) - Clones each referenced material so edits stay isolated per object
- Applies runtime color, texture, and style overrides from
objectBindingsin auseEffect - Manages a reference-counted texture cache so the same texture URL is decoded once and disposed when no longer referenced
- Adds hover, selected, and panel-hover emissive highlighting
- Creates an
AnimationMixerfor the loaded scene and exposes playback controls throughonAnimationsReady - Renders side panels for the selected object and its configured actions
Only objects listed in objectBindings are rendered interactively.
Lighting & Shadows
With the shadows prop enabled (the default), the viewer renders soft shadows and a post-processing stack (SSAO ambient occlusion, bloom, vignette, ACES filmic tone mapping). The default light rig uses ambient + hemisphere fill and a directional key light that casts shadows.
Interior models (rooms, dollhouses) are a special case: the large mesh that forms the walls/ceiling enclosure is detected by its bounding-box size and excluded from casting shadows while still receiving them. This prevents a closed shell from blocking a top-down light and sealing the interior in darkness, so furniture and decor cast realistic contact shadows onto the floor. Pass shadows={false} to turn shadows off entirely, or a custom lights prop to replace the default rig.
UI Shell
The component includes its own UI shell:
- A full-height viewer layout with built-in styles
- A sliding left side panel (~280 px wide) for the selected object’s details and actions; on narrow screens the side panels collapse to half-width so the canvas keeps its other half
- A canvas area that fills the remaining width
- A color picker that expands inline beneath the Change Color action
- A texture upload panel that expands inline beneath the Change Material action
- A right scene objects panel with search and per-object visibility toggles
ModelViewer works best when mounted in a container that owns the full
viewport height.
Model Requirements
To use this library successfully, your GLB/GLTF asset should follow these conventions:
- Mesh node names must match the keys in
objectBindings(or be referenced viabinding.modelObjectId) - Target nodes should have geometry and a
MeshStandardMaterial - The file must be accessible from the browser at the path you pass to
modelUrl; hosted.gltffiles must also serve external.binand texture files at their declared relative paths
If a node name or material does not match, that object will not render through the interactive binding flow.
Peer Dependencies
| Package | Version |
|---|---|
react | >= 17 |
react-dom | >= 17 |