Performance

Why Draco Compression Is Not Enough for a Fast GLB

Learn exactly what Draco changes, what remains expensive after decode, and how to pair geometry compression with texture, scene, and runtime optimization.

Draco can make mesh geometry dramatically smaller on the network. It cannot make a 4096 texture occupy fewer decoded texels, merge 80 material primitives, reduce the triangles the GPU renders after decode, simplify a costly shader, or fix a blocking application startup path. A GLB that shrinks from 18 MB to 6 MB can still be slow.

Use Draco as one stage in a measured delivery profile. First confirm that encoded geometry is large enough to matter. Then choose precision and speed settings against target hardware. Finally, continue optimizing the resources and runtime work Draco does not touch.

What Draco compresses in glTF#

KHR_draco_mesh_compression provides a compressed representation of a mesh primitive's indices and vertex attributes. The extension identifies a buffer view containing Draco data and maps glTF attribute semantics to Draco attribute identifiers. A supporting loader decodes that payload into renderable attribute and index arrays.

Typical compressed semantics include:

  • POSITION;
  • NORMAL;
  • TANGENT where supported by the tool path;
  • texture coordinates;
  • vertex colors;
  • joint indices and weights in compatible workflows;
  • primitive indices.

Tool support and encoder behavior vary, so inspect the final extension mapping. Morph targets are not automatically made irrelevant, animation uses separate accessors, images remain image payloads, and scene JSON remains JSON.

The Khronos extension specification defines how Draco data is attached to glTF. The Google Draco project supplies reference encoders and decoders.

A component example#

Consider a 20 MB GLB with:

  • 8 MB uncompressed or lightly encoded geometry;
  • 10 MB embedded textures;
  • 1 MB animation;
  • 1 MB scene data and other payloads.

If Draco reduces geometry by 85%, 8 MB becomes 1.2 MB. Total becomes approximately 13.2 MB, a 34% reduction overall—not 85%. If the textures already account for 16 MB of a different 20 MB file, the same geometry ratio produces a much smaller total improvement.

Now consider runtime. Decoded geometry reconstructs the original number of triangles and attribute rows, within chosen precision. If the source has 2 million triangles, the renderer still receives about 2 million triangles. Draco changed representation during transfer, not scene complexity.

Always list geometry as a percentage of total encoded bytes before predicting a result.

Quantization supplies much of the leverage#

Geometry values often begin as 32-bit floating point. Draco encoders quantize attributes to a finite number of bits, then exploit prediction and entropy coding. Lower precision can reduce bytes but changes values.

Position precision needs asset-scale context. Across a 20-meter bounding span:

  • 10 bits provide 1,023 intervals, around 19.55 millimeters each;
  • 12 bits provide 4,095 intervals, around 4.88 millimeters;
  • 14 bits provide 16,383 intervals, around 1.22 millimeters;
  • 16 bits provide 65,535 intervals, around 0.305 millimeters.

This simplified uniform calculation shows why “14-bit positions” is not one visual quality. The same 14 bits across a 20-centimeter object yield steps near 0.012 millimeters. Bounding strategy and encoder implementation matter.

Normals use angular precision. UV error can reveal seams or shift fine text. Skin weights can change deformation. Evaluate each semantic under the exact use: closest camera, moving specular light, animation extremes, and texture resolution.

Do not lower every quantization field equally. A stylized flat-color prop may tolerate coarse UVs but reveal silhouette steps; a relief-rich normal-mapped panel may need stable normals and UVs more than submillimeter positions.

Decode is a real startup stage#

The browser cannot bind Draco bytes directly as ordinary mesh buffers. It initializes decoder code, reads the bitstream, allocates output, reconstructs topology and attributes, and hands arrays to the loader. WebAssembly and workers can make this efficient, but computation and allocation remain.

Cold clients may also download the decoder. Record:

  1. decoder JavaScript and WebAssembly transfer bytes;
  2. time until the decoder is initialized;
  3. worker startup and message overhead;
  4. compressed payload bytes;
  5. decode wall time;
  6. main-thread blocking time;
  7. output typed-array bytes;
  8. peak memory while input and output overlap.

