Performance

Meshopt vs. Draco vs. Quantization for glTF

Compare glTF quantization, Draco, and meshopt by precision, transfer bytes, decode behavior, runtime layout, compatibility, and deployment cost.

Quantization, Draco, and meshopt are related but not interchangeable choices. Quantization changes how precisely attributes are represented. Draco supplies a dedicated compressed mesh representation. EXT_meshopt_compression compresses buffer-view data with a design oriented toward fast decode and GPU-friendly streams. A production pipeline may use quantization with either codec.

The best choice depends on required visual precision, supported viewers, cold decoder cost, target-device decode time, output buffer layout, and total asset composition. Comparing only the final GLB byte count omits most of the decision.

Quantization is a representation decision#

Positions, normals, texture coordinates, colors, weights, and other attributes do not always need 32-bit floats. Quantization maps values to a smaller set of representable steps, commonly stored as normalized or integer components plus a transform.

KHR_mesh_quantization expands the component types allowed for glTF mesh attributes. This can reduce raw buffer bytes and improve later entropy compression. It also introduces error.

For a position range of 5 meters, uniform precision has approximate step sizes:

  • 10 bits: 5 / 1,023 ≈ 4.89 millimeters;
  • 12 bits: 5 / 4,095 ≈ 1.22 millimeters;
  • 14 bits: 5 / 16,383 ≈ 0.305 millimeters;
  • 16 bits: 5 / 65,535 ≈ 0.076 millimeters.

The useful precision depends on the bounds being quantized, not merely the bit label. One huge shared coordinate range can waste steps on empty space. Per-mesh or per-primitive bounds can make the same bits more precise, subject to tool behavior.

Normals need enough angular precision to keep highlights stable. UVs need enough precision for the highest texture density and seam alignment. Weights need deformation tests. Quantize each semantic against a visual or behavioral threshold rather than selecting one preset blindly.

Quantization alone can reduce storage without a specialized compressed bitstream. It can also be combined with gzip-like transport, meshopt, or Draco in supported workflows.

Draco uses a mesh-specific bitstream#

KHR_draco_mesh_compression replaces a primitive's ordinary encoded attribute and index payload with a Draco bitstream referenced by the extension. The decoder reconstructs arrays before rendering.

Draco often achieves strong geometry compression by using quantization, connectivity encoding, prediction, and entropy coding. Encoder settings trade compression time and sometimes decode behavior against bytes. The glTF loader needs extension support and a compatible decoder, often delivered as WebAssembly plus integration code.

Strengths include:

  • mature adoption across many glTF tools and viewers;
  • strong reduction on dense indexed meshes;
  • independent quantization controls in common encoders;
  • a well-known reference implementation;
  • useful results for network-dominant geometry.

Tradeoffs include:

  • a required decode step before standard buffers exist;
  • decoder deployment and caching;
  • possible latency for many primitives or large meshes;
  • a decoded layout that may require further optimization depending on tool path;
  • incompatibility with targets that do not support the extension.

Measure rather than relying on the reputation of the codec. A slow encoder is usually acceptable in an offline build; a slow decoder on the user's phone is not.

Meshopt compresses buffer views#

EXT_meshopt_compression stores compressed data for buffer views and includes parameters needed to reconstruct the byte stream. It supports modes for attributes, triangles, and animation-like data, with filters for common numeric representations.

The meshoptimizer project focuses not only on encoding but also on optimizing vertex cache order, overdraw, vertex fetch, simplification, and related mesh processing. These operations are distinct. Reordering can improve runtime access and encoded size without removing triangles; simplification changes topology; meshopt compression changes delivery representation.

Common strengths are:

  • very fast decoding in many use cases;
  • output designed to reconstruct GPU-ready byte streams;
  • ability to compress more general buffer-view data, including animation paths supported by tools;
  • integration with reorder and quantization pipelines;
  • progressive or fallback patterns defined by extension usage modes.

Tradeoffs remain:

  • required extension and decoder support for the compressed path;
  • decoder code and worker strategy on the web;
  • output size that may be larger or smaller than Draco depending on asset and settings;
  • need to understand whether a target tool preserves the chosen attribute types and ordering.

