diff --git a/debug/overlays.html b/debug/overlays.html
new file mode 100644
index 00000000000..17e3ee66487
--- /dev/null
+++ b/debug/overlays.html
@@ -0,0 +1,66 @@
+
+
+
+ Mapbox GL JS debug page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/mapbox-gl.css b/dist/mapbox-gl.css
index 6ea321990cd..fc1fed2368c 100644
--- a/dist/mapbox-gl.css
+++ b/dist/mapbox-gl.css
@@ -229,6 +229,13 @@
border-bottom-right-radius: 0;
}
+.mapboxgl-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ will-change: transform;
+}
+
.mapboxgl-crosshair,
.mapboxgl-crosshair .mapboxgl-interactive,
.mapboxgl-crosshair .mapboxgl-interactive:active {
diff --git a/js/mapbox-gl.js b/js/mapbox-gl.js
index 7da4e9f24c1..35a2226e71f 100644
--- a/js/mapbox-gl.js
+++ b/js/mapbox-gl.js
@@ -11,6 +11,7 @@ mapboxgl.Navigation = require('./ui/control/navigation');
mapboxgl.Geolocate = require('./ui/control/geolocate');
mapboxgl.Attribution = require('./ui/control/attribution');
mapboxgl.Popup = require('./ui/popup');
+mapboxgl.Overlay = require('./ui/overlay');
mapboxgl.GeoJSONSource = require('./source/geojson_source');
mapboxgl.VideoSource = require('./source/video_source');
diff --git a/js/ui/overlay.js b/js/ui/overlay.js
new file mode 100644
index 00000000000..1e4eb1cddd5
--- /dev/null
+++ b/js/ui/overlay.js
@@ -0,0 +1,87 @@
+/* eslint-disable */
+'use strict';
+
+module.exports = Overlay;
+
+var DOM = require('../util/dom');
+var LngLat = require('../geo/lng_lat');
+
+/**
+ * Creates an overlay component
+ * @class Overlay
+ * @param {HTMLElement=} element DOM element to use as an overlay (creates a div element by default)
+ * @example
+ * var overlay = new mapboxgl.Overlay()
+ * .setLngLat([30.5, 50.5])
+ * .addTo(map);
+ */
+function Overlay(element) {
+ if (!element) {
+ element = DOM.create('div');
+ }
+ element.classList.add('mapboxgl-overlay');
+ this._el = element;
+ this._update = this._update.bind(this);
+}
+
+Overlay.prototype = /** @lends Overlay.prototype */{
+ /**
+ * Attaches the overlay to a map
+ * @param {Map} map
+ * @returns {Overlay} `this`
+ */
+ addTo: function(map) {
+ this.remove();
+ this._map = map;
+ map.getCanvasContainer().appendChild(this._el);
+ map.on('move', this._update);
+ this._update();
+ return this;
+ },
+
+ /**
+ * Removes the overlay from a map
+ * @example
+ * var overlay = new mapboxgl.Overlay().addTo(map);
+ * overlay.remove();
+ * @returns {Overlay} `this`
+ */
+ remove: function() {
+ if (this._map) {
+ this._map.off('move', this._update);
+ this._map = null;
+ }
+ var parent = this._el.parentNode;
+ if (parent) parent.removeChild(this._el);
+ return this;
+ },
+
+ /**
+ * Get the overlay's geographical location
+ * @returns {LngLat}
+ */
+ getLngLat: function() {
+ return this._lngLat;
+ },
+
+ /**
+ * Set the overlay's geographical position and move it.
+ * @param {LngLat} lnglat
+ * @returns {Popup} `this`
+ */
+ setLngLat: function(lnglat) {
+ this._lngLat = LngLat.convert(lnglat);
+ this._update();
+ return this;
+ },
+
+ getElement: function() {
+ return this._el;
+ },
+
+ _update: function() {
+ if (!this._map) return;
+ var pos = this._map.project(this._lngLat);
+ DOM.setTransform(this._el, 'translate(' + pos.x + 'px,' + pos.y + 'px)');
+ }
+};