Draco geometry compression in pure Rust, with the file formats and glTF scene handling around it. Nothing here links the original library; bitstream compatibility is a property of the implementation, checked against reference fixtures and the upstream binaries themselves.
This project is independent and is not an official Google Draco release.
Draco 3D Studio — a converter and
viewer for the crates below, running at filyus.github.io/draco-rust. No
upload, no server: the WASM modules do the work in the page, and the files never
leave the machine.
Open an OBJ, PLY, STL, DRC, FBX or glTF/GLB asset and it reports what the file actually contains — hierarchy, materials, skins, morph targets, animation clips — renders it, and exports any of them back out, with Draco compression optional along the way.
Converting between formats loses things, and the per-format conversion rules say which: what survives an FBX to glTF trip, what has no equivalent to survive into, and where the viewer's limits are narrower than the exporter's.
The codec: Draco bitstream encode and decode for triangle meshes and point clouds, and the geometry model everything else speaks.
Formats: OBJ, PLY, STL, binary and ASCII FBX. Each carries geometry in its own encoding, so nothing here touches the Draco codec.
Whole glTF 2.0 and pinned 2.1-draft documents, losslessly: typed scene views, packed-geometry read/write, document-preserving Draco compression, GLB v2/v3, plus the strict GLB/container parser, resource policy and accessor contracts underneath them.
Anything glTF depends on this crate alone. glTF is the one format in the
workspace that embeds a Draco bitstream, so it is also the only place where a
file format meets the codec; draco-io covers the formats that do not, and the
two no longer depend on each other.
draco-texture (KTX2 and Basis Universal transcoding) is part of the
repository but not published.
The codec and the formats are also on npm, as WebAssembly modules for the browser, bundlers and Node. Each package and subpath is a module of its own, so an application downloads only the one it imports:
| package | what it does |
|---|---|
@draco-rust/decoder |
Draco decode; ./mesh, ./point-cloud and ./legacy cut it to what you need |
@draco-rust/encoder |
Draco encode |
@draco-rust/gltf |
glTF and GLB, with ./validate and ./writer |
@draco-rust/fbx, obj, ply, stl |
the formats, read and write |
npm install @draco-rust/decoderOr straight from jsDelivr, without a bundler:
import init, { parse_drc_bytes } from "https://cdn.jsdelivr.net/npm/@draco-rust/decoder@0.3/mesh/index.js";
await init();The packages share one version and are built and published from CI with npm
provenance; web/README.md says what each entry
holds.
[dependencies]
draco-gltf = "0.7"Read a primitive, whether or not it arrived Draco-compressed:
use draco_gltf::{MeshIndex, PrimitiveIndex};
let scene = draco_gltf::import("model.glb")?;
let geometry = scene.read_primitive(PrimitiveIndex::new(MeshIndex(0), 0))?;
println!("{} vertices", geometry.vertex_count());
# Ok::<(), draco_gltf::Error>(())Or stay at the format layer and convert geometry between files:
[dependencies]
draco-io = { version = "0.6", default-features = false, features = ["ply-reader", "obj-writer"] }Every crate is feature-gated down to what an application actually calls, which
matters most on wasm32: the format readers, the writers, the codec halves and
the strict-validation passes are all separable. Each crate's README lists its
features and what they cost.
draco-core is past 1.0 and its public API is covered by SemVer. Point clouds,
sequential meshes and both EdgeBreaker traversals encode and decode, and point
clouds can use several threads on request. The per-algorithm matrix lives in
crates/draco-core/SUPPORT_MATRIX.md.
Where this port deliberately behaves differently from C++ Draco — what the
difference is and what it means for a file — is recorded in
COMPATIBILITY.md.
draco-io and draco-gltf are pre-1.0 and still move: a breaking change bumps
the minor, which is the breaking field for a 0.x crate. Each crate keeps its
own changelog and its own release tags; the web converter keeps
web/CHANGELOG.md, since its wrappers ship as release
assets rather than as crates.
Beyond KHR_draco_mesh_compression, glTF reading covers EXT_meshopt_compression,
and Draco compression preserves the extensions a document carries rather than
refusing the file — including feature IDs, GPU instancing and structural
metadata.
Decoders here are assumed to be pointed at untrusted bytes. Malformed input must fail as a typed error rather than panic, hang, or allocate without bound, and decode limits are the caller's to set.
SECURITY.md— threat model, what is guaranteed, and the resource limits a caller should enforce.FUZZING.md— how the decode paths are fuzzed. Targets cover.drcdecode and encode, glTF import and compression, OBJ/PLY/STL and FBX read and round-trip, and KTX2 transcode; they run as a smoke gate per push and as a soak every night, and through ClusterFuzzLite.
./build.ps1 -Serve # from web/, builds the WASM modules and serves the appcd web && npm ci && bash ./build.sh --appbuild.sh defaults to the lightweight release profile that the size budget is
measured against; --app is the converter profile, which the front-end needs.
The web workspace guide covers the module features, the
TypeScript build, and the Node and Playwright suites.
The crates build like any Rust library (-Copt-level=3) and carry no runtime
knobs. Decode and encode are CPU-bound and branchy.
PERFORMANCE.md— how to benchmark and profile decode/encode, the Rust-vs-C++ speed snapshot, and per-stage breakdowns.PGO.md— profile-guided optimisation: how it relates to a source library, the two-pass build, training-corpus guidance, expected benefit, and the WASM-target tradeoff.TRICKS.md— dated, code-level optimization techniques found in this repo, each with a real before/after snippet: dispatch, error layout, corner-table and entropy-coding tricks, a five-commit KD-tree case study, and what was measured and rejected.
| Path | Contents |
|---|---|
crates/ |
The published crates, plus draco-texture and the C++ test bridge. |
web/ |
WASM wrappers, the converter front-end, and its Node and Playwright suites. |
fuzz/, .clusterfuzzlite/ |
Fuzz targets and the continuous fuzzing setup. |
testdata/, tools/ |
Fixtures and development tooling. |
Apache-2.0. See LICENSE.