---
title: "How to add annotations to a splat model | Geosplats sdk"
description: "Add interactive labelled pins and popups to a splat model."
---

> For AI agents: a documentation index is available at [/llms.txt](https://docs.maptiler.com/llms.txt). Markdown versions of all pages are available by appending .md to any URL.

# How to add annotations to a splat model

This example demonstrates how to create interactive annotations on a 3D Gaussian splat model using the MapTiler GeoSplats SDK. Annotations are labelled pins attached to a model that can display HTML popups when clicked.

### NPM module setup

```bash
npm install --save @maptiler/geosplats
```

#### main.js

```js
import { SplatModel, Map, config } from "@maptiler/geosplats";
import "@maptiler/geosplats/dist/maptiler-geosplats.css";

config.apiKey = "YOUR_MAPTILER_API_KEY_HERE";
const map = new Map({
  apiKey: config.apiKey,
  container: "map",
  center: [-80.27822, 25.74077],
  zoom: 17.5,
  pitch: 50,
  bearing: -15,
});

let splatModel;
map.on("load", () => {
  splatModel = new SplatModel({
    model: "YOUR_MAPTILER_SPLAT_MODEL_ID_HERE",
    revealParams: { enabled: true },
  });
  map.addSplatModel(splatModel);
});

map.on("splatmodel:load", () => {
  splatModel.addAnnotation({
    pos: { x: -0.4, z: -0.2 },
    icon: "🏖️",
    label: "Sun Pool",
    popup: "<h3>🏖️ Sun Pool</h3><p>Outdoor pool with a 24h bar.</p>",
  });

  splatModel.addAnnotation({
    pos: { x: -0.06, z: -0.26, y: 0.001 },
    icon: "🏢",
    label: "Main Entrance",
    color: "#2bb673",
    popup: "<h3>🏢 Main Entrance</h3><p>Main hotel lobby, reception, and guest check-in.</p>",
  });

  splatModel.addAnnotation({
    pos: { x: 0.6, z: 0.46 },
    icon: "🎾",
    label: "Tennis Courts",
    popup: "<h3>🎾 Tennis Courts</h3><p>10 championship courts with floodlighting.</p>",
  });
});
```

#### index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex" />
    <title>How to add annotations to a splat model</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="map"></div>
    <script type="module" src="./main.js"></script>
  </body>
</html>
```

#### style.css

```css
body {
  font-family: Inter, sans-serif;
  margin: 0;
  overflow: hidden;
}

#map {
  width: 100%;
  height: 100vh;
}
```

### Basic JavaScript setup

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>How to add annotations to a splat model</title>
    <link
      rel="stylesheet"
      href="https://cdn.maptiler.com/maptiler-geosplats/v1.2.1/maptiler-geosplats.css"
    />
    <style>
      body {
        font-family: Inter, sans-serif;
        margin: 0;
        overflow: hidden;
      }

      #map {
        width: 100%;
        height: 100vh;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <script type="module">
      import {
        SplatModel,
        Map,
        config,
      } from "https://cdn.maptiler.com/maptiler-geosplats/v1.2.1/maptiler-geosplats.mjs";

      config.apiKey = "YOUR_MAPTILER_API_KEY_HERE";
      const map = new Map({
        apiKey: config.apiKey,
        container: "map",
        center: [-80.27822, 25.74077],
        zoom: 17.5,
        pitch: 50,
        bearing: -15,
      });

      let splatModel;
      map.on("load", () => {
        splatModel = new SplatModel({
          model: "YOUR_MAPTILER_SPLAT_MODEL_ID_HERE",
          revealParams: { enabled: true },
        });
        map.addSplatModel(splatModel);
      });

      map.on("splatmodel:load", () => {
        splatModel.addAnnotation({
          pos: { x: -0.4, z: -0.2 },
          icon: "🏖️",
          label: "Sun Pool",
          popup: "<h3>🏖️ Sun Pool</h3><p>Outdoor pool with a 24h bar.</p>",
        });

        splatModel.addAnnotation({
          pos: { x: -0.06, z: -0.26, y: 0.001 },
          icon: "🏢",
          label: "Main Entrance",
          color: "#2bb673",
          popup: "<h3>🏢 Main Entrance</h3><p>Main hotel lobby, reception, and guest check-in.</p>",
        });

        splatModel.addAnnotation({
          pos: { x: 0.6, z: 0.46 },
          icon: "🎾",
          label: "Tennis Courts",
          popup: "<h3>🎾 Tennis Courts</h3><p>10 championship courts with floodlighting.</p>",
        });
      });
    </script>
  </body>
</html>
```

## When to add annotations

The `addAnnotation()` method on `SplatModel` is only active once the model is attached and loaded onto the map. Calling `splatModel.addAnnotation()` before the model loads returns `undefined` and adds nothing.

Wait for the `splatmodel:load` event on the map before adding annotations:

```js
map.on("splatmodel:load", () => {
  splatModel.addAnnotation({
    pos: { x: -0.4, z: -0.2 },
    icon: "🏖️",
    label: "Sun Pool",
    popup: "<h3>🏖️ Sun Pool</h3><p>Outdoor pool with a 24h bar.</p>",
  });
});
```

## Pin labels and icons

Pins consist of an optional emoji icon (`icon`) and an optional text label (`label`). The pin automatically resizes to accommodate its content:

-   **Emoji icon**: A single emoji shown on the pin.
-   **Label**: Short text rendered next to the icon in white text with a crisp background.
-   **Color**: An optional pin fill color (such as hex `#2bb673`). The default fill is MapTiler blue (`#4d7fff`).

Keep labels concise. Detailed information, long descriptions, or external links belong in the popup.

## Interactive HTML popups

Defining the `popup` property turns the pin into an interactive card that opens when clicked:

-   **HTML content**: Accepts any standard HTML string, including headings, paragraphs, and formatted text.
-   **Single active popup**: Only one popup opens at a time per map. Opening another pin closes any open card.
-   **Automatic tracking**: The popup tracks the pin across camera pans, rotations, and zoom changes, and hides automatically if the pin moves off-screen.
-   **Active state collapsing**: When a pin’s popup opens, the pin badge collapses to a circular icon so it does not block the card content.

## Learn more

Check out the [MapTiler GeoSplats SDK reference](https://docs.maptiler.com/geosplats-sdk/api) to explore the full list of annotation options and event listeners.

## Related examples

### [Add splat model](https://docs.maptiler.com/geosplats-sdk/examples/add-geosplats/)

Examples

Learn how to load a map using the GeoSplats SDK and add a photorealistic splat model. A splat model offer a modern alternative to 3D meshes, perfect for complex scenes.

### [Model vs geographic positioning](https://docs.maptiler.com/geosplats-sdk/examples/annotation-positioning/)

Examples

Place annotations using model-relative bounding box coordinates or geographic coordinates.

### [Pin styles and categories](https://docs.maptiler.com/geosplats-sdk/examples/annotation-styles/)

Examples

Style annotations with colors, icons, numbers, and custom popup placement.

### [Rich HTML popups](https://docs.maptiler.com/geosplats-sdk/examples/rich-popup-annotations/)

Examples

Display custom HTML cards in annotation popups with images, tables, and custom widths.
