Soft Pulsing Marker
Add a smooth, animated pulsing dot that gently breathes and glows — ideal for showing live user location, real-time tracking, alerts, or emphasizing important points.
Quick Usage
map.addImage("pulsing-dot", pulsingDot, { pixelRatio: 2 });
map.addLayer({
id: "pulse-layer",
type: "symbol",
source: "user-location",
layout: { "icon-image": "pulsing-dot" },
});
Uses a dynamic canvas-based image that animates in real-time.
Full Working Example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Soft Pulsing Marker</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://unpkg.com/bkoi-gl@latest/dist/style/bkoi-gl.css"
/>
<script src="https://unpkg.com/bkoi-gl@latest/dist/iife/bkoi-gl.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
html,
body,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new bkoigl.Map({
container: "map",
style: "https://map.barikoi.com/styles/barikoi-light/style.json",
center: [90.36402004477634, 23.823730671721],
zoom: 10,
accessToken: "YOUR_BARIKOI_API_KEY",
});
const size = 200;
// implementation of StyleImageInterface to draw a pulsing dot icon on the map
// Search for StyleImageInterface in https://maplibre.org/maplibre-gl-js/docs/API/
const pulsingDot = {
width: size,
height: size,
data: new Uint8Array(size * size * 4),
// get rendering context for the map canvas when layer is added to the map
onAdd() {
const canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
this.context = canvas.getContext("2d");
},
// called once before every frame where the icon will be used
render() {
const duration = 1000;
const t = (performance.now() % duration) / duration;
const radius = (size / 2) * 0.3;
const outerRadius = (size / 2) * 0.7 * t + radius;
const context = this.context;
// draw outer circle
context.clearRect(0, 0, this.width, this.height);
context.beginPath();
context.arc(
this.width / 2,
this.height / 2,
outerRadius,
0,
Math.PI * 2
);
context.fillStyle = `rgba(255, 200, 200,${1 - t})`;
context.fill();
// draw inner circle
context.beginPath();
context.arc(this.width / 2, this.height / 2, radius, 0, Math.PI * 2);
context.fillStyle = "rgba(255, 100, 100, 1)";
context.strokeStyle = "white";
context.lineWidth = 2 + 4 * (1 - t);
context.fill();
context.stroke();
// update this image's data with data from the canvas
this.data = context.getImageData(0, 0, this.width, this.height).data;
// continuously repaint the map, resulting in the smooth animation of the dot
map.triggerRepaint();
// return `true` to let the map know that the image was updated
return true;
},
};
map.on("load", () => {
map.addImage("pulsing-dot", pulsingDot, { pixelRatio: 2 });
map.addSource("points", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.36402004477634, 23.823730671721],
},
},
],
},
});
map.addLayer({
id: "points",
type: "symbol",
source: "points",
layout: {
"icon-image": "pulsing-dot",
},
});
});
</script>
</body>
</html>
Customize the Pulse
| Property | Change To | Effect |
|---|---|---|
| Pulse color | rgba(255, 100, 100, ...) | Red pulse (alert) |
| Speed | duration = 800 | Faster pulse |
| Size | size = 240 | Larger dot |
| Outer radius | 0.9 * t | Stronger pulse wave |
| Inner dot color | rgba(255, 100, 100, 1) | red (Emergency/SOS) |
Popular Variants:
- Green pulse → Live user location
- Red pulse → Emergency/SOS
Advanced: Multiple Pulsing Markers
// ...existing code...
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.39, 23.79],
},
},
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.41, 23.81],
},
},
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.37, 23.77],
},
},
],
// ...existing code...
All will pulse in perfect sync!
Pro Tip: Combine with Geolocation
// Add a separate source for the live geolocation marker
map.addSource("pulse-point", {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.36402004477634, 23.823730671721], // default center
},
},
});
// Add layer for live geolocation marker
map.addLayer({
id: "pulse-point",
type: "symbol",
source: "pulse-point",
layout: {
"icon-image": "pulsing-dot",
},
});
// Watch user's position and update marker
navigator.geolocation.watchPosition(
(pos) => {
map.getSource("pulse-point").setData({
type: "Feature",
geometry: {
type: "Point",
coordinates: [pos.coords.longitude, pos.coords.latitude],
},
});
// Optional: center map on user's location
// map.setCenter([pos.coords.longitude, pos.coords.latitude]);
},
(error) => {
console.error("Geolocation error:", error);
},
{
enableHighAccuracy: true,
maximumAge: 0,
}
);
Real-time live location with pulsing effect
Make your map breathe — beautifully.