npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fxi/zartigl

v0.2.15

Published

MapLibre GL JS plugin for animated particle flow from Zarr ocean current data

Readme

zartigl

A MapLibre GL JS plugin for exploring cloud-hosted Zarr geoscience data directly in the browser.

Demo

Zartigl renders scalar rasters and vector particle fields from multidimensional Zarr stores without a dedicated tile server. It is aimed at expert analysis workflows where time, depth or pressure level, point inspection, and reproducible map state matter as much as the visual layer.

Features

  • Browser-side Zarr v2 access with chunk-aware loading and LRU caching
  • Scalar raster rendering with palettes, opacity, log scale, and value inspection
  • Vector rendering with GPU particle advection and optional raster magnitude display
  • Time and vertical-dimension controls for depth, elevation, level, or pressure-like axes
  • Point queries for time series and vertical profiles when a point-series store is available
  • Catalog-backed datasets with automatic Zarr/WMTS backend selection for scalar layers
  • MapLibre custom layer integration, including Mercator and globe rendering paths
  • Pre-rendered GeoVideo backends with true polar globe draping and portable masks
  • No server component required for public CORS-enabled stores

Quick Start

npm install @fxi/zartigl maplibre-gl
import maplibregl from "maplibre-gl";
import { Zartigl } from "@fxi/zartigl";
import { catalog } from "@fxi/zartigl/catalog";

const map = new maplibregl.Map({
  container: "map",
  style: "https://demotiles.maplibre.org/style.json",
  center: [0, 20],
  zoom: 2,
});

const z = new Zartigl({
  id: "zarr-layer",
  map,
  catalog,
  backend: "auto",
  // Optional: expose only the month ending at the latest available timestamp.
  timeRange: { trailing: "P1M" },
  geoVideo: { autoplay: false, loop: true, playbackRate: 1 },
});

await z.setLayer("ocean-current-velocity");
z.setTimeAndDepth(new Date("2025-01-01T00:00:00Z"), 0);
z.updateSettings({
  palette: "rdylbu",
  opacity: 0.9,
});

setLayer() resolves after the field store's consolidated metadata and coordinate axes have loaded. Metadata getters are therefore authoritative after the awaited call, including exact values for irregular time axes.

The root import does not bundle the catalog presets. Import catalog data from @fxi/zartigl/catalog only when you want the built-in catalog.

Demo

The production demo is the default Vite app and is published at fxi.io/zartigl.

npm install
npm run dev

Build the same app that the public demo publishes:

npm run build:prod

For the smaller public API demo:

npm run dev:minimal

The demos read the built-in catalog, which points to public Copernicus Marine ARCO stores. No local data download is needed; visible chunks are fetched directly from cloud object storage or, for some scalar layers, from WMTS when that is the safer backend.

Public API

The main entry point is the catalog-backed Zartigl facade:

const time = z.getTimeMeta();
const depth = z.getDepthMeta();
const legend = z.getLegend();

z.on("loaded", (meta) => {
  console.log(meta.min, meta.max, meta.unit);
});

z.on("status", (status) => {
  // metadata → fetching → rendering → ready, or blocked/error
  console.log(status.phase);
});

// GeoVideo uses native media playback and reports scientific time updates.
z.on("timeChange", (time) => console.log(new Date(time)));
z.on("playbackChange", (playing) => console.log({ playing }));
z.setPlaybackRate(2);
z.setLoop(false);

// Embedding applications can stop rendering and abort field requests while
// retaining the latest requested time/depth for one reload on resume.
z.suspend();
z.setTime(new Date("2025-02-01T00:00:00Z"));
z.resume();

const series = await z.queryTimeSeries({
  longitude: 7.4,
  latitude: 46.9,
  depth: 0,
  maxPoints: 256,
});

New layers initially select the latest advertised timestamp that is not in the future. Forecast timestamps remain available through getTimeMeta().values. Missing remote chunks are interpreted as Zarr fill data. A wholly unavailable frame is not cached, so selecting it again can recover after an upstream store finishes publishing it; a renderable sparse frame is cached normally.

VectorLayer, ScalarLayer, ArcoLayer, and ZarrSource remain exported for advanced use cases that need direct control over renderer internals. Ordinary MapLibre integrations should prefer Zartigl.

Alternatives And Project Fit

Zartigl overlaps with several useful projects. The goal is not to replace them, but to cover a specific MapX/ARCO workflow: public cloud Zarr stores, MapLibre integration, animated vector particles, scalar rasters, time/depth controls, point inspection, and reproducible widget configuration without a dedicated tile server.

