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

@draco-rust/decoder

v0.3.0

Published

Draco geometry decoder in WebAssembly, a safe Rust port of Google Draco: meshes, point clouds and older bitstreams, each as its own small entry.

Readme

@draco-rust/decoder

Decodes Draco geometry (.drc) in WebAssembly. It is part of draco-rust, a safe Rust port of Google's Draco, and is byte-for-byte compatible with its bitstream. It is not affiliated with Google.

Entries

Each entry is a separate module. A bundler includes only the one you import.

| import | decodes | gzip | |---|---|---| | @draco-rust/decoder | meshes and point clouds | ~92 KiB | | @draco-rust/decoder/mesh | meshes only, which is all glTF's KHR_draco_mesh_compression allows | ~77 KiB | | @draco-rust/decoder/point-cloud | point clouds only (scans, splats) | ~63 KiB | | @draco-rust/decoder/legacy | meshes and point clouds, plus bitstreams older than 2.2 | ~103 KiB |

Each entry also has a worker pool and a three.js loader of its own, which use that entry's module: @draco-rust/decoder/pool and @draco-rust/decoder/three, or @draco-rust/decoder/mesh/pool, @draco-rust/decoder/mesh/three and so on.

Usage

The function names match the converter that drives this module, so they are snake_case.

import init, { parse_drc_bytes } from "@draco-rust/decoder";

await init();
const result = parse_drc_bytes(new Uint8Array(await (await fetch("bunny.drc")).arrayBuffer()));
if (!result.success) throw new Error(result.error);
const { positions, indices, normals, uvs, colors } = result.meshes[0];

positions is a Float32Array of xyz triples, and indices a Uint32Array of triangle corners, which is empty for a point cloud. normals and uvs are Float32Arrays and colors a Uint8Array, each empty when the stream has no such attribute. Attributes the stream carries beyond those are in meshes[0].extras, and result.warnings says what was not interpreted.

Attributes the way a loader asks for them

decode_draco returns every attribute as a typed array, or the ones a loader asks for, converted to the array type it needs. Upstream Draco's decoder makes the same conversions, and this one matches it byte for byte, refusing where it refuses.

import init, { decode_draco } from "@draco-rust/decoder/mesh";

await init();
// By unique id, as glTF's KHR_draco_mesh_compression names them, or by type.
const result = decode_draco(bytes, [
  { name: "position", id: 0, type: "Float32Array" },
  { name: "color", semantic: "COLOR", type: "Uint16Array" },
]);
if (!result.success) throw new Error(result.error);
const { index, attributes } = result; // attributes[i]: { name, array, itemSize, normalized, ... }

Without the second argument every attribute comes back in its own type. The index is a Uint32Array; a third argument { index: "auto" } makes it a Uint16Array wherever the points number at most 65536, half the bytes.

On workers

Decoding runs on the calling thread. createDecoderPool runs it on workers instead, so a page stays responsive while several streams decode at once:

import { createDecoderPool } from "@draco-rust/decoder/mesh/pool";

const pool = createDecoderPool({ workers: 4 }); // default: cores - 1, at most 4
const result = await pool.decode(bytes, requests); // what decode_draco returns
pool.dispose();

The workers start as work arrives, and the wasm is compiled once for all of them. The stream is copied to the worker; pool.decode(bytes, requests, { transfer: true }) hands over the buffer itself instead. The pool runs on Web Workers, on Node's worker threads (Node 20.16 and later), and from a CDN. With workers: 0, or where there are no workers, it decodes on the calling thread. pool.decode also takes index, as decode_draco does, and an AbortSignal as signal: aborting rejects the promise at once. A decode a worker has already started still runs to its end and its result is dropped; one not yet sent is never sent. A bundler that follows new URL("./worker.js", import.meta.url), as Vite and webpack do, emits the worker on its own.

With three.js

@draco-rust/decoder/three exports DRACOLoader: three.js's own DRACOLoader, decoding on this package's workers instead of the Draco decoder three fetches with setDecoderPath. Everything else is three's class, so it goes wherever one is expected, TypeScript included:

import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "@draco-rust/decoder/mesh/three";

const gltfLoader = new GLTFLoader().setDRACOLoader(new DRACOLoader());
const gltf = await gltfLoader.loadAsync("model.glb");

load, parse, setWorkerLimit, preload and dispose work as three's do, and so does the geometry: three builds it, so vertex colours, normalization and the padding r181 added follow the three you have. Byte for byte it is the geometry three's own loader builds, with one difference: an attribute that does not convert to the type asked for -- a negative value into a Uint16Array, say -- is an error here, where three's loader fills the attribute with whatever its failed conversion left in memory.

The constructor takes three's LoadingManager and then the pool's options, new DRACOLoader(manager, { workers: 2 }), or { pool } to share one pool between loaders. three is a peer dependency, from r150 (0.150.0); the package's other entries do not need it.

From a CDN, without a bundler

import init, { parse_drc_bytes } from "https://cdn.jsdelivr.net/npm/@draco-rust/[email protected]/mesh/index.js";
await init(); // finds index_bg.wasm next to the script

In Node

Node's fetch does not read files, so pass the module's bytes yourself:

import { readFile } from "node:fs/promises";
import init, { parse_drc_bytes } from "@draco-rust/decoder";

await init({ module_or_path: await readFile(new URL(import.meta.resolve("@draco-rust/decoder/index_bg.wasm"))) });

For a subpath entry, resolve its own file, for example @draco-rust/decoder/mesh/index_bg.wasm.

Notes

  • Runs in every browser with WebAssembly, in Node 20.6 and later, in Deno and in Bun.
  • parse_drc_bytes and decode_draco decode on the calling thread; the pool decodes on workers.
  • Licensed under Apache-2.0, like Draco.

This build

@draco-rust/[email protected] was built from draco-rust commit 4b72cfc8, with draco-core 2.3.2, draco-io 0.6.0, draco-gltf 0.7.0.