A 500 KB geometry saving may be erased by a cold decoder payload for a site that loads only one tiny model. A catalog where dozens of models share a cached decoder can benefit strongly.

Use a worker pool sized for the target. Starting eight large decodes on a four-core mobile device can create CPU contention, memory spikes, and thermal load. Queueing may produce a faster first usable model even if total batch completion is later.

Draco does not compress textures#

Textures frequently dominate GLB transfer and usually dominate decoded asset memory. Draco does not resize a 4096 × 4096 base color, pack ORM channels, remove unused alpha, generate better mips, or choose a GPU transcode target.

Audit images separately:

  • dimensions and channels;
  • PNG, JPEG, WebP, or KTX2 representation;
  • encoded bytes;
  • decoded and GPU memory estimate;
  • closest projected use;
  • alpha and color-space requirements;
  • duplicate or orphaned content.

Pair geometry compression with content-aware image work. A dense scan might need Draco plus a 4096-to-2048 texture test. A simple product with huge photography may need almost no geometry change and substantial texture reduction.

Avoid putting Draco and KTX2 under one vague “compression enabled” status. They use different decoders, affect different resources, and have different failure modes.

Draco does not reduce draw calls#

Each glTF primitive has its own material assignment and extension payload. Compressing 120 primitives leaves 120 primitives. If the scene was CPU-bound by submissions, frame time may remain unchanged.

Compression can even make a highly fragmented asset awkward at startup because many small payloads or decode jobs carry overhead. Exact behavior depends on how the tool packs primitives. Consolidate compatible materials and geometry based on runtime grouping, then compress the resulting delivery primitives.

Keep boundaries needed for transparency, animation, culling, selection, and variants. The goal is not one giant Draco block; it is a sensible scene structure whose geometry is encoded efficiently.

Report before and after:

  • primitive count;
  • material count;
  • draws by pass;
  • nodes and meshes;
  • average triangles per primitive;
  • compressed bytes per primitive;
  • total decode tasks and duration.

If only bytes change, do not claim a draw-call improvement.

Draco does not simplify geometry#

Mesh simplification removes or reorganizes triangles while attempting to preserve shape and attributes. Draco encodes the triangles it receives. A 90% byte reduction can leave the full topology intact.

For runtime-heavy dense models, simplify first against a visual error target, optimize vertex and index ordering, then compress. This sequence can reduce both decoded work and transferred bytes.

Compare two independent ratios:

  • topology ratio: output triangles divided by source triangles;
  • encoding ratio: compressed geometry bytes divided by raw geometry bytes.

A model at 25% topology and 20% encoding has a different quality and performance story from a model at 100% topology and 5% encoding. The second may download smaller yet render four times as many triangles.

Preserve the unsimplified source. Draco decoding is not an editing workflow, and a delivery mesh should not become the only master.

Draco does not solve GPU memory by itself#

After decode, positions, normals, UVs, indices, and other attributes occupy buffers. Depending on the loader, decoded values may be floats or compact types. Input bytes and decoder output can coexist temporarily.

If an 800 KB Draco payload reconstructs 24 MB of buffers, the GPU allocation is still near the renderable layout. Lower quantization might influence output types in some paths, but do not assume the 800 KB figure describes resident memory.

Measure loaded attribute arrays and index types. Include morph, skin, and animation resources. Geometry compression can reduce retained network buffers if they are released, but GPU budget needs a separate calculation.

Compatibility is a deployment requirement#

A required glTF extension means a loader without support cannot render the primitive. Test every target viewer, not just file validity. Some pipelines can include fallback attributes alongside the compressed extension, but doing so increases file size and specific extension rules must be followed.

For a web application you control:

  • deploy a loader version with verified Draco support;
  • configure decoder paths before loading;
  • host decoder artifacts with correct MIME types;
  • use HTTPS and compatible cross-origin policy;
  • cache versioned decoder files;
  • verify worker creation under the site's content security policy;
  • provide an error state when decoder initialization fails.