The table below is a fit matrix based on documented project scope. "Not specified" means the feature is not a stated focus in the referenced project, not that it is impossible.

| Project | Best fit | Zarr raster | Vector particles | Time/depth UI | Query API | Polar/globe rendering | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | | zartigl | MapX-ready ARCO-like ocean layers | Yes, Zarr v2 | Yes | Built in | Time series and vertical profiles when a point-series store is available | MapLibre globe/ECEF path for lon/lat scalar and vector layers, including over-pole views | Catalog defaults, WMTS fallback for selected scalar layers, MapX snippet generation | | Copernicus Marine MyOcean Pro | Copernicus end-user exploration | Service-managed | Product-dependent | Application UI | Product UI, not a reusable library API | Service-managed | Reference viewer, not a MapLibre/WebGL library to embed in MapX | | @carbonplan/zarr-layer | General Zarr raster layers for MapLibre/Mapbox | Yes, Zarr v2/v3 | No, raster-focused | Selector API | GeoJSON queries | Full polar coverage documented for MapLibre with suitable untiled datasets | Strong closest alternative for raster Zarr; supports custom stores, CRS reprojection, custom fragment shaders | | @carbonplan/maps | React maps for prepared multidimensional rasters | Yes, prepared Zarr pyramids | No | React selector workflow | Not its main focus | Web Mercator-focused | Higher-level React framework; its core MapLibre path is documented as Web Mercator-only | | zarr-gl | Lightweight Zarr layer for Mapbox/MapLibre | Yes | No | Limited | Not specified | Not specified | The README now recommends using @carbonplan/zarr-layer instead | | maplibre-gl-wind | Wind particle layer for deck.gl/MapLibre | No | Yes | No | No | Not specified | Takes wind textures or point data, not Zarr metadata/chunks |

The closest general alternative is @carbonplan/zarr-layer. It has a broader documented scope for raster Zarr rendering: Zarr v2/v3, custom stores, CRS reprojection, custom fragment shaders, GeoJSON queries, and full polar coverage in MapLibre for suitable untiled datasets. If the requirement were only "draw a multidimensional Zarr raster on a MapLibre map", using or contributing to @carbonplan/zarr-layer would be a strong option.

Zartigl remains useful because it combines pieces that are not currently covered together by those projects:

  • ARCO-oriented catalog entries and defaults for Copernicus Marine products.
  • U/V vector products rendered as animated particles, with optional raster magnitude in the same layer.
  • Time and vertical-axis metadata exposed as application controls.
  • Depth/elevation labeling, ordering, and point inspection tuned for ocean products.
  • MapLibre globe rendering for both scalar and vector fields, including polar scenes beyond the usual Web Mercator 85.0511° latitude limit.
  • GPU particle-state fallbacks for browser and hardware differences, including float, half-float, and RGBA8 compatibility mode.
  • A facade API that can generate MapX widget snippets and keep widget state reproducible.

deck.gl, Three.js, or @carbonplan/zarr-layer could still be useful integration targets. They would not remove the ARCO catalog, time/depth metadata, vector particle simulation, point-query workflow, or MapX widget-state work that zartigl currently owns.

Catalog Metadata

To validate the built-in catalog:

npm run catalog:validate

To add or update catalog entries:

uv run scripts/catalog_builder/skills/list_layers.py
uv run scripts/catalog_builder/skills/search_products.py wave
uv run scripts/catalog_builder/skills/query_dataset.py <dataset_id>
uv run scripts/catalog_builder/skills/validate_catalog.py

This requires Python >= 3.12, uv, and a free Copernicus Marine account.

GeoVideo

GeoVideo pre-renders an expensive scalar Zarr timeline into a short, streamable H.264 MP4 while retaining spatial bounds, scientific dates, provenance, units, palette, and color domain in a sidecar manifest. Version 2 transports quantized scalar values in luminance and a static lossless mask as a separate PNG, so the browser can apply palette and scalar styling in WebGL. It is a visualization backend only: point queries always use the authoritative Zarr point-series store.

Unlike MapLibre's native four-corner video source, GeoVideoLayer maps an equirectangular scalar-luma texture through zartigl's subdivided lon/lat mesh. Globe mode therefore reaches both poles. A separate lossless image supplies the static validity mask without doubling the video width.

Displaying a GeoVideo

The built-in catalog includes an Arctic sea-ice thickness animation. Select the GeoVideo backend explicitly and enable autoplay when creating the Zartigl instance:

