Skip to content
Animate a Point Along a Route

Animate a Point Along a Route in Maptoolkit Maps JS

Animating a point along a predefined route combines Turf.js’s along function with a frame counter to calculate the current position at each step. The camera can optionally follow the moving point using map.easeTo, creating a smooth tracking effect. Use this pattern to build route replays, delivery tracking demos, or any visualization where a symbol needs to travel a fixed geographic path.

const API_KEY = 'YOUR_API_KEY';

    // Route: Innsbruck → Salzburg → Vienna
    const route = turf.lineString([
        [11.39085, 47.27574],
        [13.0550, 47.8095],
        [15.4395, 47.0707],
        [16.3738, 48.2082]
    ]);

    const lineLength = turf.length(route, { units: 'kilometers' });
    const steps = 500;
    const point = { type: 'Feature', geometry: { type: 'Point', coordinates: route.geometry.coordinates[0] } };

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

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

    let step = 0;
    let animId;

    map.on('load', () => {
        map.addSource('route', { type: 'geojson', data: route });
        map.addSource('point', { type: 'geojson', data: point });

        map.addLayer({
            id: 'route',
            type: 'line',
            source: 'route',
            paint: { 'line-color': '#888', 'line-width': 3, 'line-dasharray': [2, 2] }
        });

        map.addLayer({
            id: 'point',
            type: 'circle',
            source: 'point',
            paint: { 'circle-radius': 10, 'circle-color': '#3887be', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' }
        });

        function animate() {
            const along = turf.along(route, (step / steps) * lineLength, { units: 'kilometers' });
            point.geometry.coordinates = along.geometry.coordinates;
            map.getSource('point').setData(point);
            if (step < steps) {
                step++;
                animId = requestAnimationFrame(animate);
            }
        }
        animate();
    });

    document.getElementById('replay').addEventListener('click', () => {
        cancelAnimationFrame(animId);
        step = 0;
        function animate() {
            const along = turf.along(route, (step / steps) * lineLength, { units: 'kilometers' });
            point.geometry.coordinates = along.geometry.coordinates;
            map.getSource('point').setData(point);
            if (step < steps) {
                step++;
                animId = requestAnimationFrame(animate);
            }
        }
        animate();
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animate a Point Along a Route - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Animate a point marker along a route using Turf.js for interpolation." />
    <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" />
    <script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
    <style>
        html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
        #map { width: 100%; height: 100%; }
        #replay {
            position: absolute;
            top: 10px;
            left: 10px;
            padding: 10px 20px;
            background: #3887be;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 14px;
        }
    </style>
</head>
<body>
<div id="map"></div>
<button id="replay">Replay</button>
<script>
    const API_KEY = 'YOUR_API_KEY';

    // Route: Innsbruck → Salzburg → Vienna
    const route = turf.lineString([
        [11.39085, 47.27574],
        [13.0550, 47.8095],
        [15.4395, 47.0707],
        [16.3738, 48.2082]
    ]);

    const lineLength = turf.length(route, { units: 'kilometers' });
    const steps = 500;
    const point = { type: 'Feature', geometry: { type: 'Point', coordinates: route.geometry.coordinates[0] } };

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

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

    let step = 0;
    let animId;

    map.on('load', () => {
        map.addSource('route', { type: 'geojson', data: route });
        map.addSource('point', { type: 'geojson', data: point });

        map.addLayer({
            id: 'route',
            type: 'line',
            source: 'route',
            paint: { 'line-color': '#888', 'line-width': 3, 'line-dasharray': [2, 2] }
        });

        map.addLayer({
            id: 'point',
            type: 'circle',
            source: 'point',
            paint: { 'circle-radius': 10, 'circle-color': '#3887be', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' }
        });

        function animate() {
            const along = turf.along(route, (step / steps) * lineLength, { units: 'kilometers' });
            point.geometry.coordinates = along.geometry.coordinates;
            map.getSource('point').setData(point);
            if (step < steps) {
                step++;
                animId = requestAnimationFrame(animate);
            }
        }
        animate();
    });

    document.getElementById('replay').addEventListener('click', () => {
        cancelAnimationFrame(animId);
        step = 0;
        function animate() {
            const along = turf.along(route, (step / steps) * lineLength, { units: 'kilometers' });
            point.geometry.coordinates = along.geometry.coordinates;
            map.getSource('point').setData(point);
            if (step < steps) {
                step++;
                animId = requestAnimationFrame(animate);
            }
        }
        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 7, centered around [13.5, 47.6]. Display a dashed route from Innsbruck to Salzburg to Graz to Vienna and animate a point along it, with a replay button.

How it works

turf.along takes a line and a distance and returns the point at that distance along it. The animation advances a distance counter each frame and asks Turf where that is, which is what keeps the movement on the line instead of cutting corners between vertices.

Stepping by distance rather than by vertex is the important part: route vertices are not evenly spaced, so advancing one vertex per frame speeds up through detail and slows down on straight sections.

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.

Bearing between the current and next position is what lets an icon point the way it is travelling, via icon-rotate.

The route here is a fixed line. In a real application it comes from the Routing API as an encoded polyline you decode first.

Next steps

Feeding it a real route is the step that matters, and the Routing API returns both the geometry and per-leg durations, which lets the animation run in proportion to actual travel time rather than at a constant rate.

Following the moving point with the camera is the usual next request, and it needs a way for the user to break the follow and get it back, since a map that always recentres is frustrating to explore.