---
title: "Custom loading animation effect | Geosplats sdk"
description: "Learn how to load a custom loading animation while the splat model is loading."
---

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

# Custom loading animation effect

This example demonstrates how to add a custom loading animation while the splat model is loading.

You must always initialize a Map instance before adding a `SplatModel`. Initialize the map with the **VOID** style so the system doesn’t render any tiles, leaving the map empty.

Before the model loads, you can display a custom loading animation screen. Then, you can capture the model loading event `splatmodel:load` and, once loading is complete, hide the loading screen or perform any other action like add a basemap.

### NPM module setup

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

#### main.js

```js
---
layout: none
---

import { SplatModel, Map, ReferenceMapStyleDefaults, config } from "https://cdn.maptiler.com/maptiler-geosplats//maptiler-geosplats.mjs";;
import '@maptiler/geosplats/dist/maptiler-geosplats.css';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
const map = new Map({
  apiKey: config.apiKey,
  container: "map",
  basemap: ReferenceMapStyleDefaults.VOID
});

//Show a error message 
map.on('error', (e) => {
  const message = e.message.toLowerCase();
  console.log(message);

  // Hide the loader so any error message is visible
  const loader = document.getElementById("loader");
  if (loader) {
    loader.classList.add("hidden");
  }

  if (message.includes('gpu') || message.includes('apple device')) {
    const errorEl = document.getElementById('webGpuError');
    if (errorEl) {
      errorEl.classList.remove('d-none');
      if (message.includes('gpu')) {
        errorEl.textContent =
          'Your browser does not support WebGPU. It is necessary to use a modern browser that supports it. Also make sure to enable hardware acceleration in your browser settings.';
      } else {
        errorEl.textContent = e.message;
      }
    }
  }
});

let splatModel;

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

  // Capture splat model load event
  map.on("splatmodel:load", () => {
    // Hide the custom loading screen or perform any other desired action after the model has loaded.
    const loader = document.getElementById("loader");
    if (loader) {
      loader.classList.add("hidden");
      setTimeout(() => {
        loader.remove();
      }, 500);
    }

    map.setBasemap('satellite-v4'); //Add a basemap
    
    setTimeout(() => {
      map.zoomIn(); //Zoom in 
    }, 300);
  });

});
```

#### index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex" />
    <title>Loading splat objects in an Empty Map</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div
      id="webGpuError"
      class="d-none position-absolute top-50 start-50 translate-middle w-50"
    ></div>
    <div id="loader">
      <img
        src="https://docs.maptiler.com/styles/style/logo/maptiler-logo-adaptive.svg"
        alt="MapTiler Logo"
        class="maptiler-logo"
      />
      <div class="spinner"></div>
    </div>
    <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;
}

#webGpuError {
  border-radius: 6px;
  border: 1px solid #3174ff;
  padding: 24px;
  z-index: 1001;
}

#loader {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #ffffff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  transition: opacity 0.5s ease-out;
}

#loader.hidden {
  opacity: 0;
  pointer-events: none;
}

.maptiler-logo {
  width: 200px;
  margin-bottom: 24px;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 3px solid rgba(49, 116, 255, 0.1);
  border-top-color: #3174ff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
```

### 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>Loading splat objects in an Empty Map</title>
  <link rel="stylesheet"
    href="https://cdn.maptiler.com/maptiler-geosplats/v1.1.2/maptiler-geosplats.css" />
  <style>
    body {
      font-family: Inter, sans-serif;
      margin: 0;
      overflow: hidden;
    }

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

    #webGpuError {
      border-radius: 6px;
      border: 1px solid #3174ff;
      padding: 24px;
      z-index: 1001;
    }

    #loader {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: #ffffff;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      z-index: 1000;
      transition: opacity 0.5s ease-out;
    }

    #loader.hidden {
      opacity: 0;
      pointer-events: none;
    }

    .maptiler-logo {
      width: 200px;
      margin-bottom: 24px;
    }

    .spinner {
      width: 40px;
      height: 40px;
      border: 3px solid rgba(49, 116, 255, 0.1);
      border-top-color: #3174ff;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    @keyframes spin {
      to {
        transform: rotate(360deg);
      }
    }
  </style>
</head>

<body>
  <div id="webGpuError" class="d-none position-absolute top-50 start-50 translate-middle w-50"></div>
  <div id="loader">
    <img src="https://docs.maptiler.com/styles/style/logo/maptiler-logo-adaptive.svg" alt="MapTiler Logo"
      class="maptiler-logo" />
    <div class="spinner"></div>
  </div>
  <div id="map"></div>
  <script type="module">
    import {
      SplatModel,
      Map,
      ReferenceMapStyleDefaults,
      config,
    } from "https://cdn.maptiler.com/maptiler-geosplats/v1.1.2/maptiler-geosplats.mjs";

    config.apiKey = "YOUR_MAPTILER_API_KEY_HERE";
    const map = new Map({
      apiKey: config.apiKey,
      container: "map",
      basemap: ReferenceMapStyleDefaults.VOID,
    });

    //Show a error message
    map.on("error", (e) => {
      const message = e.message.toLowerCase();
      console.log(message);

      // Hide the loader so any error message is visible
      const loader = document.getElementById("loader");
      if (loader) {
        loader.classList.add("hidden");
      }

      if (message.includes("gpu") || message.includes("apple device")) {
        const errorEl = document.getElementById("webGpuError");
        if (errorEl) {
          errorEl.classList.remove("d-none");
          if (message.includes("gpu")) {
            errorEl.textContent =
              "Your browser does not support WebGPU. It is necessary to use a modern browser that supports it. Also make sure to enable hardware acceleration in your browser settings.";
          } else {
            errorEl.textContent = e.message;
          }
        }
      }
    });

    let splatModel;

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

      // Capture splat model load event
      map.on("splatmodel:load", () => {
        // Hide the custom loading screen or perform any other desired action after the model has loaded.
        const loader = document.getElementById("loader");
        if (loader) {
          loader.classList.add("hidden");
          setTimeout(() => {
            loader.remove();
          }, 500);
        }

        map.setBasemap('satellite-v4'); //Add a basemap

        setTimeout(() => {
          map.zoomIn(); //Zoom in 
        }, 300);
      });
    });
  </script>
</body>

</html>
```

## Learn more

Check out the [MapTiler GeoSplats SDK JS reference](https://docs-qmgv.pages.dev/geosplats-sdk/api)

## Related examples

### [Edit model in custom editor](https://docs-qmgv.pages.dev/geosplats-sdk/examples/custom-editor/)

Examples

Build a custom 3D editor to adjust the position, rotation, and scale of splat models and save their properties to a database.

### [Add splat model](https://docs-qmgv.pages.dev/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.

### [Change basemap](https://docs-qmgv.pages.dev/geosplats-sdk/examples/change-basemap/)

Examples

Learn how to dynamically switch between different MapTiler basemaps while maintaining photorealistic 3D splat models in your scene.

### [Opacity and visibility](https://docs-qmgv.pages.dev/geosplats-sdk/examples/opacity-visibility/)

Examples

Learn how to dynamically control the transparency and visibility of photorealistic splats objects using the MapTiler GeoSplats SDK.