import maplibregl from "maplibre-gl";
import { Zartigl } from "@fxi/zartigl";
import { catalog } from "@fxi/zartigl/catalog";

const map = new maplibregl.Map({
  container: "map",
  style: "https://demotiles.maplibre.org/style.json",
  center: [-132.454826, 85.051129],
  zoom: 0.511,
});

map.setProjection({ type: "globe" });

const z = new Zartigl({
  map,
  catalog,
  backend: "geovideo",
  geoVideo: {
    autoplay: true,
    loop: true,
    playbackRate: 1,
  },
});

await z.setLayer("sea-ice-thickness");

backend: "geovideo" selects the published GeoVideo manifest from the catalog's sea-ice-thickness entry. Globe projection demonstrates its polar coverage. loop and playbackRate are optional playback settings. Browsers may restrict autoplay, so embedding applications should provide a user-gesture fallback when necessary.

Try the configured instance directly in the Arctic sea-ice GeoVideo example.

uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --dry-run
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --upload

The Arctic sea-ice configuration covers June 2022 through August 2026 and is available at scripts/geovideo/examples/sea-ice-thickness-arctic.json.

Generation requires FFmpeg. Upload credentials remain in .env; no private key is bundled into the demo or written to a manifest. See scripts/geovideo/README.md for the configuration and smoke-test workflow.

Dataset Scope

The built-in catalog focuses on Copernicus Marine ARCO products, including ocean currents and scalar ocean/ice variables. Other public cloud-native Zarr v2 stores can be used when their store URLs and variables are described in a compatible catalog entry and runtime compatibility is verified. Time, vertical, spatial, and variable metadata are loaded live from consolidated Zarr metadata and coordinate chunks.

Good fits include:

  • Ocean currents, Stokes drift, and wave fields
  • Sea-ice drift and ice-related scalar products
  • Atmospheric wind or pressure-level products
  • Climate model outputs with time and vertical dimensions
  • Hydrodynamic model rasters or vectors

Vector layers need either U/V component variables or a catalog derivation from direction and magnitude. Scalar layers need one numeric variable and enough coordinate metadata to locate chunks by longitude, latitude, time, and optional vertical dimension.

Catalog validation checks the JSON contract, not remote service behavior. For built-in catalog additions, use the ARCO-oriented catalog-builder scripts first. WMTS is only an optional scalar shortcut; for polar products, prefer Zarr unless WMTS coverage is verified to reach the poles.

How It Works

1. Zarr Is A Multidimensional Raster Cube

Think of a Cloud Optimized GeoTIFF extended into longitude, latitude, time, and depth or pressure. The data is split into chunks. Zartigl requests only the chunks that intersect the current viewport at the selected time and vertical coordinate, similar to a COG tile request.

2. Chunks Become GPU Textures

Scalar chunks are normalized into a raster texture and colorized by the selected palette. By default the color domain follows the loaded frame; scalar layers can instead set colorDomain: [min, max] to keep colors comparable across viewports and dates. Vector chunks are converted into a two-channel field texture, with validity carried as a mask.

For vector U/V data:

R = eastward component
G = northward component
A = valid data mask

3. Particles Are Virtual Drifters

For vector particle rendering, thousands of virtual drifters are seeded across the viewport. Every frame, the GPU samples the vector texture, moves each particle, and respawns particles that leave the valid domain.

Particle state lives in GPU textures using a ping-pong framebuffer pattern, so positions do not need to round-trip through CPU arrays each frame. Positions are stored at full float precision where the GPU supports it (falling back to 16-bit packing), which keeps trails crisp when zoomed in. Motion is zoom-compensated so a single speed setting yields roughly constant visual speed from low to high zoom.

Trail history currently remains a screen-space texture. During camera movement it is faded aggressively rather than cleared every frame, which avoids flicker while limiting displaced trail artifacts on both Mercator and globe projections. A world-aligned or reprojected trail buffer is a possible experimental direction, but globe reprojection and polar continuity require additional work.

4. Queries Use The Same Source Logic

Point inspection, time-series sampling, and vertical-profile sampling reuse the same Zarr metadata, coordinate lookup, chunk decoding, and cache machinery as the renderer. The query API is intentionally separate from the visual interaction layer, so applications can build their own analysis UI.

Build And Release Checks

npm test
npm run build:prod
npm run build:lib
npm run catalog:validate

npm run release:check runs the main validation path before publishing.

License

MIT