# Camera and modes — Five SDK

Switch view modes, navigate observer points, move the camera, and restore a saved scene view.

Canonical: https://developer.realsee.ai/docs/five-sdk/camera-and-modes
Source: https://developer.realsee.ai/docs/five-sdk/camera-and-modes.md
Language: en

[Download examples](https://developer.realsee.ai/starters/react-five/1.3.0/starter.zip)

Build viewing controls after the scene has loaded.

## Run the example

Open **SDK examples** in the [running starter](https://developer.realsee.ai/docs/five-sdk/quickstart). Try the mode control, next viewpoint, camera turn, and saved-view controls. Check the scene itself after each action.

## Switch the mode

Use the public `changeMode` operation and await its result. The supported mode values used here are `Panorama`, `Floorplan`, and `Topview`. Listen for the completed change before updating a control's selected state.

```ts title="starters/react-five/src/recipes/changeView.ts"
import type { Five, Mode } from "@realsee/five";

export type SceneView = Extract<Mode, "Panorama" | "Floorplan" | "Topview">;

/** Await this from a button handler; show a recoverable error on rejection. */
export async function changeView(five: Five, view: SceneView): Promise<void> {
  await five.changeMode(view, undefined, { duration: 500 });
}

/** Subscribe to the completed mode change, and call the returned cleanup. */
export function watchView(
  five: Five,
  onChange: (view: Mode) => void
): () => void {
  onChange(five.currentMode);
  return five.on("mode.change", (event) => onChange(event.mode));
}
```

A Work may not contain the model or assets a mode needs. Keep the current scene available and show the rejected operation rather than claiming that every scene supports every mode.

## Navigate to an actual viewpoint

Choose from the loaded Work's observers where **`active || loadable`** is true, matching Five SDK 6.9.2's runtime navigation checks. These are alternatives: the published Public Demo has active observers whose `loadable` flag is false. Requiring both flags would incorrectly remove every destination. A panorama index on its own is insufficient when a viewer contains more than one Work; keep `workCode` with `panoIndex`.

```ts title="starters/react-five/src/recipes/navigateScene.ts"
import type { Five, Pano, Pose } from "@realsee/five";

/** Five 6.9.2 accepts active OR explicitly loadable observers, not both flags. */
export function listDestinations(five: Five): Pano[] {
  return five.observers
    .filter((observer) => observer.active || observer.loadable)
    .map((observer) => ({
      workCode: observer.work.workCode,
      panoIndex: observer.panoIndex,
    }));
}

export async function goToDestination(five: Five, pano: Pano): Promise<void> {
  await five.moveToPano(pano, { effect: "fade", duration: 500 });
}

/** Angles use radians. This rotates/zooms without changing the panorama. */
export async function lookAt(
  five: Five,
  pose: Pick<Pose, "longitude" | "latitude" | "fov">
): Promise<void> {
  await five.updateCamera(pose, 500);
}
```

Camera angles use **radians**. `updateCamera` changes the viewing pose; `moveToPano` changes the panorama. Keep these as separate UI actions so the user knows what will happen.

## Save and restore a view

Copy the current state into a serializable snapshot. A live Three.js vector must not be stored by reference. Restore a trusted snapshot only after its matching Work is loaded.

```ts title="starters/react-five/src/recipes/savedViews.ts"
import type { Five, State } from "@realsee/five";
import { Vector3 } from "three";

export type SavedView = Omit<State, "offset"> & {
  offset: [number, number, number];
};

/** The returned value is JSON serializable and does not retain live vectors. */
export function saveView(five: Five): SavedView {
  const state = five.getCurrentState();
  return {
    ...state,
    offset: [state.offset.x, state.offset.y, state.offset.z],
  };
}

/** Restore only a trusted snapshot for the same loaded Work. */
export function restoreView(five: Five, view: SavedView): void {
  if (view.workCode !== five.work.workCode)
    throw new Error("Load the snapshot's Work before restoring its view.");
  five.setState({ ...view, offset: new Vector3(...view.offset) }, true);
}
```

The snapshot identifies a view, not permission to fetch that Work. A deployed application must authorize the resource independently.

## Check the result

Switch Panorama → Floorplan → Panorama. Save a view, move elsewhere, then restore it. The restored position and viewing direction should match the saved state. Report a mode rejection separately from a successful camera movement.

Continue with [Events and coordinates](https://developer.realsee.ai/docs/five-sdk/events-and-coordinates).
