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

cog-tiler-wasm

v0.2.4

Published

Serverless Cloud Optimized GeoTIFF (COG) dynamic tiling in WebAssembly. TiTiler-style XYZ tiles, no backend.

Readme

cog-tiler-wasm

CI Live demo

Serverless, TiTiler-style XYZ tiling of Cloud Optimized GeoTIFFs, in WebAssembly. No backend, no GDAL, no PROJ - the map fetches COG byte ranges directly and synthesizes z/x/y tiles client-side.

Live demo - loads a sample EPSG:3857 COG over HTTP range requests and renders it on a MapLibre map, all in the browser. Paste any CORS- and range-enabled 3857 COG URL to try your own.

This crate is the tiling brain. It does the slippy-map math (tile -> source pixel window), picks the right COG overview level, and renders a decoded window into an RGBA tile (rescale + colormap + nodata alpha). It deliberately does not parse COGs or do network I/O - that is delegated to whitebox-wasm's CogStream, which already implements a pure-Rust codec stack (Deflate/LZW/JPEG/WebP/…) and HTTP range streaming. The two compose into a complete tiler:

  CogStream (whitebox-wasm)               CogTiler (this crate)
  ─────────────────────────               ─────────────────────
  geo_transform(), levels_json()   ──▶     new(gt, w, h, epsg, nodata, levels)
                                           pixel_window_for_tile(z,x,y) ─▶ {level,x,y,w,h}
  tiles_for_window(level,x,y,w,h)  ◀──     (JS fetches byte ranges, decodes tiles)
  decode_tile_f64(...)             ──▶     render(window, w, h, min, max, cmap) ─▶ RGBA

The result is a TiTiler-class viewer with zero hosting cost: wire it to a MapLibre custom protocol and the browser does everything.

Why this is feasible without a server

A dynamic tile server (TiTiler = FastAPI + rio-tiler + GDAL) does five things: read a COG by HTTP range request, decode the relevant internal tiles, resample to the requested XYZ tile, apply rescale/colormap/nodata, and encode. Every one of those has a pure-Rust, WASM-clean implementation today - the two historically hard parts (a C-free codec stack and CRS handling) are already solved in whitebox-wasm. This crate adds the thin layer on top: mercator addressing, overview selection, resampling, and rendering.

Status

v1 (this scaffold): EPSG:3857 sources, single-band rendering, built-in colormaps, MapLibre demo. The crate builds to wasm32-unknown-unknown and ships a wasm-pack web package.

See Roadmap for warping, multi-band, and edge/WASI serving.

Build

rustup target add wasm32-unknown-unknown
cargo install wasm-pack
wasm-pack build crates/cog-tiler-wasm --release --target web --out-dir pkg

Run the demo locally

npm run dev   # builds the wasm, assembles demo/, serves http://localhost:8000/

npm run dev builds the wasm into demo/, copies in cog-tiler.js + the sample, and starts a zero-dependency static server with HTTP range support (which the tile streaming needs - the stdlib python -m http.server does not do ranges). Set PORT to change the port. No npm install is required (the dev scripts use only Node built-ins; the demo loads its peer deps from a CDN via an import map).

The published GitHub Pages demo is built the same way by .github/workflows/pages.yml.

Usage (reusable module)

cog-tiler.js is the package's main entry. It wraps the wasm tiler + whitebox-wasm and handles EPSG:3857 sources, on-the-fly warping of any projected/4326 COG to Web Mercator, and paletted (categorical) rendering - so apps import it instead of copying the demo.

It ships inside the npm package (main/module -> cog-tiler.js); the raw wasm tiler is also available at the cog-tiler-wasm/wasm subpath. Install it alongside its peer dependencies:

npm install cog-tiler-wasm whitebox-wasm proj4 geotiff geotiff-geokeys-to-proj4 maplibre-gl
import maplibregl from "maplibre-gl";
import { init, openCog, registerCogProtocol } from "cog-tiler-wasm";

await init(); // load the wasm modules once

let source = null;
// The protocol resolves the active source + render settings per tile.
registerCogProtocol(maplibregl, "cog", () => ({
  source,
  render: { min: 0, max: 3000, colormap: "viridis" }, // ignored for paletted COGs
}));

source = await openCog(url); // EPSG:3857 fast path, or warped if projected/4326
// openCog also accepts a local raster: a File (e.g. from <input type=file>),
// Blob, ArrayBuffer, or Uint8Array - read in memory, no server needed.
map.addSource("cog", { type: "raster", tiles: ["cog://{z}/{x}/{y}"], tileSize: 256 });
map.addLayer({ id: "cog", type: "raster", source: "cog" });

// source.crsLabel, source.levels, source.hasPalette, source.boundsLonLat
// and source.renderTileRGBA(z, x, y, render) / renderTilePNG(...) are also exposed.

TiTiler-style COG API

CogSource mirrors the read endpoints of TiTiler's COG API, client-side (works on projected/paletted sources too, via the warp path):

