ParticleLayer
The wind-layer API, GPU simulation, particle appearance, and tuning controls are experimental and may change. WebGL2 and WebGPU particle simulation, wind arrows, and station surfaces are browser-tested independently; image-derived mountain terrain still depends on upstream support.
ParticleLayer animates GPU-resident particles through a station-interpolated geographic wind
field. WebGL2 uses transform feedback and single-vertex point rendering; WebGPU uses a compute
shader and native GPU-buffer-backed point primitives. Neither simulation reads particle positions
back to the CPU.
Import
import {ParticleLayer} from '@deck.gl-community/geo-layers';
Example
import {Deck} from '@deck.gl/core';
import {createWindField, parseWindData, ParticleLayer} from '@deck.gl-community/geo-layers';
const stations = await fetch('/wind/stations.json').then(response => response.json());
const weather = await fetch('/wind/weather.bin').then(response => response.arrayBuffer());
const windField = createWindField(stations, parseWindData(weather, stations.length));
const deck = new Deck({
initialViewState: {longitude: -98, latitude: 38, zoom: 4, pitch: 45},
controller: true
});
let time = 0;
function animate() {
deck.setProps({
layers: [
new ParticleLayer({
id: 'wind-particles',
windField,
time,
numParticles: 100_000,
speedScale: 0.16,
color: [186, 233, 223, 34],
elevationScale: 24,
surfaceOffset: 1_700,
pointRadiusPixels: 0.7
})
]
});
time += 1 / 108;
requestAnimationFrame(animate);
}
animate();
Keep windField and id stable. deck.gl transfers the existing simulation state to the next
layer instance, so advancing time does not reallocate the particle buffers.
Properties
windField (WindField, required)
Shared station forecast created with createWindField. Changing the field
creates a new GPU simulation.
time (number, default 0)
Fractional forecast-frame index and simulation time. Forecast frames wrap automatically.
numParticles (number, default 2400)
Number of simulated GPU particles. The showcase defaults to 100_000 and lets you select up to
1_000_000. Changing the count necessarily reallocates the simulation buffers; debounce sliders
rather than reallocating on every pointer-move event.
trailLength (number, default 12)
Maximum retained history for the device-free CPU fallback. The GPU path instead renders directly from current and previous GPU buffers and fades particles by their GPU-resident lifetime. At high densities it prioritizes single-vertex points over additional trail geometry.
speedScale (number, default 0.085)
Geographic distance per 30-fps-equivalent simulation step. Simulation speed is adjusted for elapsed time instead of assuming a fixed browser frame rate.
widthMinPixels (number, default 1.1)
Minimum visible trail width in screen pixels.
color (Color, default [194, 246, 224, 210])
RGBA particle and trail color. The GPU multiplies alpha by fade-in and fade-out lifetime curves. Use a lower alpha when rendering hundreds of thousands of overlapping particles.
elevationScale (number, default 1)
Multiplier applied to station-interpolated elevation in meters.
surfaceOffset (number, default 160)
Vertical separation in meters above the interpolated terrain.
pointRadiusPixels (number, default 1.6)
Radius of moving particle heads in screen pixels.
Rendering and lifecycle
- WebGL2: cached
rgba32floatweather textures, transform-feedback ping-pong buffers, and one native point vertex per particle. - WebGPU: cached weather textures, a WGSL compute pipeline, and native GPU-buffer-backed point primitives.
- Coverage: invalid samples are respawned within the wind field; overlong segments are clipped.
- Cleanup: deck.gl finalization releases the weather textures, simulation buffers, and pipeline after submitted GPU work has completed.
- Scope: native wind arrows and station-triangulated surfaces support WebGPU, but image-derived
mountain terrain still depends on upstream
TerrainLayercompatibility.
See the wind showcase guide, the Wind Map example, and the WebGPU compatibility matrix.