maplibre-gl-wind
v0.2.3
Published
Wind particle visualization layer for deck.gl and MapLibre GL JS
Maintainers
Readme
MapLibre GL Wind
A high-performance wind particle visualization layer for deck.gl. Renders animated wind flow particles with speed-based coloring using GPU transform feedback.
Based on Az's deck.gl 9.x particle layer implementation.
Live demo: maplibre-gl-wind.geoql.in — current global wind from Open-Meteo, on a MapLibre map, with every layer prop adjustable.
Features
- GPU-accelerated particle animation using transform feedback
- Speed-based color ramps (Windy.com style)
- Configurable particle count, speed, and lifetime
- IDW interpolation for wind data points
- Works with deck.gl 9.x and MapLibre GL JS
Installation
# npm
npm install maplibre-gl-wind @deck.gl/core @deck.gl/layers
# pnpm
pnpm add maplibre-gl-wind @deck.gl/core @deck.gl/layers
# JSR
pnpm dlx jsr add @geoql/maplibre-gl-windUsage
With Wind Texture Image
import { Deck } from '@deck.gl/core';
import { WindParticleLayer } from 'maplibre-gl-wind';
const deck = new Deck({
initialViewState: {
longitude: 0,
latitude: 20,
zoom: 2,
},
controller: true,
layers: [
new WindParticleLayer({
id: 'wind',
image: 'path/to/wind-texture.png',
bounds: [-180, -90, 180, 90],
imageUnscale: [-50, 50],
numParticles: 8192,
maxAge: 50,
speedFactor: 50,
colorRamp: [
[0.0, [59, 130, 189, 255]],
[0.5, [253, 174, 97, 255]],
[1.0, [213, 62, 79, 255]],
],
speedRange: [0, 30],
}),
],
});With Wind Data Points (IDW Interpolation)
import { WindParticleLayer, generateWindTexture } from 'maplibre-gl-wind';
// `direction` is where the wind blows TOWARD, in degrees clockwise from north.
// Weather APIs report where it comes FROM: add 180 to those.
const windData = [
{ lat: 40.7, lon: -74.0, speed: 5.2, direction: 180 },
{ lat: 34.0, lon: -118.2, speed: 3.1, direction: 270 },
// ... more points
];
const { canvas, imageUnscale } = generateWindTexture(windData, {
width: 360,
height: 180,
bounds: [-180, -90, 180, 90],
});
const layer = new WindParticleLayer({
id: 'wind',
image: canvas.toDataURL(),
bounds: [-180, -90, 180, 90],
imageUnscale,
numParticles: 8192,
colorRamp: [
[0.0, [59, 130, 189, 255]],
[1.0, [213, 62, 79, 255]],
],
});With MapLibre GL JS
import { Map } from 'maplibre-gl';
import { MapboxOverlay } from '@deck.gl/mapbox';
import { WindParticleLayer } from 'maplibre-gl-wind';
const map = new Map({
container: 'map',
style: 'https://tiles.openfreemap.org/styles/liberty',
center: [0, 20],
zoom: 2,
});
map.on('load', () => {
const overlay = new MapboxOverlay({
layers: [
new WindParticleLayer({
id: 'wind',
image: 'path/to/wind-texture.png',
bounds: [-180, -90, 180, 90],
imageUnscale: [-50, 50],
numParticles: 8192,
}),
],
});
map.addControl(overlay);
});API
WindParticleLayer
| Option | Type | Default | Description |
| -------------- | ------------- | ---------------------- | -------------------------------------------- |
| image | string | - | URL or data URL of wind texture |
| bounds | number[] | [-180, -90, 180, 90] | Geographic bounds [west, south, east, north] |
| imageUnscale | number[] | [0, 0] | Wind velocity range [min, max] |
| numParticles | number | 8192 | Number of particles |
| maxAge | number | 50 | Particle lifetime in frames |
| speedFactor | number | 50 | Particle speed multiplier |
| colorRamp | ColorStop[] | Blue to red gradient | Speed-based color stops [position, color] |
| speedRange | number[] | [0, 30] | Speed range for color mapping [min, max] |
| width | number | 1.5 | Particle trail width |
| animate | boolean | true | Enable animation |
generateWindTexture
Converts wind data points to a texture using IDW interpolation. U and V are
both encoded with one shared range, returned as imageUnscale — pass it
straight to WindParticleLayer. rasterizeWindField does the same work
without the DOM (workers, server) and returns the raw RGBA pixels.
function generateWindTexture(
windData: WindDataPoint[],
options?: {
width?: number;
height?: number;
bounds?: [number, number, number, number];
power?: number;
},
): WindTextureResult;createWindDataFromOpenWeatherMap
Helper to convert OpenWeatherMap API responses to wind data points.
function createWindDataFromOpenWeatherMap(
responses: Array<{
coord: { lat: number; lon: number };
wind?: { speed?: number; deg?: number };
}>,
): WindDataPoint[];Wind Texture Format
Wind textures encode U (east-west) and V (north-south) velocity components:
- R channel: U component, scaled from
imageUnscaleto 0-255 - G channel: V component, scaled from the same
imageUnscalerange - A channel: Should be 255 for valid data
Requirements
- Node.js >= 24.0.0
- deck.gl >= 9.0.0
Contributing
- Fork and create a feature branch from
main - Make changes following conventional commits
- Ensure commits are signed
- Submit a PR
pnpm install
pnpm run build
pnpm run lint
pnpm run typecheck
pnpm run test
# demo site (demo/, Nuxt on Cloudflare Workers)
pnpm run demo:devToolchain: Node 26 (devEngines), pnpm (version pinned in packageManager).
More from the author
- maplibre-gl-interpolate-heatmap — IDW interpolated heatmap layer
- maplibre-gl-snow — WebGPU snow particle layer
- maplibre-gl-starfield — Three.js starfield skybox for globe maps
- maplibre-gl-draw-circle — draw and edit circles on a MapLibre map
- v-maplibre — 50+ Vue 3 components for MapLibre GL
- mapcn-vue — shadcn-vue map components you copy into your app
See every project at vinayakkulkarni.dev/projects and geoql.in.
License
MIT © Vinayak Kulkarni
Credits
- Az's deck.gl particle layer - Original implementation
- deck.gl - WebGL visualization framework
- luma.gl - WebGL2 engine