// Metadata / read
src.info();           // /cog/info         -> bounds, count, dtype, nodata, overviews, min/maxzoom, ...
src.infoGeoJSON();    // /cog/info.geojson -> GeoJSON Feature (bbox polygon + info)
src.tilejson();       // /cog/tilejson.json -> Mapbox TileJSON document
await src.point(lon, lat);          // /cog/point -> band value(s) at a WGS84 coordinate
await src.statistics({ maxSize });  // /cog/statistics -> per-band min/max/mean/std/count/
                                    //   valid_percent/median/percentiles/histogram (from an overview)

// Image generation (all accept render params below)
await src.previewPNG({ maxSize: 1024 });               // /cog/preview -> PNG bytes
await src.bboxPNG([minLon, minLat, maxLon, maxLat]);   // /cog/bbox    -> PNG bytes
await src.preview(opts);   // -> { width, height, rgba }  (bbox() likewise)

// Render params (on tiles, preview, bbox):
//   bidx:     1-based bands; one -> colormap/palette, three -> RGB composite
//   rescale:  [[min,max], ...] per band, or [min,max]; or min/max shorthand
//   colormap: one of colormaps()  (viridis, magma, plasma, inferno, cividis,
//             turbo, terrain, blues, greens, reds, rdylgn, spectral, gray)
//   reversed: sample the colormap back-to-front (single-band)
//   stretch:  transfer curve "linear" | "sqrt" | "log"  (applied after rescale)
//   gamma:    power-law gamma (1 = off; applied after the stretch)
//   nodata:   override the transparency value
//   opacity:  output alpha multiplier 0..1
await src.renderTilePNG(z, x, y, { bidx: [4, 3, 2], rescale: [0, 3000] }); // false-color RGB
import { colormaps } from "cog-tiler-wasm";

The render pipeline (nodata -> rescale -> stretch -> gamma -> colormap, plus reverse/opacity) matches the controls of GPU raster viewers such as maplibre-gl-raster, so cog-tiler-wasm can serve as a CPU/WASM rendering backend for the same UI - the panel (band, rescale, colormap, curve, gamma, opacity) maps directly onto these params and re-renders by re-requesting tiles.

Server-only endpoints (/map.html, WMTSCapabilities.xml, /validate, /stac) are out of scope for a client-side library; band-math expression is on the roadmap.

For a no-build page, map the peer deps with an import map (see demo/index.html):

<script type="importmap">
  { "imports": {
    "whitebox-wasm": "https://esm.sh/[email protected]",
    "proj4": "https://esm.sh/[email protected]",
    "geotiff": "https://esm.sh/[email protected]",
    "geotiff-geokeys-to-proj4": "https://esm.sh/[email protected]"
  } }
</script>

Low-level Rust API

The wasm crate (CogTiler) is the 3857 tiling brain underneath cog-tiler.js: pixel_window_for_tile(z, x, y) maps a tile to a source-pixel window/overview, and render(window, w, h, min, max, colormap, nodata_alpha) rasterizes an assembled f64 window to RGBA. See cog-tiler.js for the window-assembly and warp loops built on top.

A runnable MapLibre example (custom cog:// protocol) is in demo/index.html.

API

version(), tile_bounds_3857(z, x, y) -> [minx,miny,maxx,maxy].

new CogTiler(geo_transform, width, height, epsg, nodata, levels_json)

  • geo_transform - 6-element GDAL affine of the full-res raster (Float64Array)
  • width/height - full-resolution pixel dimensions
  • epsg - source CRS; must be 3857 in v1
  • nodata - optional nodata value (undefined/NaN = none)
  • levels_json - JSON array of level descriptors, finest level first; only width/height are read (extra whitebox-wasm fields are ignored)

Properties: epsg, num_levels.

pixel_window_for_tile(z, x, y) -> { level, x, y, w, h, level_width, level_height, empty } The overview level and pixel window covering the tile. empty is true when the tile lies outside the raster.

render(pixels, win_w, win_h, min, max, colormap, nodata_alpha) -> Uint8Array A 256*256*4 RGBA tile. pixels is the decoded row-major f64 window; colormap is "viridis" | "magma" | "terrain" | "gray". Empty windows render fully transparent.

Roadmap

  • TiTiler COG API parity - done: info, info.geojson, tilejson, point, statistics, preview, bbox, bidx/RGB band selection, and 13 colormaps (see above). Next: band-math expression and color_formula.
  • Warping of projected/4326 sources and paletted/categorical rendering are done in cog-tiler.js (proj4js + geotiff.js). Planar (INTERLEAVE=BAND) multi-band COGs are read per-band via geotiff.js too, since whitebox-wasm's streaming decoder is chunky-only. Next: planar support upstream in whitebox-wasm (and exposing its proj string + color table) to drop the geotiff.js dependency, then move the warp into the Rust crate (proj4rs).
  • Edge / WASI serving - run the same module as a serverless XYZ endpoint near the data, not only in the browser.
  • STAC / mosaics - multi-asset orchestration.

Releasing

The npm package bundles the wasm tiler and the cog-tiler.js module (assembled by scripts/prepare-pkg.mjs). To cut a release, push a vX.Y.Z tag; release.yml builds, assembles, and publishes to npm via Trusted Publishing (OIDC, no token):

git tag v0.2.0 && git push origin v0.2.0

One-time setup: configure the package's Trusted Publisher on npmjs.com (package -> Settings -> Trusted Publisher) to this repo + release.yml.

License

MIT © OpenGeos.