---
title: "Add events on 3D models | 3D JS | JavaScript maps SDK"
description: "Listen for mouse events on 3D models in maps."
---

> 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.

# Add events on 3D models

Add event listeners to your 3D models to improve interaction with objects added to your maps.

This demonstration showcases how to add user interactivity to 3D models, click, doubleclick, mouseenter, mouseleave.

Click or move the mouse over the 3D models to get to event information

### NPM module setup

```bash
npm install --save @maptiler/sdk @maptiler/3d three
```

#### main.js

```js
import { Map, MapStyle, config } from '@maptiler/sdk';
import '@maptiler/sdk/dist/maptiler-sdk.css';
import { Layer3D } from '@maptiler/3d';

import * as THREE from 'three';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const map = new Map({
  container: 'map',
  style: MapStyle.SATELLITE,
  center: [-119.63553428649902, 37.728162793005595], // starting position [lng, lat]
  zoom: 12.5, // starting zoom
  hash: true,
  bearing: 0,
  pitch: 45,
  terrainControl: true,
  terrain: true,
});

const shapes = [
  {
    lngLat: [-119.65308666229248, 37.738277034695685],
      mesh: (
        new THREE.Mesh(
          new THREE.BoxGeometry(10, 10, 10),
          new THREE.MeshStandardMaterial({ color: "red", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "box",
    altitude: 120,
  },
  {
    lngLat: [-119.61553573, 37.723071209025974],
    mesh: (
      new THREE.Mesh(
        new THREE.SphereGeometry(10),
        new THREE.MeshStandardMaterial({ color: "blue", roughness: 0.5, metalness: 0.5 })
      )
    ),
    name: "sphere",
    altitude: 120,
  },
  {
    lngLat: [-119.63008403778076, 37.735154664553534],
    altitude: 260,
    mesh: (
        new THREE.Mesh(
          new THREE.TorusGeometry(10, 5, 32, 32),
          new THREE.MeshStandardMaterial({ color: "green", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "torus"
  },
  {
    lngLat: [-119.65128421783447, 37.72208679574618],
    altitude: 300,
    mesh: (
        new THREE.Mesh(
          new THREE.TorusKnotGeometry(10, 2, 32, 32),
          new THREE.MeshStandardMaterial({ color: "rebeccapurple", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "torusKnot"
  },
];

const info = document.getElementById("event-info");

function setInfo(text) {
  info.innerHTML = text;
}

// Waiting for the map to be ready
map.on('ready', async () => {
  // Create a Layer3D and add it
  const layer3D = new Layer3D("custom-3D-layer");
  map.addLayer(layer3D);

   // Increasing the intensity of the ambient light
  layer3D.setAmbientLight({ intensity: 2 });

  // Adding a point light
  layer3D.addPointLight("point-light", { intensity: 30 });

  shapes.forEach((shape) => {
    const initialScale = 20;

    layer3D.addMesh(shape.name, shape.mesh, {
      lngLat: new maptilersdk.LngLat(shape.lngLat[0], shape.lngLat[1]),
      heading: 0,
      scale: initialScale,
      altitude: shape.altitude,
    });
    const mesh = layer3D.getItem3D(shape.name);

    const threeMesh = mesh?.mesh;

    const originalMaterial = threeMesh?.material;
    const swapMaterial = new THREE.MeshBasicMaterial({ color: "dodgerblue" });

    mesh?.on("mouseenter", (event) => {
      const mesh = layer3D.getItem3D(shape.name);
      const threeMesh = mesh?.mesh;
      if (threeMesh && "material" in threeMesh) {
        threeMesh.material = swapMaterial;
      }
      setInfo(`mouseenter! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
    mesh?.on("mouseleave", (event) => {
      const mesh = layer3D.getItem3D(shape.name);
      const internalMesh = mesh?.mesh;
      if (internalMesh && "material" in internalMesh) {
        internalMesh.material = originalMaterial;
      }
      setInfo(`mouseleave! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
    
    mesh?.on("click", (event) => {
      if (mesh?.scale[0] === initialScale) {
        mesh?.setScale(initialScale * 1.5);
      } else {
        mesh?.setScale(initialScale);
      }
      setInfo(`click! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });

    mesh?.on("dblclick", (event) => {
      setInfo(`dblclick! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
  });

  let heading = 0;

  function loop() {
    requestAnimationFrame(loop);
    for (const { name: meshName } of shapes) {
      layer3D.getItem3D(meshName)?.modify({
        heading: heading,
      });
    }
    heading += 0.5;
  }

  loop();
});
```

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="robots" content="noindex">
  <title>Add events on 3D models | NPM example</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="map"></div>
  <div id="event-info"></div>
  <script type="module" src="./main.js"></script>
</body>
</html>
```

#### style.css

```css
body {margin: 0; padding: 0;}
#map {position: absolute; top: 0; bottom: 0; width: 100%;}
#event-info {
  position: absolute;
  top: 10px;
  left: 10px;
  background-color: #ffffff;
}
```

### Basic JavaScript setup

```html
<html>
  <head>
    <title>Add events on 3D models</title>
    <style>
      body {margin: 0; padding: 0;}
      #map {position: absolute; top: 0; bottom: 0; width: 100%;}
      #event-info {
        position: absolute;
        top: 10px;
        left: 10px;
        background-color: #ffffff;
      }
    </style>

    <script src="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.umd.min.js"></script>
    <link href="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.css" rel="stylesheet" />
    <script src="https://cdn.maptiler.com/maptiler-3d/v4.0.1/maptiler-3d.umd.min.js"></script>
  </head>

  <body>
    <script type="importmap">
    {
      "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.172.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.172.0/examples/jsm/"
      }
    }
  </script>
    <div id="map"></div>
    <div id="event-info"></div>
    <script type="module">
      import * as THREE from 'three';
      
      maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

      const map = new maptilersdk.Map({
        container: 'map',
        style: maptilersdk.MapStyle.SATELLITE,
        center: [-119.63553428649902, 37.728162793005595], // starting position [lng, lat]
        zoom: 12.5, // starting zoom
        hash: true,
        bearing: 0,
        pitch: 45,
        terrainControl: true,
        terrain: true,
      });

      const shapes = [
        {
          lngLat: [-119.65308666229248, 37.738277034695685],
            mesh: (
              new THREE.Mesh(
                new THREE.BoxGeometry(10, 10, 10),
                new THREE.MeshStandardMaterial({ color: "red", roughness: 0.5, metalness: 0.5 })
              )
          ),
          name: "box",
          altitude: 120,
        },
        {
          lngLat: [-119.61553573, 37.723071209025974],
          mesh: (
            new THREE.Mesh(
              new THREE.SphereGeometry(10),
              new THREE.MeshStandardMaterial({ color: "blue", roughness: 0.5, metalness: 0.5 })
            )
          ),
          name: "sphere",
          altitude: 120,
        },
        {
          lngLat: [-119.63008403778076, 37.735154664553534],
          altitude: 260,
          mesh: (
              new THREE.Mesh(
                new THREE.TorusGeometry(10, 5, 32, 32),
                new THREE.MeshStandardMaterial({ color: "green", roughness: 0.5, metalness: 0.5 })
              )
          ),
          name: "torus"
        },
        {
          lngLat: [-119.65128421783447, 37.72208679574618],
          altitude: 300,
          mesh: (
              new THREE.Mesh(
                new THREE.TorusKnotGeometry(10, 2, 32, 32),
                new THREE.MeshStandardMaterial({ color: "rebeccapurple", roughness: 0.5, metalness: 0.5 })
              )
          ),
          name: "torusKnot"
        },
      ];

      const info = document.getElementById("event-info");

      function setInfo(text) {
        info.innerHTML = text;
      }

      // Waiting for the map to be ready
      map.on('ready', async () => {
        // Create a Layer3D and add it
        const layer3D = new maptiler3d.Layer3D("custom-3D-layer");
        map.addLayer(layer3D);

         // Increasing the intensity of the ambient light
        layer3D.setAmbientLight({ intensity: 2 });

        // Adding a point light
        layer3D.addPointLight("point-light", { intensity: 30 });

        shapes.forEach((shape) => {
          const initialScale = 20;

          layer3D.addMesh(shape.name, shape.mesh, {
            lngLat: new maptilersdk.LngLat(shape.lngLat[0], shape.lngLat[1]),
            heading: 0,
            scale: initialScale,
            altitude: shape.altitude,
          });
          const mesh = layer3D.getItem3D(shape.name);

          const threeMesh = mesh?.mesh;

          const originalMaterial = threeMesh?.material;
          const swapMaterial = new THREE.MeshBasicMaterial({ color: "dodgerblue" });

          mesh?.on("mouseenter", (event) => {
            const mesh = layer3D.getItem3D(shape.name);
            const threeMesh = mesh?.mesh;
            if (threeMesh && "material" in threeMesh) {
              threeMesh.material = swapMaterial;
            }
            setInfo(`mouseenter! \n
              name: ${shape.name}\n
              lng: ${shape.lngLat[0]} \n
              lat: ${shape.lngLat[1]} \n
              x, y: ${event.point.x}, ${event.point.y} \n
            `);
          });
          mesh?.on("mouseleave", (event) => {
            const mesh = layer3D.getItem3D(shape.name);
            const internalMesh = mesh?.mesh;
            if (internalMesh && "material" in internalMesh) {
              internalMesh.material = originalMaterial;
            }
            setInfo(`mouseleave! \n
              name: ${shape.name}\n
              lng: ${shape.lngLat[0]} \n
              lat: ${shape.lngLat[1]} \n
              x, y: ${event.point.x}, ${event.point.y} \n
            `);
          });
          
          mesh?.on("click", (event) => {
            if (mesh?.scale[0] === initialScale) {
              mesh?.setScale(initialScale * 1.5);
            } else {
              mesh?.setScale(initialScale);
            }
            setInfo(`click! \n
              name: ${shape.name}\n
              lng: ${shape.lngLat[0]} \n
              lat: ${shape.lngLat[1]} \n
              x, y: ${event.point.x}, ${event.point.y} \n
            `);
          });

          mesh?.on("dblclick", (event) => {
            setInfo(`dblclick! \n
              name: ${shape.name}\n
              lng: ${shape.lngLat[0]} \n
              lat: ${shape.lngLat[1]} \n
              x, y: ${event.point.x}, ${event.point.y} \n
            `);
          });
        });

        let heading = 0;

        function loop() {
          requestAnimationFrame(loop);
          for (const { name: meshName } of shapes) {
            layer3D.getItem3D(meshName)?.modify({
              heading: heading,
            });
          }
          heading += 0.5;
        }

        loop();
      });

    </script>
  </body>
</html>
```

## Related examples

### [Animate a 3D model](https://docs.maptiler.com/sdk-js/examples/3d-js-animated/)

Examples

Animate 3D models and display them on a map.

### [Add a plane 3D model](https://docs.maptiler.com/sdk-js/examples/3d-js-plane/)

Examples

Add and position flying 3D airplane models on maps.

### [Add multiple 3D models](https://docs.maptiler.com/sdk-js/examples/3d-js-multi/)

Examples

Incorporate multiple 3D models with adjustable parameters on maps.

### [3D terrain map](https://docs.maptiler.com/sdk-js/examples/3d-map/)

Tutorials

Create and display 3D terrain maps on web pages.
