Skip to main content
mujoco-react has a few camera concepts because rendering, simulation, debugging, and dataset capture are different jobs.

Camera Types

Viewer Camera

The viewer camera is the camera the user sees through. It is owned by React Three Fiber and can be controlled with tools such as OrbitControls.
Changing the viewer camera changes the visible browser viewport. It does not change MuJoCo model cameras.

MuJoCo Cameras

MuJoCo cameras are declared in MJCF. They move with their parent body and are exposed through MuJoCo camera state such as cam_xpos and cam_xmat. Use cameraName when you want a frame from an MJCF <camera>:
mujocoCameraCompatibility applies MuJoCo camera metadata such as resolution, field of view, clipping, and intrinsics when the WASM model exposes them.

Mounted Capture Poses

For policy or dataset streams, a camera can also be resolved from a MuJoCo site or body. This is useful when a robot model exposes optical frames as sites instead of MJCF cameras.
Mounted poses follow simulation state, but they still render offscreen. The user’s orbit camera does not move.

Explicit Capture Poses

Use position with lookAt when the camera is synthetic and not part of the MuJoCo model.
This creates an offscreen Three.js camera for the capture only. It is not a MuJoCo <camera>, and it is not automatically visible in the scene.

Virtual Debug Cameras

virtualCameras on <Debug> draws a marker and frustum for synthetic camera poses. This helps you line up explicit policy or offscreen render viewpoints without adding extra MJCF cameras.
Virtual debug cameras are visual overlays only. They do not create a MuJoCo camera, do not change the viewer camera, and are excluded from camera captures so debug geometry does not contaminate policy images.

Output Modes: Snapshot, Live Stream, Tensor

Any camera pose above (MJCF camera, mounted site/body, or explicit pose) can be turned into three different outputs. Pick the output by what consumes it. The snapshot helpers (captureCameraFrame, useCameraFrameCapture, usePolicyCameraFrames, recordMountedCameraFrameSequence) all produce a data URL or Blob — they encode a PNG/JPEG on every call. That is the right tool for a download or a single saved frame, but too slow for live preview or per-step inference. For those, use the live-stream and tensor APIs below, which read pixels straight off the GPU.

Live Camera Streams

To show a live camera feed on screen, render the scene from a MuJoCo camera into a <canvas> every frame — no PNG round-trip. For a camera tile embedded in HTML UI (a panel, sidebar, or overlay), use useCameraStream. Put the <canvas> anywhere in the DOM and call the hook inside <MujocoCanvas> with a ref to it:
useCameraStream renders offscreen and blits into the canvas, so it composites normally in the DOM (including inside opaque panels) and does not take over the render loop. It uses the async capture path, so Gaussian-splat environments render through their dedicated capture renderer — streaming a splat scene at full rate does not disturb the main view’s splat sort. Pass fps to cap the update rate, or paused to freeze it. For a transparent picture-in-picture overlay on a full-bleed canvas, use <CameraView> / useCameraViewport, which render the camera into a gl.scissor region tracking a DOM element:
<CameraView> is cheaper (it scissors into the main canvas instead of re-reading pixels) but while a view is mounted the canvas switches to a managed render loop. That is incompatible with EffectComposer/postprocessing and is occluded by opaque DOM layered over the canvas — prefer useCameraStream for panel tiles, and <CameraView> for transparent overlays.

Policy Image Tensors

For in-browser policy inference, capture straight into a Float32Array — no canvas, no PNG. usePolicyCameraTensors keeps one reusable session per camera and re-aims it to the live MuJoCo pose each step:
For one-off conversions use captureCameraFrameTensor(); for lower-level control, createCameraFrameCaptureSession() exposes captureTensor() and capturePixels(), and pixelsToPolicyImageTensor() converts a raw RGBA buffer.

Choosing an API