StyleImageInterface
Quick answer
StyleImageInterface is the contract for dynamic icons registered through map.addImage: it exposes width, height and data, plus optional render, onAdd and onRemove hooks. When render returns true, the pixel buffer is re-uploaded before the next frame, enabling pulsing beacons and similar animation.
interface StyleImageInterface = { width, height, data, render?, onAdd?, onRemove? }
Key points
- data is a Uint8Array of RGBA pixels sized width times height times four.
- render() returning true schedules a texture update for that frame.
- onAdd and onRemove handle setup and teardown of the icon.
Example
var SIZE = 48;
var pulse = {
width: SIZE,
height: SIZE,
data: new Uint8Array(SIZE * SIZE * 4),
onAdd: function (map) {
this.map = map;
this.start = performance.now();
},
render: function () {
var t = (performance.now() - this.start) / 1000;
var alpha = 0.35 + 0.35 * Math.sin(t * 2.4);
var r = SIZE / 2 - 2;
for (var y = 0; y < SIZE; y++) {
for (var x = 0; x < SIZE; x++) {
var dx = x - SIZE / 2;
var dy = y - SIZE / 2;
var inside = dx * dx + dy * dy <= r * r;
var i = (y * SIZE + x) * 4;
this.data[i] = 31;
this.data[i + 1] = 95;
this.data[i + 2] = 208;
this.data[i + 3] = inside ? Math.round(alpha * 255) : 0;
}
}
this.map.triggerRepaint();
return true;
},
onRemove: function () {
this.map = null;
}
};
map.addImage('pulse', pulse);map.addLayer({
id: 'pulse-layer',
type: 'symbol',
source: 'poi',
layout: {
'icon-image': 'pulse',
'icon-size': 0.6,
'icon-allow-overlap': true
}
});
Notes
- Returning true every frame re-uploads pixels needlessly and hurts performance.
- A data length that disagrees with width and height corrupts the upload.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). StyleImageInterface. https://www.jingweizhi.com/en/maplibre/api/style-image-interface.html (accessed 2026-11-03).