Skip to content

Animate a Marker in Maptoolkit Maps JS

Moving a marker smoothly across a map requires updating its longitude and latitude on every animation frame using setLngLat, then scheduling the next frame with requestAnimationFrame. The marker’s position is calculated from elapsed time so the speed stays consistent regardless of frame rate. Use this for tracking moving objects like vehicles, showing a user walking a route, or animating a point of interest into view.

const API_KEY = 'YOUR_API_KEY';

    const center = [11.39085, 47.27574];
    const radius = 0.05;

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center,
        zoom: 11,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    const marker = new maptoolkit.Marker({ color: '#ee8a65' })
        .setLngLat(center)
        .addTo(map);

    function animate(timestamp) {
        const t = (timestamp / 4000) % 1;
        const angle = t * 2 * Math.PI;
        marker.setLngLat([
            center[0] + radius * Math.cos(angle) / Math.cos(center[1] * Math.PI / 180),
            center[1] + radius * Math.sin(angle)
        ]);
        requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animate a Marker - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Animate a marker's position using setLngLat on each animation frame." />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.css" />
    <style>
        html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
        #map { width: 100%; height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script>
    const API_KEY = 'YOUR_API_KEY';

    const center = [11.39085, 47.27574];
    const radius = 0.05;

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center,
        zoom: 11,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    const marker = new maptoolkit.Marker({ color: '#ee8a65' })
        .setLngLat(center)
        .addTo(map);

    function animate(timestamp) {
        const t = (timestamp / 4000) % 1;
        const angle = t * 2 * Math.PI;
        marker.setLngLat([
            center[0] + radius * Math.cos(angle) / Math.cos(center[1] * Math.PI / 180),
            center[1] + radius * Math.sin(angle)
        ]);
        requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
</script>
</body>
</html>

Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.

Use the Maptoolkit Connector. Create an interactive map with zoom level 11, centered around [11.39085, 47.27574]. Place a marker that animates in a circle around the center.

How it works

A Marker is a DOM element the library keeps positioned over the canvas, so moving it is setLngLat on each frame. There is no source to update and no data to re-upload, which makes this the cheapest way to animate exactly one thing.

requestAnimationFrame is the right timer here, not setInterval. It runs in step with the browser’s paint cycle, so frames are not computed and then thrown away, and it pauses automatically when the tab is hidden.

The trade-off is scale. One marker animating is cheaper than a source update; fifty are far more expensive, because each is a real element the browser lays out every frame. Past a handful, animate a circle or symbol layer through setData instead.

Marker movement is not interpolated for you. A position set every frame looks smooth, one set every second looks like teleporting, which is why a slow data feed needs interpolating between the points you receive.

Next steps

The next decision is usually how many. One marker animating is comfortable, a fleet is not, and moving to a point layer before that becomes a problem is easier than retrofitting it after.

Beyond movement, a marker that rotates to face its direction of travel reads far better, and that means keeping the previous position to compute a bearing from.