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

@bitruvius/draco

v0.3.1

Published

Draco geometry decoder seam for the Bitruvius glTF, 3D Tiles and I3S pipelines: decode-only Google Draco wasm (KHR_draco_mesh_compression / 3DTILES_draco_point_compression), vendored and hardened

Readme

@bitruvius/draco

Draco geometry decode for glTF, 3D Tiles and I3S, behind one injectable seam.

Internal building block. This package exists so that @bitruvius/sdk-maplibre and the Bitruvius codecs can resolve their dependencies on npm. It has no standalone product story. Unless you are deliberately building against it, install the SDK instead.

This package wraps the vendored, decode-only Google Draco wasm and exposes it as the DracoGeometryDecoder contract from @bitruvius/geo-core. It decodes KHR_draco_mesh_compression, 3DTILES_draco_point_compression, and I3S Draco mesh and point geometry into per-semantic typed arrays plus triangle indices.

Why it is its own package

The parsers must not know about the codec. @bitruvius/gltf is a pure bytes-in parser with no wasm and no DOM. The contract it programs against (DracoGeometryDecoder) lives in @bitruvius/geo-core, so the parser depends on an interface and this package supplies the implementation. Inject it and the compressed path lights up; leave it out and the parser still builds and still decodes uncompressed tiles.

Three pipelines need Draco and none of them may depend on each other. @bitruvius/tiles3d-mesh, @bitruvius/tiles3d-points and @bitruvius/i3s all hit Draco payloads. The I3S engine does not go through the glTF parser at all, so the decoder cannot live inside @bitruvius/gltf. Keeping it as a leaf package with one dependency (@bitruvius/geo-core) is what lets all three share it without a cycle.

One wasm, one instantiation, one license boundary. The Draco module is initialized lazily and shared process-wide, so a map streaming a 3D Tiles mesh layer, a pnts layer and an I3S scene layer at the same time downloads and instantiates the wasm once. That also confines Google's Apache-2.0 code to a single package with its own wasm/NOTICES, rather than copying it into every format package.

What is in it

  • DracoDecoder: the seam implementation, with three entry points. decode() takes the glTF unique-id map from the extension's attributes. decodeI3sMesh() and decodeI3sPoints() take a bare buffer, because I3S ships no unique-id map.
  • decodeDracoGeometry(): the synchronous core, for callers that own the module lifecycle themselves.
  • ensureDraco() / dracoWasmUrl(): the wasm host. dracoWasmUrl() exists because a Web Worker cannot resolve the package's sibling wasm/ asset, so the main thread hands it the absolute URL.
  • The wasm itself, published under the ./wasm/* subpath export.
import { DracoDecoder } from '@bitruvius/draco';
import { GltfMeshDecoder } from '@bitruvius/gltf';

// Inject the seam; the glTF parser then handles KHR_draco_mesh_compression for you.
const meshDecoder = new GltfMeshDecoder({ draco: new DracoDecoder() });

The wasm resolves from, in precedence: bytes or a URL you pass, the SDK-configured CDN base (configure({ wasmBaseUrl })), then the package-relative asset. Point it explicitly when you are running cross-origin:

const draco = new DracoDecoder({
  wasmInit: { wasmUrl: 'https://cdn.example.com/draco_decoder.wasm' },
});

How it decodes

Attribute extraction uses the heap-pointer fast path (_malloc plus GetAttributeDataArrayForAllPoints): one bulk copy per attribute, no per-element marshalling. Geometry semantics come back as Float32Array, _FEATURE_ID_* as Uint32Array, and the caller applies any glTF accessor normalized scaling.

I3S is the awkward case and gets a dedicated path. Its attributes are addressed by Draco type rather than by id, and feature-index and uv-region are both Draco GENERIC, so a type lookup cannot tell them apart; each is resolved by its i3s-attribute-type metadata entry. Two decisions there are deliberate and worth knowing about:

  • Normals are not decoded. I3S meshes are baked-lit photogrammetry or flat-shaded from screen-space derivatives, so the renderer never reads them. Skipping them cuts decompression time, the worker transfer, and about 12 bytes per vertex of VRAM on tiles that run 0.1M to 0.8M vertices.
  • i3s-scale_x/y is returned, not applied. It comes back as scaleX and scaleY for the caller to fold into the node matrix, which is one matrix edit instead of a full pass over every vertex. Positions stay node-local; the i3s package adds the reference frame.

Who should depend on it

Inside the SDK: @bitruvius/tiles3d-mesh, @bitruvius/tiles3d-points, @bitruvius/i3s and @bitruvius/sdk-maplibre.

Outside it: only if you are driving @bitruvius/gltf yourself and need the compressed path. If you are using the SDK, the layers already wire this in, on a worker, with the wasm URL forwarded for you.

Third-party attribution

The wasm is Google's Draco decoder (draco_decoder.wasm), Apache-2.0, vendored decode-only (the encoder is absent) and hardened via vendored-3d-wasm. See wasm/NOTICES, which ships in the published package alongside the full Apache-2.0 and MIT licence texts. Decoding is free and ungated, runs entirely client-side, and the encoder is not distributed.

Trademarks

Draco and Google are trademarks of Google LLC. glTF is a trademark of The Khronos Group Inc. Esri, ArcGIS and I3S are trademarks of Environmental Systems Research Institute, Inc. Cesium and 3D Tiles are trademarks of Cesium GS, Inc. MapLibre is a trademark of the MapLibre organization. All other marks are the property of their respective owners.

These names are used solely to describe the data formats this software interoperates with. Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such relationship is implied.

License

Proprietary. The full terms ship as LICENSE inside this package, and are readable before installing at cdn.bitruvius.com/legal/sdk-license-v1.txt.

© Bitruvius, Inc.