The EXT_meshopt_compression specification is the interoperability contract. The meshoptimizer command-line or library documentation describes the broader processing pipeline.

Do not compare different precision levels#

A codec test is invalid if one candidate uses 14-bit positions and another uses 10-bit positions. The smaller result may simply have discarded more information.

Establish semantic precision first. One procedure is:

  1. start from the same validated source;
  2. generate quantization candidates for position, normal, UV, color, and weights;
  3. find the lowest passing precision for each semantic;
  4. lock those settings;
  5. encode Draco and meshopt candidates from equivalent data;
  6. verify decoded numeric or rendered error;
  7. compare bytes and performance.

Some toolchains combine quantization inside codec commands rather than exposing an identical pre-quantized source. In that case, inspect decoded accessors and calculate error against the original.

Keep the original floating-point artifact outside the delivery folder. Re-encoding an already quantized candidate compounds error and makes a later codec comparison unfair. Every candidate should branch from the same named source revision and publish its decoded error report.

Preserve screenshots, numeric bounds, and animation samples so a future encoder upgrade is judged against the same evidence and not a newly remembered standard.

Use multiple error views: object-space position deviation, screen-space image difference, moving highlights, UV checker, and animation poses. One metric does not capture every attribute.

Output bytes need a component breakdown#

Compare geometry bytes, not only total GLB bytes. If images account for 14 MB and geometry candidates range from 700 KB to 1 MB, the total difference may be only 300 KB across a roughly 15 MB file.

Report:

  • total GLB bytes;
  • compressed geometry and animation bytes;
  • unchanged embedded image bytes;
  • extension JSON and fallback bytes;
  • decoder payload on a cold cache;
  • HTTP response bytes with production headers.

Fallback layouts can affect size. An extension may be required, leaving no uncompressed fallback, or used in a way that permits a fallback path. Including both makes the file larger but broadens compatibility. Follow the extension rules and test actual loaders rather than inventing a hybrid.

A shared decoder amortizes across multiple models. In a one-model marketing page, cold decoder bytes matter. In an application with 500 assets, they are nearly irrelevant per asset after caching.

Decode throughput and latency#

Throughput measures decoded bytes or vertices per second over a large workload. Latency measures time until a particular model is available. Many tiny primitives can have low total bytes and poor latency because setup, worker messages, allocations, and scene construction repeat.

Benchmark at least:

  • one small model on a cold decoder;
  • one representative product;
  • the largest allowed model;
  • a scene with many small primitives;
  • several assets requested concurrently;
  • a warm second load.

Use a low-tier phone, a mid-tier phone, and a representative desktop. Record CPU utilization, main-thread blocked time, worker count, and peak memory. A codec that wins on a laptop may contend with UI and camera processing on mobile.

Test the production build. Development source maps, React development checks, hot reload, and unoptimized bundling distort results.

Runtime layout after decode#

The renderer cares about final vertex order, index order, attribute stride, component type, and buffer binding. A transfer codec that reconstructs poorly ordered triangles could surrender vertex-cache efficiency unless the encoder pipeline preserved or optimized order.

Meshoptimizer exposes separate operations for vertex-cache, overdraw, and vertex-fetch optimization. Their order matters. A common offline flow deduplicates, optimizes cache order, optionally optimizes overdraw within a threshold, optimizes vertex fetch, quantizes, then encodes. Follow the tool's current recommendations for the version in use.

Draco encoders and loaders may choose reconstructed ordering. If runtime frame time matters, profile the decoded asset, not the source ordering. Reordering after decode on the client adds startup work; doing appropriate optimization offline is preferable when possible.

Inspect whether attributes remain interleaved or become separate buffers. Separate arrays may be fine, but they change bind and memory behavior. Do not promise “GPU-ready” without examining the loader output.

Progressive loading and fallback needs#

Some applications need a coarse model quickly and higher detail later. Meshopt-related workflows can support progressive strategies when assets and loaders are designed for them, but a single compressed GLB does not become progressive automatically. HTTP range behavior, chunk placement, level organization, and application logic all matter.

A simpler reliable strategy is separate LOD files:

  • request a 150 KB distant model immediately;
  • show it once decoded;
  • fetch a 900 KB interaction model;
  • request a 3 MB inspection level only after zoom intent.

