Leaflet Route Line with Animated Vehicle — Free HTML CSS JS Snippet

Leaflet Route Line with Animated Vehicle · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

True along-route interpolation
Positioned by real distance, not snapped to waypoints.
Frame-rate-independent speed
Progress is computed from elapsed wall-clock time.
Fill-in traveled line effect
A second polyline extends behind the moving vehicle.
Exact pause and resume
Elapsed progress is preserved precisely, not reset.
Live ETA countdown
Ticks down in sync with actual route progress.
Free OpenStreetMap tiles
No API key or paid map provider required.

About this UI Snippet

Leaflet Route Line with Animated Vehicle — Moving Along a Path, Not Between Points

Screenshot of the Leaflet Route Line with Animated Vehicle snippet rendered live

A live tracking map's vehicle marker needs to move smoothly along its actual route, not teleport from waypoint to waypoint. That requires being able to answer "where exactly is 43% of the way along this route" for any percentage — which means precomputing the route's real geometry once, up front, rather than trying to interpolate it fresh on every animation frame.

Cumulative distance turns "43% along" into a real coordinate

Before any animation starts, the code walks the route's waypoints once and builds a running total of the straight-line distance between each consecutive pair. That cumulative-distance array is what pointAt(progress) uses to find which route segment a given progress percentage actually falls into, then linearly interpolates the exact latitude and longitude within that one segment — the vehicle can be positioned anywhere along the path, not just snapped to a waypoint.

One requestAnimationFrame loop, driven by real elapsed time

The animation reads performance.now() on every frame and divides elapsed time by a fixed duration to get a 0-to-1 progress value — it never increments position by a fixed step per frame. That's what keeps the vehicle's speed consistent regardless of the viewer's actual frame rate; a fixed-step approach would move the van faster on a high-refresh display and slower on a throttled background tab.

The traveled line is a second polyline, not a repaint of the first

Rather than redrawing the whole route every frame, a second, differently-colored polyline (traveled) is extended with setLatLngs to include every fully-passed waypoint plus the vehicle's current exact position — which is what produces the classic "line fills in behind the moving vehicle" effect cheaply.

Pausing preserves exact progress, not just a paused flag

Clicking Pause records performance.now() - startTime as pausedAt — the exact elapsed milliseconds so far. Resuming recomputes startTime as now - pausedAt, which is what lets the animation continue from precisely where it left off instead of restarting or jumping, even though the underlying loop always computes progress from a wall-clock timestamp.

Reusing it

This is the standard shape for any live-tracking display — delivery, ride-share, shipping, fleet — built on a known route. Swap the hardcoded ROUTE array for real waypoints (from a routing API or GPS breadcrumbs) and replace the simulated ETA countdown with a real remaining-distance calculation.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to derive along-path interpolation math from scratch. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the cumulative-distance array lets pointAt(progress) find the correct route segment and interpolate a precise coordinate within it, and why driving the animation from performance.now() rather than a per-frame step keeps its speed consistent across different frame rates. The same assistant can help optimize it — ask whether the straight-line (Euclidean) distance approximation between waypoints is accurate enough for this use case, or whether a real route with many widely-spaced waypoints should use Haversine distance instead. It's also useful for extending the effect: ask it to rotate the vehicle icon to face its direction of travel using the bearing value already computed, animate multiple vehicles on different routes simultaneously, or replace the simulated animation with a live position feed from a WebSocket. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a Leaflet map that animates a vehicle icon smoothly along a predefined route polyline, with a live ETA and pause/resume control, using Leaflet.js (load Leaflet's CSS and JS from a CDN, no other library), in plain HTML, CSS, and JavaScript.

