---
title: "3D models intersections | 3D JS | JavaScript maps SDK"
description: "Detect intersections between 3D models using MapTiler JS module."
---

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

# 3D models intersections

Use the [MapTiler 3D JS module](https://docs.maptiler.com/sdk-js/modules/3d/) test whether a 3D model intersects another 3D model.

[Model credit](#fn:1)

### 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, AltitudeReference } from '@maptiler/3d';

import * as THREE from 'three';
config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const center = [-11.40318, 21.12953];

const map = new Map({
  container: document.getElementById('map'),
  hash: true,
  style: MapStyle.SATELLITE,
  zoom: 12.63,
  center: center,
  pitch: 47,
  maxPitch: 85,
  terrainControl: true,
  terrain: true,
  maptilerLogo: true,
});

(async () => {
  await map.onReadyAsync();

  const layer3D = new Layer3D("item3d-intersections");
  map.addLayer(layer3D);

  // Adding a point light
  layer3D.addPointLight("light", {
    lngLat: [
      center[0],
      center[1],
    ],
    intensity: 10,
    decay: 0.1,
    color: "#ffffff",
    altitude: 6000,
  });

  // Adding a mesh. 
  const leftCubeMesh = new THREE.Mesh(new THREE.BoxGeometry(400, 400, 400), new THREE.MeshStandardMaterial({ color: "red", metalness: 0.25, roughness: 0.5 }));
  leftCubeMesh.name = "static-cube-1";
  const leftCubeItem3D = layer3D.addMesh("mesh1", leftCubeMesh, {
    altitudeReference: AltitudeReference.MEAN_SEA_LEVEL,
    lngLat: [
      center[0] - 0.02,
      center[1],
    ],
  });

  leftCubeItem3D.modify({
    heading: 45,
    altitude: 1000,
  });

  const movingItem3D = await layer3D.addMeshFromURL("duck", "https://docs-media.maptiler.com/docs/models/duck.glb", {
    lngLat: center,
    heading: 0,
    scale: 180,
    altitude: 950,
    altitudeReference: AltitudeReference.MEAN_SEA_LEVEL,
  });

  const sphereRightMesh = new THREE.Mesh(new THREE.SphereGeometry(250, 32, 32), new THREE.MeshStandardMaterial({ color: "red", roughness: 0.5 }));
  sphereRightMesh.name = "static-sphere";
  const sphereRightItem3D = layer3D.addMesh("sphere", sphereRightMesh, {
    lngLat: [
      center[0] + 0.02,
      center[1],
    ],
    altitude: 1000,
    altitudeReference: AltitudeReference.MEAN_SEA_LEVEL,
  });

  const itemLngLat = movingItem3D.lngLat;
  let progress = 0;

  addDebugCheckBox([leftCubeItem3D, movingItem3D, sphereRightItem3D]);

  function loop() {
    requestAnimationFrame(loop);
    progress += 0.0005;

    leftCubeItem3D.modify({
      heading: 45,
      altitude: 1000,
    });

    movingItem3D.modify({
      lngLat: [
        itemLngLat.lng + Math.sin(progress * Math.PI * 2) * 0.025,
        itemLngLat.lat,
      ],
      heading: progress * Math.PI * 100,
    });

    const mesh2IntersectsMesh1 = movingItem3D.intersects(leftCubeItem3D, "low");
    const mesh2IntersectsMesh3 = movingItem3D.intersects(sphereRightItem3D, "low");

    if (mesh2IntersectsMesh1) {
      recursivelySetMaterialColor(leftCubeMesh, "green");
    } else if (mesh2IntersectsMesh3) {
      recursivelySetMaterialColor(sphereRightMesh, "blue");
    } else {
      recursivelySetMaterialColor(leftCubeMesh, "red");
      recursivelySetMaterialColor(sphereRightMesh, "red");
    }
  }

  loop();

})()

function recursivelySetMaterialColor(mesh, color) {
  if (mesh instanceof THREE.Mesh) {
    mesh.material.userData.oldMap = mesh.material.map;
    mesh.material.map = null;
    mesh.material.color.set(color);
  } else if (mesh instanceof Group) {
    mesh.traverse((node) => {
      if (node instanceof THREE.Mesh) {
        console.log(node);
        node.material.userData.oldMap = node.material.map;
        node.material.map = null;
        node.material.color.set(color);
      }
    });
  }
}

function addDebugCheckBox(objects) {
  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  const label = document.createElement("label");
  label.style.position = "absolute";
  label.style.top = "10px";
  label.style.left = "10px";
  label.style.zIndex = "1000";
  label.style.background = "rgba(255,255,255,0.7)";
  label.style.padding = "4px 8px";
  label.style.borderRadius = "3px";
  label.style.fontSize = "14px";
  label.appendChild(checkbox);
  label.appendChild(document.createTextNode(" Show debug bounds"));
  checkbox.addEventListener("change", () => {
    objects.forEach((object) => {
      object.debug = checkbox.checked;
    });
  });
  document.body.appendChild(label);
}
```

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="robots" content="noindex">
  <title>MapTiler 3D | Item3D Intersections</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 {
  margin: 0;
  padding: 0;
}

#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}
```

### Basic JavaScript setup

```html
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>MapTiler 3D | Item3D Intersections</title>
  <script src="https://cdn.jsdelivr.net/npm/lil-gui@0.19"></script>
  <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" />
  <!-- Importing MapTiler 3D js -->
  <script src="https://cdn.maptiler.com/maptiler-3d/v4.0.1/maptiler-3d.umd.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
  </style>
</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>
  <script type="module">
    import * as THREE from 'three';
    maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

    const center = [-11.40318, 21.12953];

    const map = new maptilersdk.Map({
      container: document.getElementById('map'),
      hash: true,
      style: maptilersdk.MapStyle.SATELLITE,
      zoom: 12.63,
      center: center,
      pitch: 47,
      maxPitch: 85,
      terrainControl: true,
      terrain: true,
      maptilerLogo: true,
    });

    (async () => {
      await map.onReadyAsync();

      const layer3D = new maptiler3d.Layer3D("item3d-intersections");
      map.addLayer(layer3D);

      // Adding a point light
      layer3D.addPointLight("light", {
        lngLat: [
          center[0],
          center[1],
        ],
        intensity: 10,
        decay: 0.1,
        color: "#ffffff",
        altitude: 6000,
      });

      // Adding a mesh. 
      const leftCubeMesh = new THREE.Mesh(new THREE.BoxGeometry(400, 400, 400), new THREE.MeshStandardMaterial({ color: "red", metalness: 0.25, roughness: 0.5 }));
      leftCubeMesh.name = "static-cube-1";
      const leftCubeItem3D = layer3D.addMesh("mesh1", leftCubeMesh, {
        altitudeReference: maptiler3d.AltitudeReference.MEAN_SEA_LEVEL,
        lngLat: [
          center[0] - 0.02,
          center[1],
        ],
      });

      leftCubeItem3D.modify({
        heading: 45,
        altitude: 1000,
      });

      const movingItem3D = await layer3D.addMeshFromURL("duck", "https://docs-media.maptiler.com/docs/models/duck.glb", {
        lngLat: center,
        heading: 0,
        scale: 180,
        altitude: 950,
        altitudeReference: maptiler3d.AltitudeReference.MEAN_SEA_LEVEL,
      });

      const sphereRightMesh = new THREE.Mesh(new THREE.SphereGeometry(250, 32, 32), new THREE.MeshStandardMaterial({ color: "red", roughness: 0.5 }));
      sphereRightMesh.name = "static-sphere";
      const sphereRightItem3D = layer3D.addMesh("sphere", sphereRightMesh, {
        lngLat: [
          center[0] + 0.02,
          center[1],
        ],
        altitude: 1000,
        altitudeReference: maptiler3d.AltitudeReference.MEAN_SEA_LEVEL,
      });

      const itemLngLat = movingItem3D.lngLat;
      let progress = 0;

      addDebugCheckBox([leftCubeItem3D, movingItem3D, sphereRightItem3D]);

      function loop() {
        requestAnimationFrame(loop);
        progress += 0.0005;

        leftCubeItem3D.modify({
          heading: 45,
          altitude: 1000,
        });

        movingItem3D.modify({
          lngLat: [
            itemLngLat.lng + Math.sin(progress * Math.PI * 2) * 0.025,
            itemLngLat.lat,
          ],
          heading: progress * Math.PI * 100,
        });

        const mesh2IntersectsMesh1 = movingItem3D.intersects(leftCubeItem3D, "low");
        const mesh2IntersectsMesh3 = movingItem3D.intersects(sphereRightItem3D, "low");

        if (mesh2IntersectsMesh1) {
          recursivelySetMaterialColor(leftCubeMesh, "green");
        } else if (mesh2IntersectsMesh3) {
          recursivelySetMaterialColor(sphereRightMesh, "blue");
        } else {
          recursivelySetMaterialColor(leftCubeMesh, "red");
          recursivelySetMaterialColor(sphereRightMesh, "red");
        }
      }

      loop();

    })()

    function recursivelySetMaterialColor(mesh, color) {
      if (mesh instanceof THREE.Mesh) {
        mesh.material.userData.oldMap = mesh.material.map;
        mesh.material.map = null;
        mesh.material.color.set(color);
      } else if (mesh instanceof Group) {
        mesh.traverse((node) => {
          if (node instanceof THREE.Mesh) {
            console.log(node);
            node.material.userData.oldMap = node.material.map;
            node.material.map = null;
            node.material.color.set(color);
          }
        });
      }
    }

    function addDebugCheckBox(objects) {
      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      const label = document.createElement("label");
      label.style.position = "absolute";
      label.style.top = "10px";
      label.style.left = "10px";
      label.style.zIndex = "1000";
      label.style.background = "rgba(255,255,255,0.7)";
      label.style.padding = "4px 8px";
      label.style.borderRadius = "3px";
      label.style.fontSize = "14px";
      label.appendChild(checkbox);
      label.appendChild(document.createTextNode(" Show debug bounds"));
      checkbox.addEventListener("change", () => {
        objects.forEach((object) => {
          object.debug = checkbox.checked;
        });
      });
      document.body.appendChild(label);
    }
  </script>
</body>

</html>
```

[COLLADA duck](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main) One texture Credit: © 2006, Sony. [SCEA Shared Source License, Version 1.0](https://spdx.org/licenses/SCEA.html) [↩](#fnref:1)

## Related examples

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

Examples

Rotate 3D models by setting the pitch tilt property.

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

Examples

Listen for mouse events on 3D models in maps.

### [3D model pitch and roll](https://docs.maptiler.com/sdk-js/examples/3d-js-roll-pitch/)

Examples

Rotate 3D models using pitch and roll properties.

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

Examples

Animate 3D models and display them on a map.