Each level can use the codec best supported by the destination. The cost is more manifests, requests, and lifecycle logic.

For third-party handoff, compatibility often outweighs custom progressive behavior. Keep a conservative profile for broad distribution and an optimized profile for the viewer you control.

Animation changes the comparison#

Draco's glTF extension targets mesh primitives, while EXT_meshopt_compression can represent compressed buffer views used by animation accessors through supported modes and filters. If a character file is dominated by animation, comparing only mesh bytes understates the potential difference.

Inventory keyframes, constant channels, timestamps, translations, rotations, and scales. Reduce redundant keys against tolerances before encoding. A codec should not be asked to preserve thousands of samples that express no meaningful change.

Validate quaternion interpolation and contact events after reduction and quantization. A 2-millimeter foot drift can be more visible than larger error in a loose cloth accessory.

Compatibility matrix#

Create a destination table with rows for:

  • your web viewer and exact loader version;
  • Android or iOS handoff viewers;
  • Unity, Unreal, Godot, or Blender import path if used;
  • marketplaces or content management processors;
  • validation and preview tools;
  • offline fallback conversion tools.

Columns should include KHR_mesh_quantization, KHR_draco_mesh_compression, EXT_meshopt_compression, decoder deployment, animation support, and observed result. “glTF supported” is too broad.

Extensions evolve from vendor to multi-vendor or Khronos status, and tool support changes. Pin tested versions and rerun the matrix before pipeline upgrades.

Candidate scoring#

A practical scorecard includes:

  1. passed visual and animation tolerance;
  2. total cold bytes including decoder;
  3. geometry and animation bytes alone;
  4. cold initialization time;
  5. representative decode time;
  6. main-thread blocked time;
  7. peak decode memory;
  8. post-decode CPU and GPU frame time;
  9. target coverage percentage;
  10. build determinism and maintenance cost.

Set hard gates first. A candidate unsupported by a required destination fails regardless of its size. A candidate with visible silhouette stepping fails regardless of decode speed. Rank only the passing outputs.

Sensible profiles#

A broad-compatibility profile might use core glTF plus KHR_mesh_quantization if all destinations support it, with carefully encoded conventional images. A controlled web viewer might use meshopt compression and KTX2. A marketplace known to support Draco may receive a Draco profile. A source archive should keep full editable precision and no dependency on delivery decoders.

Avoid making one artifact satisfy contradictory needs when separate generated outputs are cheap. Store the source, pipeline configuration, and reports; regenerate destination profiles rather than hand-editing them.

Comparison checklist#

  • Attribute precision is equivalent before codec ranking.
  • Position precision is evaluated relative to quantized bounds.
  • Normals, UVs, weights, and animation have separate tolerances.
  • Total and component bytes include cold decoder delivery.
  • Small, large, fragmented, and concurrent decode cases are benchmarked.
  • Main-thread blocking and peak memory accompany wall time.
  • Decoded buffer types and ordering are inspected.
  • Runtime frame timing uses the decoded candidate.
  • Progressive behavior is implemented explicitly, not assumed.
  • Every destination has a versioned extension-support result.
  • Source and delivery artifacts remain separate.
  • Tool commands and versions reproduce every profile.

Sources and further reading#

There is no universal compression winner. Lock visual precision, test real decoded layouts and devices, include the cold deployment cost, and select a documented profile for each destination that must consume the asset.

Keep learning

Related guides

Performance10 min

Why Large GLB Files Slow Websites

Trace a GLB through download, decode, parsing, image expansion, GPU upload, compilation, and rendering to fix the stage users actually feel.

glbweb performanceloading
Performance10 min

How to Diagnose a Slow 3D Model

Use a repeatable decision tree to isolate network, decode, memory, CPU submission, vertex, fragment, shader, and lifecycle bottlenecks.

3d profilingdiagnosticswebgl performance
Performance10 min

How to Build Mobile 3D Performance Budgets

Turn device tiers, frame deadlines, memory limits, network conditions, thermal behavior, and scene measurements into enforceable mobile 3D budgets.

mobile 3dperformance budgetframe time