CompareViewer
Shows two configurations next to each other with one camera: a before and
after of a renovation, two paint options, the current part against its
replacement. Each side is a full ModelViewer,
so a side can differ in anything a viewer takes: objectBindings,
sceneConfig, material variants, even a different modelUrl.
import "@liveroom-tech/react-immersive/styles.css";
import { CompareViewer } from "@liveroom-tech/react-immersive";
<div style={{ height: 600 }}>
<CompareViewer
modelUrl="/kitchen.glb"
licenseKey="your-license-key"
before={{ label: "Current", objectBindings: currentBindings }}
after={{ label: "Proposed", objectBindings: proposedBindings }}
/>
</div>;Layouts
layout="slider"(default) lays the two views over each other. A divider splits them: the left of it showsbefore, the right showsafter. Drag the divider, or focus it and use the arrow keys, Page Up/Down, Home, and End. It is a slider to screen readers, with each side’s label and share.layout="side-by-side"shows the two views next to each other, each half the width.
<CompareViewer
modelUrl="/car.glb"
licenseKey="your-license-key"
layout="side-by-side"
before={{ sceneConfig: { ...config, model: { ...config.model, activeVariants: ["Paint: Red"] } } }}
after={{ sceneConfig: { ...config, model: { ...config.model, activeVariants: ["Paint: Blue"] } } }}
/>One camera
Orbiting, panning, or zooming either side moves both. The side you last grabbed leads and the other copies it every frame, so a fly-in, a zoom to a selected object, or a refit on one side shows on both. With two different models, the views line up on the same point in space, so the models should share a coordinate system.
Props
type CompareViewerProps = Partial<ModelViewerProps> & {
licenseKey: string;
before: CompareViewerSide; // the left side
after: CompareViewerSide; // the right side
layout?: "slider" | "side-by-side"; // default "slider"
split?: number; // the divider, 0 (left edge) to 1; default 0.5
onSplitChange?: (split: number) => void;
};
type CompareViewerSide = Partial<ModelViewerProps> & {
label?: string; // shown over the side; default "Before" / "After"
};Every other prop is shared by both sides, and a side’s own props win.
Callbacks run per side: give before.onObjectSelect and
after.onObjectSelect to tell the sides apart. Both sides start from the
minimal preset,
with no panels or buttons, so their interfaces don’t overlap; pass preset
or ui to change that. Pass split to control the divider yourself;
without it, CompareViewer keeps the position.
Each side has its own WebGL (or WebGPU) canvas. The two share one download and parse of the model file, but the GPU holds two copies of its geometry and textures.
poster, loading and reveal work as on
ModelViewer. With
reveal="interaction", one View in 3D button starts both sides.
BindingBuilder has the same slider over its preview (Compare), to check edits against the project as it was opened.
The divider and the default side labels use the
labels keys compareBefore, compareAfter,
compareDivider, and compareDividerValue.