# Events and coordinates — Five SDK

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

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

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

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](https://developer.realsee.ai/docs/five-sdk/quickstart). 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.

```ts title="starters/react-five/src/recipes/pickPoint.ts"
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.

```ts title="starters/react-five/src/recipes/placeLabel.ts"
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](https://developer.realsee.ai/docs/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](https://developer.realsee.ai/docs/five-sdk/capture-images).
