Select platform: [Android](https://developers.google.com/maps/documentation/android-sdk/views.md.txt "View this page for the Android platform docs.") [iOS](https://developers.google.com/maps/documentation/ios-sdk/views.md.txt "View this page for the iOS platform docs.") [JavaScript](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt "View this page for the JavaScript platform docs.")

<br />


<iframe src="https://geo-devrel-javascript-samples.web.app/samples/webgl-tilt-rotation/app/dist/" allow="fullscreen; "></iframe>

[View Sample](https://developers.google.com/maps/documentation/javascript/examples/webgl/webgl-tilt-rotation.md.txt)

The Maps JavaScript API offers two different implementations of the map: raster
and vector. The raster map loads the map as a grid of pixel-based raster image
tiles, which are generated by Google Maps Platform server-side, then served to
your web app. The vector map is a composed of vector-based tiles, which are
drawn at load time on the client-side using WebGL, a web technology that allows
the browser to access the GPU on the user's device to render 2D and 3D graphics.

The vector map is the same Google map your users are familiar with using, and
offers a number of advantages over the default raster tile map, most notably
the sharpness of vector-based images, and the addition of 3D buildings at close
zoom levels. The vector map supports these features:

- [Programmatic tilt and heading control](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#tilt-rotation)
- [Enhanced camera control](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#control-camera)
- [Fractional zoom for smoother zooming](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#fractional-zoom)
- [3D content and animations](https://developers.google.com/maps/documentation/javascript/webgl/webgl-overlay-view.md.txt)

- For maps loaded using a `div` element and JavaScript, the default rendering
  type is `google.maps.RenderingType.RASTER`.

- For maps loaded using the `gmp-map` element, the default rendering type is
  `google.maps.RenderingType.VECTOR`, with tilt and heading control enabled.

[Get started with Vector Maps](https://developers.google.com/maps/documentation/javascript/webgl.md.txt)

## Tilt and rotation

You can set tilt and rotation (heading) on the [vector map](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt)
by including the `heading` and `tilt` properties when initializing the map, and
by calling the `setTilt` and `setHeading` methods on the map. The following
example adds some buttons to the map which show programmatically adjusting tilt
and heading in 20-degree increments.


### TypeScript

```typescript
function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: {
        lat: 37.7893719,
        lng: -122.3942,
      },
      zoom: 16,
      heading: 320,
      tilt: 47.5,
      mapId: "90f87356969d889c",
    }
  );

  const buttons: [string, string, number, google.maps.ControlPosition][] = [
    ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER],
    ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER],
    ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER],
    ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER],
  ];

  buttons.forEach(([text, mode, amount, position]) => {
    const controlDiv = document.createElement("div");
    const controlUI = document.createElement("button");

    controlUI.classList.add("ui-button");
    controlUI.innerText = `${text}`;
    controlUI.addEventListener("click", () => {
      adjustMap(mode, amount);
    });
    controlDiv.appendChild(controlUI);
    map.controls[position].push(controlDiv);
  });

  const adjustMap = function (mode: string, amount: number) {
    switch (mode) {
      case "tilt":
        map.setTilt(map.getTilt()! + amount);
        break;
      case "rotate":
        map.setHeading(map.getHeading()! + amount);
        break;
      default:
        break;
    }
  };
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;
```

> [!NOTE]
> **Note:** Read the [guide](https://developers.google.com/maps/documentation/javascript/using-typescript.md.txt) on using TypeScript and Google Maps.

### JavaScript

```javascript
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: {
      lat: 37.7893719,
      lng: -122.3942,
    },
    zoom: 16,
    heading: 320,
    tilt: 47.5,
    mapId: "90f87356969d889c",
  });
  const buttons = [
    ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER],
    ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER],
    ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER],
    ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER],
  ];

  buttons.forEach(([text, mode, amount, position]) => {
    const controlDiv = document.createElement("div");
    const controlUI = document.createElement("button");

    controlUI.classList.add("ui-button");
    controlUI.innerText = `${text}`;
    controlUI.addEventListener("click", () => {
      adjustMap(mode, amount);
    });
    controlDiv.appendChild(controlUI);
    map.controls[position].push(controlDiv);
  });

  const adjustMap = function (mode, amount) {
    switch (mode) {
      case "tilt":
        map.setTilt(map.getTilt() + amount);
        break;
      case "rotate":
        map.setHeading(map.getHeading() + amount);
        break;
      default:
        break;
    }
  };
}

window.initMap = initMap;
```

> [!NOTE]
> **Note:** The JavaScript is compiled from the TypeScript snippet.

### CSS

```css
/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

.ui-button {
  background-color: #fff;
  border: 0;
  border-radius: 2px;
  box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
  margin: 10px;
  padding: 0 0.5em;
  font: 400 18px Roboto, Arial, sans-serif;
  overflow: hidden;
  height: 40px;
  cursor: pointer;
}
.ui-button:hover {
  background: rgb(235, 235, 235);
}
```

### HTML

```html
<html>
  <head>
    <title>Tilt and Rotation</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="map"></div>

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
```

<br />

### Use mouse and keyboard gestures

If tilt and rotation (heading) user interactions have been enabled (either
programmatically or in the Google Cloud Console), then users can adjust the tilt
and rotation using the mouse and keyboard:

- **Using the mouse**, hold down the shift key, then drag the mouse up and down to adjust tilt, right and left to adjust heading.
- **Using the keyboard**, hold down the shift key, then use the up and down arrow keys to adjust tilt, and the right and left arrow keys to adjust heading.

### Programmatically adjust tilt and heading

Use the [`setTilt()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setTilt)
and
[`setHeading()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setHeading)
methods to programmatically adjust tilt and heading on a vector map. Heading is
the direction the camera is facing in clockwise degrees starting north, so
[`map.setHeading(90)`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setHeading)
will rotate the map so that east is facing up. The tilt angle is measured from
the zenith, so
[`map.setTilt(0)`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setTilt) is
looking straight down, while
[`map.setTilt(45)`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setTilt)
will result in an oblique view.

- **Tilt:** Call [`setTilt()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setTilt) to set the tilt angle of the map. Use [`getTilt()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.getTilt) to get the current tilt value.
- **Heading:** Call [`setHeading()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setHeading) to set the heading of the map. Use [`getHeading()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.getHeading) to get the current heading value.

To change the map center while preserving tilt and heading, use
[`map.setCenter()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setCenter)
or [`map.panBy()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.panBy).

The allowed range of tilt angles depends on the current zoom level of the map.
The minimum tilt angle is always 0 degrees (looking straight down), and the
maximum tilt angle increases with the zoom level (interpolating linearly by
3.75 degrees per zoom level from zoom 10 to 14, and by 15 degrees per zoom
level from zoom 14 to 15.5). Values outside the allowed range for the current
zoom level are clamped to the nearest allowed bound:

| Zoom level | Allowed tilt range | Maximum tilt angle |
|---|---|---|
| 10 or lower | 0° to 30° | 30° |
| 11 | 0° to 33.75° | 33.75° |
| 12 | 0° to 37.5° | 37.5° |
| 13 | 0° to 41.25° | 41.25° |
| 14 | 0° to 45° | 45° |
| 14.5 | 0° to 52.5° | 52.5° |
| 15 | 0° to 60° | 60° |
| 15.5 or higher | 0° to 67.5° | 67.5° |

You can also use the
[`moveCamera()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.moveCamera)
method to programmatically change heading, tilt, center, and zoom.
[Learn more](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#control-camera).

### Impact on other methods

When tilt or rotation is applied to the map, the behavior of other
Maps JavaScript API methods is affected:

- [`map.getBounds()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.getBounds) always returns the smallest bounding box that includes the visible region; when tilt is applied, the returned bounds may represent a larger region than the visible region of the map's viewport.
- [`map.fitBounds()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.fitBounds) will reset tilt and heading to zero prior to fitting the bounds.
- [`map.panToBounds()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.panToBounds) will reset tilt and heading to zero prior to panning the bounds.
- [`map.setTilt()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setTilt) accepts any value, but restricts the maximum tilt based on the current map zoom level.
- [`map.setHeading()`](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#Map.setHeading) accepts any value, and will modify it to fit into the range 0 to 360 degrees.

## Control the camera

Use the `map.moveCamera()` function to update any combination of camera
properties at once. `map.moveCamera()` accepts a single parameter containing
all of the camera properties to update. The following example shows calling
`map.moveCamera()` to set `center`, `zoom`, `heading`, and `tilt` at once:

```javascript
map.moveCamera({
  center: new google.maps.LatLng(37.7893719, -122.3942),
  zoom: 16,
  heading: 320,
  tilt: 47.5
});
```

You can animate camera properties by calling `map.moveCamera()` with an
animation loop, as shown here:

```javascript
const degreesPerSecond = 3;

function animateCamera(time) {
  // Update the heading, leave everything else as-is.
  map.moveCamera({
    heading: (time / 1000) * degreesPerSecond
  });

  requestAnimationFrame(animateCamera);
}

// Start the animation.
requestAnimationFrame(animateCamera);
```

## The camera position

The map view is modeled as a **camera** looking down on a flat plane. The position
of the camera (and hence the rendering of the map) is specified by the
following properties: [target (latitude/longitude location)](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#target_location),
[bearing](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#bearing_orientation), [tilt](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#tilt_viewing_angle),
and [zoom](https://developers.google.com/maps/documentation/javascript/vector-map.md.txt#zoom).
:camera:

### Target (location)

The camera target is the location of the center of the map, specified as
latitude and longitude coordinates.
The latitude can be between -85 and 85 degrees, inclusive. Values above or below this range will be clamped to the nearest value within this range. For example, specifying a latitude of 100 will set the value to 85. Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped such that they fall within the range (-180, 180). For example, 480, 840 and 1200 will all be wrapped to 120 degrees.

### Bearing (orientation)

The camera bearing specifies the compass direction, measured in degrees from true north, corresponding
to the top edge of the map. If you draw a vertical line from the center of the map to the top edge
of the map, the bearing corresponds to the heading of the camera (measured in degrees) relative
to true north.

A bearing of 0 means that the top of the map points to true north.
A bearing value 90 means the top of the map points due east (90 degrees on a compass). A value
180 means the top of the map points due south.

The Maps API lets you change a map's bearing. For example, someone driving a car
often turns a road map to align it with their direction of travel, while hikers using a map and
compass usually orient the map so that a vertical line is pointing north.

### Tilt (viewing angle)

The tilt defines the camera's position on an arc directly over the map's
center position, measured in degrees from the
[nadir](https://en.wikipedia.org/wiki/Nadir)
(the direction pointing directly below the camera). A value of 0 corresponds to a camera pointed
straight down. Values greater than 0 correspond to a camera that is pitched toward the horizon by
the specified number of degrees.

When you change the viewing angle, the map appears in perspective, with far-away features
appearing smaller, and nearby features appearing larger. The following
illustrations demonstrate this.

In the images below, the viewing angle is 0 degrees. The first image shows a
schematic of this; position **1** is the camera position, and position **2**
is the current map position. The resulting map is shown below it.

|---|---|
| ![Screenshot of a map with a camera positioned at 0 degrees viewing angle, at a zoom level of 18.](https://developers.google.com/static/maps/documentation/android-sdk/images/camera-angle-0-shot.png) The map displayed with the camera's default viewing angle. | ![Diagram that shows the default position of the camera, directly over the map position, at an angle of 0 degrees.](https://developers.google.com/static/maps/documentation/android-sdk/images/camera_0.png) The default viewing angle of the camera. |

In the images below, the viewing angle is 45 degrees. Notice that the camera
moves halfway along an arc between straight overhead (0 degrees) and the ground (90 degrees),
to position **3** . The camera is still pointing at the map's center point, but the area
represented by the line at position **4** is now visible.

|---|---|
| ![Screenshot of a map with a camera positioned at 45 degrees viewing angle, at a zoom level of 18.](https://developers.google.com/static/maps/documentation/android-sdk/images/camera-angle-45-shot.png) The map displayed with a viewing angle of 45 degrees. | ![Diagram that shows the camera's viewing angle set to 45 degrees, with the zoom level still set to 18.](https://developers.google.com/static/maps/documentation/android-sdk/images/camera_45.png) A camera viewing angle of 45 degrees. |

The map in this screenshot is still centered on the same point as in the
original map, but more features have appeared at the top of the map. As you
increase the angle beyond 45 degrees, features between the camera and the map
position appear proportionally larger, while features beyond the map position
appear proportionally smaller, yielding a three-dimensional effect.

### Zoom

The zoom level of the camera determines the scale of the map. At larger zoom
levels more detail can be seen on the screen, while at smaller zoom levels
more of the world can be seen on the screen.


The zoom level need not be an integer. The range of zoom
levels permitted by the map depends on a number of factors including target,
map type and screen size. Any number out of the range will be converted to the
next closest valid value, which can be either the minimum zoom level or the
maximum zoom level. The following list shows the approximate level of detail
you can expect to see at each zoom level:

- 1: World
- 5: Landmass/continent
- 10: City
- 15: Streets
- 20: Buildings

> [!NOTE]
> **Note:**Due to screen size and density, some devices may not support the lowest zoom levels.

The following images show the visual appearance of different zoom levels:

|---|---|---|
| ![Screenshot of a map at a zoom level of 5](https://developers.google.com/static/maps/documentation/javascript/images/zoom-level-6.png) A map at zoom level 5. | ![Screenshot of a map at a zoom level of 15](https://developers.google.com/static/maps/documentation/javascript/images/zoom-level-14.png) A map at zoom level 15. | ![Screenshot of a map at zoom level 20](https://developers.google.com/static/maps/documentation/javascript/images/zoom-level-19.png) A map at zoom level 20. |

## Fractional Zoom

Vector maps support fractional zoom, which lets you zoom using fractional
values instead of integers. While both raster and vector maps support fractional
zoom, fractional zoom is on by default for vector maps, and off by default for
raster maps. Use the `isFractionalZoomEnabled` map option to turn fractional
zoom on and off.

The following example shows enabling fractional zoom when initializing the map:

```javascript
map = new google.maps.Map(document.getElementById('map'), {
  center: {lat: -34.397, lng: 150.644},
  zoom: 8,
  isFractionalZoomEnabled: true
});
```

You can also turn fractional zoom on and off by setting the
`isFractionalZoomEnabled` map option as shown here:

```javascript
// Using map.set
map.set('isFractionalZoomEnabled', true);

// Using map.setOptions
map.setOptions({isFractionalZoomEnabled: true});
```

You can set a listener to detect whether fractional zoom is turned on; this is
most useful if you have not explicitly set `isFractionalZoomEnabled` to `true`
or `false`. The following example code checks to see whether fractional zoom
is enabled:

```javascript
map.addListener('isfractionalzoomenabled_changed', () => {
  const isFractionalZoomEnabled = map.get('isFractionalZoomEnabled');
  if (isFractionalZoomEnabled === false) {
    console.log('not using fractional zoom');
  } else if (isFractionalZoomEnabled === true) {
    console.log('using fractional zoom');
  } else {
    console.log('map not done initializing yet');
  }
});
```

> [!CAUTION]
> **Caution:** The `zoom_changed` event will fire more frequently when fractional zoom is enabled. This will affect any listeners set up to track `zoom_changed`. For optimal performance, consider updating only if the zoom has changed by an integer value, or apply throttling.