Join us online - 19th of November
Save your spot
For AI agents: a documentation index is available at /llms.txt. Markdown versions of all pages are available by appending .md to any URL.

Customize annotation pins and colors

This example shows how to customize the appearance of annotations on a 3D Gaussian splat model using custom colors, pin shapes, numbered step badges, and prioritized popup placement.

NPM module setup


npm install --save @maptiler/geosplats
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", () => {
  // 1. Single character label creates a round circular badge (ideal for numbered tours)
  splatModel.addAnnotation({
    pos: { x: -0.06, z: -0.26 },
    label: "1",
    color: "#3174ff",
    popup: "<h3>Tour Stop 1</h3><p>Main hotel lobby and reception.</p>",
  });

  splatModel.addAnnotation({
    pos: { x: -0.4, z: -0.2 },
    label: "2",
    color: "#2bb673",
    popup: "<h3>Tour Stop 2</h3><p>Outdoor heated swimming pool and terrace.</p>",
  });

  // 2. Icon-only round pin: an emoji without label text
  splatModel.addAnnotation({
    pos: { x: 0.1, z: 0.2 },
    icon: "🚘",
    color: [77, 77, 77],
    popup: "<h3>Valet Parking</h3><p>Underground secure parking garage.</p>",
  });

  // 3. Hex color and customized popup placement preference
  splatModel.addAnnotation({
    pos: { x: -0.6, z: 0.5 },
    icon: "β›³",
    label: "Golf Course",
    color: "#e69439",
    popupPlacement: ["top", "bottom", "right", "left"],
    popup: "<h3>Golf Course</h3><p>18-hole championship green.</p>",
  });

  // 4. Color defined as an RGB object
  splatModel.addAnnotation({
    pos: { x: 0.35, z: -0.25 },
    icon: "πŸ’†",
    label: "Luxury Spa",
    color: { r: 180, g: 60, b: 180 },
    popupPlacement: ["left", "right"],
    popup: "<h3>Luxury Spa</h3><p>Full wellness, massage, and sauna treatments.</p>",
  });

  // 5. Named color
  splatModel.addAnnotation({
    pos: { x: 0.6, z: 0.46 },
    icon: "🎾",
    label: "Tennis Club",
    color: "red",
    popup: "<h3>Tennis Club</h3><p>10 floodlit championship courts.</p>",
  });
});

Pin geometry and sizing

The pin automatically adjusts its shape depending on the length of its content:

  • Single character or emoji: Produces a circular pin. This layout is ideal for numbered tour sequences ("1", "2", "3") or standalone icon symbols ("🚘", "β›³").
  • Longer text: From three characters onward, the pin renders as a rounded rectangular badge with an arrow pointer centered on the coordinate.
  • Active state collapsing: When a pin with a popup is clicked, it collapses into a compact circular pin displaying only its icon, preventing visual overlap with the popup card.

// Circular numbered tour pin
splatModel.addAnnotation({
  pos: { x: -0.06, z: -0.26 },
  label: "1",
  color: "#3174ff",
});

// Icon-only circular pin
splatModel.addAnnotation({
  pos: { x: 0.1, z: 0.2 },
  icon: "🚘",
  color: [77, 77, 77],
});

Color formats and categorization

The color property configures the pin background fill color (the white label text and white border remain consistent). It accepts:

  • Hexadecimal strings: #RGB, #RGBA, #RRGGBB, #RRGGBBAA (e.g. "#e69439").
  • RGB arrays: [r, g, b] or [r, g, b, a] with components from 0 to 255 (and alpha from 0 to 1).
  • RGB objects: { r, g, b, a? } with numerical values.
  • Supported named colors: "black", "white", "red", "green", "blue", "yellow", "magenta", "cyan", and "transparent".

const CATEGORY_COLORS = {
  leisure: "#2bb673",
  dining: "#e69439",
  services: "#3174ff",
};

splatModel.addAnnotation({
  pos: { x: -0.4, z: -0.2 },
  icon: "πŸ–οΈ",
  label: "Pool",
  color: CATEGORY_COLORS.leisure,
});

Controlling popup placement

Use popupPlacement to define an ordered list of preferred sides where the popup can open relative to the pin:

  • Available values: "top", "bottom", "left", and "right".
  • Every frame, the SDK selects the first side with sufficient screen clearance on the map.
  • If none of the sides have sufficient space, the side with the most available room is chosen.

// Prefer opening above or below, falling back to sides
splatModel.addAnnotation({
  pos: { x: -0.6, z: 0.5 },
  label: "Golf Course",
  popup: "<h3>Golf Course</h3><p>Championship green.</p>",
  popupPlacement: ["top", "bottom", "right", "left"],
});

Learn more

Check out the MapTiler GeoSplats SDK reference for full documentation on AnnotationOptions, AnnotationPopupPlacement, and Color.

How to add annotations to a splat model

Add annotations

Examples

Add interactive labelled pins and popups to a splat model.

Position annotations relative to model or geography

Model vs geographic positioning

Examples

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

Create rich HTML popups in annotations

Rich HTML popups

Examples

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

Capture map and splats events

Capture events

Examples

Learn how to capture both global map events and specific splats events like load, update, mouseover, and click.

Was this helpful?