For third-party destinations, check their current accepted extension set. A structurally valid Draco GLB may still be rejected or transcoded by a platform.

Run the Khronos validator, but treat it as structural evidence. It cannot prove the site's decoder URL is correct or the low-end phone can decode within the experience target.

Geometry ordering and compression profile#

Compression is not isolated from prior mesh processing. Vertex deduplication, triangle order, attribute precision, and primitive partitioning affect results. Tools such as gltf-transform can apply deduplication, pruning, reorder, quantization, Draco, meshopt, and texture steps in explicit pipelines.

Record the exact tool version and options. “Draco high” in one GUI is not a reproducible specification. Store a command or configuration plus metrics:

  • input hash and source revision;
  • encoder and version;
  • quantization bits by semantic;
  • speed or compression level settings;
  • output geometry bytes;
  • decode timing devices;
  • reference render results.

Deterministic or at least explainable builds make regressions visible. A tool upgrade that saves 3% but doubles decode time should not pass unnoticed.

Compare a candidate matrix#

Build at least four outputs:

  1. uncompressed geometry at approved source precision;
  2. Draco with conservative precision and fast settings;
  3. Draco with target precision and balanced settings;
  4. alternative meshopt or quantized delivery where the platform supports it.

If runtime geometry is also excessive, add simplified versions at approved ratios before encoding. Use the same images and scene structure while comparing codecs so results are attributable.

For each candidate capture:

  • total GLB and geometry bytes;
  • decoder cold and warm bytes;
  • cold and warm load milestones;
  • decode duration on low, median, and high devices;
  • peak memory;
  • first interactive frame;
  • steady CPU and GPU frame time;
  • image or geometric error from fixed views;
  • extension support across destinations.

Select against the product priority. An offline-installed application may value decode speed and runtime layout over another 5% network saving. A mobile web landing page may value transfer strongly but reject a main-thread stall.

When Draco is a strong fit#

Draco is often valuable when:

  • geometry is a substantial portion of transfer bytes;
  • assets contain dense positions, normals, UVs, or colors;
  • the decoder is already cached or shared across many assets;
  • target loaders fully support the extension;
  • a modest decode stage fits the interaction milestone;
  • quantization can be chosen without visible damage.

It is lower priority when a file is dominated by images, the mesh is already tiny, the target does not support the extension, or the application's critical problem is draw calls, fragment shading, or GPU memory.

Complete optimization sequence#

  1. Validate and inventory the source GLB.
  2. Establish transfer, decode, memory, and frame baselines.
  3. Remove unused nodes, accessors, images, and material slots.
  4. Simplify geometry only where runtime and quality evidence support it.
  5. Consolidate scene structure without breaking culling or interaction.
  6. Choose attribute precision against asset scale and target cameras.
  7. Encode Draco and at least one comparison profile.
  8. Optimize texture dimensions, formats, channels, and reuse separately.
  9. Test cold and warm decode on representative devices.
  10. Validate, render, profile, and retain the build report.

Shipping checklist#

  • Geometry's original share of total bytes justifies codec work.
  • Quantization is documented per attribute semantic.
  • Closest-view and animation tests pass after quantization.
  • Decoder transfer and initialization are included in cold timing.
  • Decode work does not freeze interaction.
  • Peak input-plus-output memory fits low-tier devices.
  • Textures have their own optimization and memory plan.
  • Primitive, material, and draw counts are reviewed separately.
  • Runtime triangle and vertex budgets pass after decode.
  • Every target viewer supports the required extension.
  • Decoder hosting, CSP, worker, cache, and error paths are tested.
  • Tool versions and commands make the output reproducible.

Sources and further reading#

Draco is effective at the job it actually performs: reducing encoded mesh geometry. A fast GLB still needs appropriate topology, textures, scene grouping, memory behavior, decoder deployment, and target-device validation.

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