> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/mapbox-gl-js/llms.txt)

# Animate the camera around a point with 3D terrain

Use the [`FreeCamera`](https://docs.mapbox.com/mapbox-gl-js/api/properties/#freecameraoptions) API to create a fly-over animation focused on a point.

> Example code:

**JavaScript**

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Animate the camera around a point with 3D terrain</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.28.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.28.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>

<script>
    const map = new mapboxgl.Map({
        // TO MAKE THE MAP APPEAR YOU MUST
        // ADD YOUR ACCESS TOKEN FROM
        // https://account.mapbox.com
        accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
        container: 'map',
        zoom: 11.53,
        center: [138.7189, 35.1691],
        pitch: 76,
        bearing: -177.2,
        // Choose from Mapbox's core styles, or make your own style with Mapbox Studio
        style: 'mapbox://styles/mapbox/standard-satellite',
        config: {
            basemap: {
                showPedestrianRoads: false,
                showPlaceLabels: false,
                showPointOfInterestLabels: false,
                showRoadLabels: false,
                showTransitLabels: false,
                showAdminBoundaries: false
            }
        },
        interactive: false
    });

    map.on('style.load', () => {
        map.setFog({}); // Set the default atmosphere style

        // Add terrain
        map.addSource('mapbox-dem', {
            'type': 'raster-dem',
            'url': 'mapbox://mapbox.mapbox-terrain-dem-v1',
            'tileSize': 512,
            'maxzoom': 14
        });
        map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1.5 });
    });

    function updateCameraPosition(position, altitude, target) {
        const camera = map.getFreeCameraOptions();

        camera.position = mapboxgl.MercatorCoordinate.fromLngLat(
            position,
            altitude
        );
        camera.lookAtPoint(target);

        map.setFreeCameraOptions(camera);
    }

    let animationIndex = 0;
    let animationTime = 0.0;

    // wait for the terrain to load before starting animations
    map.once('idle', () => {
        // linearly interpolate between two altitudes/positions based on time
        const lerp = (a, b, t) => {
            if (Array.isArray(a) && Array.isArray(b)) {
                const result = [];
                for (let i = 0; i < Math.min(a.length, b.length); i++)
                    result[i] = a[i] * (1.0 - t) + b[i] * t;
                return result;
            } else {
                return a * (1.0 - t) + b * t;
            }
        };

        const animations = [
            {
                duration: 20000.0,
                animate: (phase) => {
                    const start = [138.73375, 35.41914];
                    const end = [138.72649, 35.33974];
                    const alt = [7000.0, 6000.0];

                    // interpolate camera position while keeping focus on a target lat/lng
                    const position = lerp(start, end, phase);
                    const altitude = lerp(alt[0], alt[1], phase);
                    const target = [138.73036, 35.36197];

                    updateCameraPosition(position, altitude, target);
                }
            },
            {
                duration: 15000.0,
                animate: (phase) => {
                    const start = [138.72649, 35.33974];
                    const end = [138.72623, 35.31977];
                    const alt = [6000.0, 6000.0];
                    const target1 = [138.73036, 35.36197];
                    const target2 = [138.74831, 35.34784];

                    // interpolate both the camera position and target
                    const position = lerp(start, end, phase);
                    const altitude = lerp(alt[0], alt[1], phase);
                    const target = lerp(target1, target2, phase);

                    updateCameraPosition(position, altitude, target);
                }
            },
            {
                duration: 15000.0,
                animate: (phase) => {
                    // create easing function for the animation
                    const easeInOutQuad = (t) => {
                        return t < 0.5
                            ? 2.0 * t * t
                            : (4.0 - 2.0 * t) * t - 1.0;
                    };
                    const start = [138.72623, 35.31977];
                    const end = [138.73375, 35.41914];
                    const alt = [6000.0, 7000.0];
                    const target1 = [138.74831, 35.34784];
                    const target2 = [138.73036, 35.36197];

                    // interpolate both the camera position and target
                    const position = lerp(start, end, easeInOutQuad(phase));
                    const altitude = lerp(alt[0], alt[1], phase);
                    const target = lerp(target1, target2, phase);

                    updateCameraPosition(position, altitude, target);
                }
            }
        ];

        let lastTime = 0.0;
        function frame(time) {
            animationIndex %= animations.length;
            const current = animations[animationIndex];

            if (animationTime < current.duration) {
                // Normalize the duration between 0 and 1 to interpolate the animation
                const phase = animationTime / current.duration;
                current.animate(phase);
            }

            // Elasped time since last frame, in milliseconds
            const elapsed = time - lastTime;
            animationTime += elapsed;
            lastTime = time;

            if (animationTime > current.duration) {
                animationIndex++;
                animationTime = 0.0;
            }

            window.requestAnimationFrame(frame);
        }

        window.requestAnimationFrame(frame);
    });
