Use this task when you already have Work data and need to display it safely.
Run the example
Run the starter, then open SDK examples. A real public scene should reach Ready. Resize the browser and check that the canvas stays aligned with its container.
Work describes a scene; it is not a gateway token, a Team VR identifier, or arbitrary metadata. Public JSON and Team API responses must reach parseWork before load.
Mount and dispose
Give the host a visible width and height. Mount the renderer before loading Work. Keep the returned cleanup and call it when leaving this task or replacing the scene.
import type { Five as FiveInstance } from "@realsee/five";
export type MountedScene = {
five: FiveInstance;
dispose: () => void;
};
/** Browser only. Give host an explicit height before calling this function. */
export async function mountScene(
host: HTMLElement,
rawWork: unknown,
signal?: AbortSignal
): Promise<MountedScene> {
const { Five, parseWork } = await import("@realsee/five");
signal?.throwIfAborted();
const five = new Five({ imageOptions: { size: 512 } });
const layer = document.createElement("div");
layer.style.cssText = "width:100%;height:100%";
host.append(layer);
let disposed = false;
const resize = new ResizeObserver(() => five.refresh());
const dispose = () => {
if (disposed) return;
disposed = true;
signal?.removeEventListener("abort", dispose);
resize.disconnect();
five.dispose();
layer.remove();
};
signal?.addEventListener("abort", dispose, { once: true });
try {
five.appendTo(layer);
resize.observe(layer);
await five.load(parseWork(rawWork), "initial");
signal?.throwIfAborted();
five.refresh();
return { five, dispose };
} catch (error) {
dispose();
throw error;
}
}Pass an AbortSignal when the host belongs to a component or route. Cancellation releases the instance and its own layer; it does not promise that every SDK-owned asset request stops immediately.
Observe the relevant loading stage
A Work-ready event, panorama texture, model load, and refined model represent different stages. Attach listeners before the load you want to observe; unsubscribe when the viewer is disposed.
import type { Five } from "@realsee/five";
export type LoadingFeedback =
| { stage: "work"; state: "ready" }
| { stage: "panorama"; state: "loading"; progress: number }
| { stage: "panorama"; state: "ready" | "error" }
| { stage: "model"; state: "ready" | "refined" | "error" };
/** Attach before load. Progress describes an asset, not the entire app/API. */
export function watchLoading(
five: Five,
onChange: (value: LoadingFeedback) => void
): () => void {
const stop = [
five.on("works.ready", () => onChange({ stage: "work", state: "ready" })),
five.on("pano.texture.progress", ({ progress }) =>
onChange({ stage: "panorama", state: "loading", progress })
),
five.on("pano.texture.success", () =>
onChange({ stage: "panorama", state: "ready" })
),
five.on("pano.texture.error", () =>
onChange({ stage: "panorama", state: "error" })
),
five.on("models.load", () => onChange({ stage: "model", state: "ready" })),
five.on("models.refined", () =>
onChange({ stage: "model", state: "refined" })
),
five.on("model.error", () => onChange({ stage: "model", state: "error" })),
];
return () => stop.forEach((unsubscribe) => unsubscribe());
}A panorama progress value describes that asset. Do not display it as a fabricated percentage for authentication, all scene assets, or the complete app.
Check the result
- The panorama or model is visible and responds to interaction.
- The canvas follows its container after resize.
- Changing route or scene removes the old canvas and listeners.
- Missing assets show an error; the app does not substitute a synthetic image.
For Team-owned Work, keep authorization on the server. Continue with Camera and modes.
