Skip to main content
Documentation

Events and coordinates

Handle a scene tap, intersect the model, and project a world-space label into a browser overlay.

On this page

Use this task to place your own interface on a captured space. Pick a point on the model geometry; a screen click is not automatically a valid 3D location.

Run the example

Open SDK examples from the starter. Enable picking, select a visible model surface, and inspect the coordinate result. Place a label at that point, then turn the camera to check that the overlay follows it.

Pick a point on the model

Subscribe only while the placement tool is active. The callback prevents the same tap from also triggering the SDK's normal viewpoint navigation.

import type { Five, Intersection } from "@realsee/five";

/** Opt in while a placement tool is active; the returned cleanup restores it. */
export function watchPickedPoint(
  five: Five,
  onPick: (hit: Intersection | null) => void
): () => void {
  return five.on("gesture.tap", (event) => {
    // Prevent this tool's tap from also triggering default point navigation.
    event.preventDefault();
    const hits = five.model.intersectRaycaster(event.center.raycaster);
    onPick(hits[0] ?? null);
  });
}

null means there is no model intersection at that point. Sky, transparent areas, missing geometry and scene loading can produce no hit. Clear the placement state instead of inventing a coordinate.

Project the label into the viewport

The overlay must cover the same rectangle as the canvas. Use a stable positioned parent, an absolutely positioned overlay, and pointer-events: none for a passive label.

import type { Five } from "@realsee/five";
import type { Vector3 } from "three";

/** Put overlay over the canvas, with the same dimensions and position. */
export function placeLabel(
  five: Five,
  overlay: HTMLElement,
  position: Vector3,
  text: string
): () => void {
  const label = document.createElement("span");
  label.textContent = text;
  label.style.cssText =
    "position:absolute;pointer-events:none;transform:translate(-50%,-100%);padding:6px 10px;border-radius:6px;background:#fff;color:#102f50";
  overlay.append(label);
  const update = () => {
    const point = five.project2d(position, true);
    label.hidden = point === null;
    if (point) {
      label.style.left = `${point.x}px`;
      label.style.top = `${point.y}px`;
    }
  };
  update();
  const stop = five.on("render", update);
  return () => {
    stop();
    label.remove();
  };
}

Use textContent for untrusted text. Remove the label and its render listener when the task ends. The true argument enables the SDK's model-occlusion test; it adds intersection work for each label. This remains a 2D interface element, not a persistent annotation stored on your server.

For editable panorama tags and richer spatial UI, continue with Dnalogel panorama tags.

Check the result

  • An actual surface produces coordinates; a miss stays empty.
  • Placement mode does not also navigate to another panorama.
  • Turning or resizing the scene keeps the label aligned.
  • Closing the tool unsubscribes its listener and removes its label.

Continue with Capture an image.