</script>

</body>
</html>
```

**React**

```jsx
import React, { useEffect, useRef } from 'react';
import * as mapboxgl from 'mapbox-gl/esm';

import 'mapbox-gl/dist/mapbox-gl.css';

const MapboxExample = () => {
  const mapContainerRef = useRef();
  const mapRef = useRef();

  useEffect(() => {
    mapRef.current = new mapboxgl.Map({
      // TO MAKE THE MAP APPEAR YOU MUST
      // ADD YOUR ACCESS TOKEN FROM
      // https://account.mapbox.com
      accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
      container: mapContainerRef.current,
      zoom: 11.53,
      center: [138.7189, 35.1691],
      pitch: 76,
      bearing: -177.2,
      style: 'mapbox://styles/mapbox/standard-satellite',
      config: {
        basemap: {
          showPedestrianRoads: false,
          showPlaceLabels: false,
          showPointOfInterestLabels: false,
          showRoadLabels: false,
          showTransitLabels: false,
          showAdminBoundaries: false
        }
      },
      interactive: false
    });

    mapRef.current.on('style.load', () => {
      mapRef.current.setFog({});

      mapRef.current.addSource('mapbox-dem', {
        type: 'raster-dem',
        url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
        tileSize: 512,
        maxzoom: 14
      });
      mapRef.current.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 });
    });

    function updateCameraPosition(position, altitude, target) {
      const camera = mapRef.current.getFreeCameraOptions();

      camera.position = mapboxgl.MercatorCoordinate.fromLngLat(
        position,
        altitude
      );
      camera.lookAtPoint(target);

      mapRef.current.setFreeCameraOptions(camera);
    }

    let animationIndex = 0;
    let animationTime = 0.0;

    mapRef.current.once('idle', () => {
      const lerp = (a, b, t) => {
        if (Array.isArray(a) && Array.isArray(b)) {
          const result = [];
          for (let i = 0; i < Math.min(a.length, b.length); i++)
            result[i] = a[i] * (1.0 - t) + b[i] * t;
          return result;
        } else {
          return a * (1.0 - t) + b * t;
        }
      };

      const animations = [
        {
          duration: 20000.0,
          animate: (phase) => {
            const start = [138.73375, 35.41914];
            const end = [138.72649, 35.33974];
            const alt = [7000.0, 6000.0];
            const target = [138.73036, 35.36197];

            const position = lerp(start, end, phase);
            const altitude = lerp(alt[0], alt[1], phase);

            updateCameraPosition(position, altitude, target);
          }
        },
        {
          duration: 15000.0,
          animate: (phase) => {
            const start = [138.72649, 35.33974];
            const end = [138.72623, 35.31977];
            const alt = [6000.0, 6000.0];
            const target1 = [138.73036, 35.36197];
            const target2 = [138.74831, 35.34784];

            const position = lerp(start, end, phase);
            const altitude = lerp(alt[0], alt[1], phase);
            const target = lerp(target1, target2, phase);

            updateCameraPosition(position, altitude, target);
          }
        },
        {
          duration: 15000.0,
          animate: (phase) => {
            const easeInOutQuad = (t) => {
              return t < 0.5 ? 2.0 * t * t : (4.0 - 2.0 * t) * t - 1.0;
            };
            const start = [138.72623, 35.31977];
            const end = [138.73375, 35.41914];
            const alt = [6000.0, 7000.0];
            const target1 = [138.74831, 35.34784];
            const target2 = [138.73036, 35.36197];

            const position = lerp(start, end, easeInOutQuad(phase));
            const altitude = lerp(alt[0], alt[1], phase);
            const target = lerp(target1, target2, phase);

            updateCameraPosition(position, altitude, target);
          }
        }
      ];

      let lastTime = 0.0;
      function frame(time) {
        animationIndex %= animations.length;
        const current = animations[animationIndex];

        if (animationTime < current.duration) {
          const phase = animationTime / current.duration;
          current.animate(phase);
        }

        const elapsed = time - lastTime;
        animationTime += elapsed;
        lastTime = time;

        if (animationTime > current.duration) {
          animationIndex++;
          animationTime = 0.0;
        }

        window.requestAnimationFrame(frame);
      }

      window.requestAnimationFrame(frame);
    });
  }, []);

  return <div id="map" ref={mapContainerRef} style={{ height: '100%' }}></div>;
};

export default MapboxExample;
```