Map effects plugin
The Map Effects plugin adds practical post-processing to a TomTomMap: effects computed from the rendered map's pixels rather than from its style. They work on every style, custom ones included, and survive style releases; MapLibre itself has no post-processing. The playground below puts every effect on one panel:
Every effect is named by the problem it solves, and off by default. Each has a guide of its own:
| Guide | Effects | Solves |
|---|---|---|
| Bloom | bloom.* |
Emphasis on dark and driving styles: lit roads, traffic tubes and route lines glow, optionally only in the colours you name |
| Depth of field | depthOfField.* |
Depth on a tilted map: one band of distance stays sharp, the rest defocuses as through a lens |
| Grade, tint, fog, edge blur and vignette | grade.*, tint.*, fog.*, edgeBlur.*, vignette.* |
Muting the base map under your data; depth and focus at the rim of the view |
| Custom GPU passes | addPass |
Your own fragment shaders inside the effect chain, with every other effect working on their result |
Installation
npm install @tomtom-org/maps-sdk maplibre-gl @tomtom-org/maps-sdk-plugin-map-effects
@tomtom-org/maps-sdk and maplibre-gl are peer dependencies.
Usage
import { resetKnob, setKnob, TomTomMap } from '@tomtom-org/maps-sdk/map';
import { effectKnobCatalogue, MapEffects } from '@tomtom-org/maps-sdk-plugin-map-effects';
const map = new TomTomMap({
style: 'standardDark',
mapLibre: {
container: 'map',
// Bloom, the depth of field, your own passes and capture read the map canvas back.
canvasContextAttributes: { preserveDrawingBuffer: true },
},
});
const effects = new MapEffects(map); // or new MapEffects(map, settings)
effects.updateConfig({ 'grade.saturation': 0.4, 'grade.brightness': 0.85, 'tint.opacity': 0.15 }); // a quiet base
setKnob(effects, effectKnobCatalogue, 'bloom.intensity', 0.5); // one knob
resetKnob(effects, effectKnobCatalogue, 'bloom.intensity'); // one effect off
effects.resetConfig(); // everything off
preserveDrawingBuffer: trueis needed by bloom, the depth of field, your own passes and capture, which read the map canvas back. The plugin warns once when it is missing.- One knob goes through the SDK's
setKnob,getKnobandresetKnobwitheffectKnobCatalogue, as on a map module;getKnobreads the value set, else the catalogue default. - Several knobs:
updateConfig(settings)sets them and keeps the rest,applyConfig(settings)replaces them all,resetConfig()turns everything off, andgetConfig()returns the knobs set as a plain object (MapEffectsSettings). - Every value is checked with the SDK's
validateKnobValuebefore anything is touched: a refused value throwsRangeError, an unknown idError, and the picture never half-changes. remove()detaches the effects, takes their elements out of the page and releases the chain's WebGL context, which matters because a browser caps how many contexts a page may hold.
How it renders
No effect knob writes the MapLibre style: every effect reads the map's rendered pixels, so the knobs change the frame rather than what the map draws. The one link to the style is bloom.only, which keys on the colours the style paints for the style colour knobs it names, whichever module holds them:
Three kinds of layer, in this order:
- The GPU chain, a WebGL2 canvas under everything else, runs your passes and the depth of field on the map's pixels, each reading what the one before left without the picture leaving the GPU.
- Overlays, CSS layers over that result: grade, fog, edge blur, tint and vignette.
- Bloom, a small canvas blended over the frame.
What follows from that order:
- Bloom reads the chain's output, not the raw map: a defocused highlight glows as the wide soft disc a photograph gives it, and a pass of yours that shifts hues runs before bloom's colour key.
- Capture runs the same chain at the requested pixel ratio, then composites the same overlays: one implementation, so what is captured is what was on screen.
- The chain and bloom redraw on the map's
renderevent, coalesced to one draw per animation frame; the CSS overlays need no redrawing. - Nothing takes pointer events: the effects sit below the map's controls and leave hit-testing and SDK events alone.
The catalogue
effectKnobCatalogue lists every knob as static data, readable with no map: id, kind (number, color or colors), description, default and useCase, plus a range on a number knob and the accepted names in options on bloom.only. It shares the entry shape of the SDK's knob catalogues; the playground above builds its panel from it. effectKnobIds lists the ids, EffectKnobId is their union and EffectKnobValueOf<ID> the value one takes.
Ranges and defaults are in each effect's guide:
| Effect | Knob ids |
|---|---|
| Bloom | bloom.intensity, bloom.radius, bloom.threshold, bloom.only, bloom.onlyTolerance |
| Grade | grade.brightness, grade.contrast, grade.saturation |
| Fog | fog.intensity, fog.reach |
| Edge blur | edgeBlur.intensity, edgeBlur.reach |
| Depth of field | depthOfField.intensity, depthOfField.focus, depthOfField.band, depthOfField.bokeh |
| Tint | tint.color, tint.opacity |
| Vignette | vignette.intensity, vignette.reach |
Capture
capture({ pixelRatio }) renders the current view, map and effects, into one canvas. The map is re-rendered at the requested ratio (never resampled), read back once every tile is drawn, and returned to following the device. MapLibre clamps to its maxCanvasSize, so measure the ratio you got from the returned canvas.
const canvas = await effects.capture({ pixelRatio: 3 });
canvas.toBlob((blob) => upload(blob), 'image/png');
Relation to the Styling Foundations module
The Styling Foundations module changes the style, what the map draws; effects change the frame, how the drawn map looks. They compose: the data-viz map preset quiets the map's content, and grade.saturation and tint.opacity mute what remains.
API reference
For complete documentation see the MapEffects API Reference and effectKnobCatalogue.
Related guides and examples
Related guides
- Map styling - The styling knobs, and the map-wide colour ids
bloom.onlynames - Map modules - The knob kinds the effect catalogue shares with the SDK's own catalogues
Related examples
- Map effects playground - Every effect on one panel
- Bloom playground - Every bloom knob, the colour scope included
- Traffic flow with bloom - Live traffic glowing over a dark map
- Mixed map effects - A watercolour pass of your own, with bloom, grade and vignette over it
- 3D terrain playground - A depth of field over a valley
- Route fly-over playground - A depth of field holding the road ahead in focus