Requirements:
- Define a route as an ordered array of latitude/longitude waypoints, and draw it as a polyline on the map, with distinct markers for the start and end points.
- Before starting the animation, precompute the cumulative distance along the route up to each waypoint, and write a function that, given a progress value from 0 to 1, uses that precomputed data to find which route segment the target distance falls within and linearly interpolates an exact coordinate within that segment — the vehicle must be able to sit anywhere along the path, not only snap to the nearest waypoint.
- Animate a vehicle icon (a custom HTML/emoji marker, not the library's default pin) moving along the route over a fixed total duration, driving the animation loop from actual elapsed wall-clock time (not a fixed per-frame increment), so its real-world speed stays consistent regardless of the display's frame rate.
- Draw a second, differently colored line that grows to represent the portion of the route already traveled, extending it on every animation frame to include the vehicle's current interpolated position, without redrawing the full original route line.
- Show a countdown estimated-time-of-arrival readout above the map that updates in sync with the route progress, and switches to a "delivered" message once the animation completes.
- Add a Pause/Resume button: pausing must stop the vehicle exactly where it currently is, and resuming must continue the animation from that exact same point rather than restarting from the beginning or jumping to a different position.
- Use free OpenStreetMap tile layers so the demo requires no API key.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="rt-wrap">
  <div class="rt-bar">
    <div class="rt-title">Delivery #4471 — En Route</div>
    <div class="rt-eta" id="rtEta">ETA --</div>
    <button class="rt-btn" id="rtBtn" type="button">Pause</button>
  </div>
  <div class="rt-map" id="rtMap"></div>
</div>

Step by step

How to Use

  1. 1
    Add the Leaflet CDNLoad leaflet.css and leaflet.js before the snippet's JS runs.
  2. 2
    Paste HTML, CSS, and JSA route line renders and the van starts moving automatically.
  3. 3
    Watch the traveled line fill inA darker line extends behind the van as it moves.
  4. 4
    Watch the ETA countdownIt ticks down and reads "Delivered" at the end.
  5. 5
    Click PauseThe van stops exactly where it is.
  6. 6
    Click ResumeIt continues from the same point, not from the start.

Real-world uses

Common Use Cases

Delivery and courier tracking pages
The exact "where's my order" map pattern.
Ride-share driver-en-route screens
Smooth vehicle motion along a confirmed route.
Fleet and logistics dashboards
Multiple simultaneous vehicles on shared routes.
Flight or shipment tracking visualizations
Same interpolation math, longer routes.
Trip playback and route replay tools
Pair with the heatmap density layer elsewhere in this collection for historical trip analysis.
Learning Leaflet animation
A clear reference for smooth marker motion along a path.

Got questions?

Frequently Asked Questions

Before animating, the code computes the cumulative straight-line distance up to every waypoint in the route. Given a progress percentage, it finds which segment that percentage's target distance falls within, then linearly interpolates between that segment's two endpoints proportionally — which is what lets the vehicle sit at any point along the path, not just jump discretely from one waypoint to the next.

Reading performance.now() and dividing the elapsed time by a fixed total duration produces a progress value that's tied to real wall-clock time, so the animation takes the same real-world duration regardless of how many frames the browser actually renders per second. Advancing by a fixed step every frame instead would make the vehicle move faster on high-refresh-rate displays and slower when the tab is throttled in the background.

A separate polyline object is kept specifically for the traveled portion, and its coordinate list is replaced on every frame with all fully-passed waypoints plus the vehicle's current interpolated position via setLatLngs. This only updates that one polyline's data — the full route line underneath is drawn once and never touched again.

Pausing records how many milliseconds of animation had actually elapsed (performance.now() minus the original start time) into a pausedAt variable. Resuming recalculates a new effective start time as the current time minus that stored elapsed value, so the very next frame's elapsed-time calculation picks up from exactly where it left off rather than starting the duration over.

Replace the ROUTE array with waypoints from a routing API (or raw GPS breadcrumb points), and either keep the time-based simulated animation for a smooth playback experience, or drive the marker's position directly from live GPS updates (skipping the animation loop) when you have a real, frequently-updating position feed instead of a route to simulate traveling.