Reducing GLB file size without wrecking quality

"Without quality loss" is doing a lot of work in that phrase. Some of these techniques genuinely are lossless; the ones that give you the big numbers are not, and knowing which is which is the whole skill.

Last reviewed July 27, 2026

Find out what is actually big first

A GLB is a JSON chunk plus a binary chunk holding geometry buffers and embedded images. In practice, textures dominate — a typical product model is 70–90% image data. Optimising geometry on a texture-bound asset is effort spent in the wrong place, so measure before you compress.

The quickest read is to list the images and their dimensions. If you find a 4096 × 4096 base-colour map on a product that renders 400 px wide on a phone, you have found your problem, and no clever geometry codec will matter more than fixing it.

The genuinely free wins

These change bytes without changing pixels. Always do them, always first.

  • Deduplicate. Exporters routinely emit the same texture or mesh multiple times. gltf-transform dedup removes exact duplicates.
  • Prune. Unused materials, orphaned nodes, unreferenced accessors and empty animation channels are all dead weight. gltf-transform prune.
  • Drop what you do not use. A second UV set nobody samples, a tangent attribute for a material with no normal map, an occlusion map at 4K when the effect is barely visible.
  • Transport compression. Serve with gzip or Brotli. The JSON chunk compresses dramatically; already-compressed images will not, which is why gzipped size is the number worth tracking rather than raw size.

Geometry: quantization, meshopt, Draco

glTF stores vertex attributes as 32-bit floats by default, which is far more precision than any product model needs. Quantization (KHR_mesh_quantization) stores positions, normals and UVs at 16 or 8 bits. It is technically lossy, and at sane bit depths the error is far below what a display can resolve — this is the closest thing to a free lunch in the format.

[`EXT_meshopt_compression`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_meshopt_compression) re-encodes the quantized buffers into a form that compresses well with gzip and decodes extremely fast in the browser. It also preserves a GPU-friendly vertex layout, so you are not trading load time for render time.

[`KHR_draco_mesh_compression`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_draco_mesh_compression) typically produces smaller geometry on disk than meshopt, at the cost of a heavier WASM decoder and noticeably more CPU work at load. On a mid-range phone that decode is not free, and it happens before anything appears on screen. Draco also has thinner support outside the browser — several USDZ conversion paths will not read it.

Textures: resize, then re-encode

  1. Resize. Halving a texture's dimensions quarters its pixels. Nothing else in this article has that leverage. Decide the resolution from how large the product renders, not from what the source happened to be.
  2. Repack. glTF wants occlusion, roughness and metalness in one texture's R/G/B channels. Three separate greyscale maps is triple the cost for identical output.
  3. Re-encode. WebP (EXT_texture_webp) is smaller on disk than JPEG at equal quality. KTX2 (KHR_texture_basisu) is roughly JPEG-sized on disk but stays compressed in GPU memory. They solve different problems — see WebP vs KTX2.

Order of operations

Sequence matters more than tool choice. Dedup and prune first, so you are not paying lossy costs on data you were about to delete. Then resize textures. Then re-encode them. Then quantize geometry. Then apply meshopt or Draco. Then gzip at the edge. Running a lossy texture pass before deduplication is the single most common way to get a worse result from the same tools.

The free tools, and when they are enough

Genuinely: try these first. For a lot of assets they are all you need, and recommending otherwise would be dishonest.

gltfpack and gltf-transform
# gltfpack — quantize + meshopt, resize textures to half
gltfpack -i model.glb -o out.glb -cc -tr -ts 0.5

# gltf-transform — explicit, inspectable passes
gltf-transform dedup     model.glb  a.glb
gltf-transform prune     a.glb      b.glb
gltf-transform resize    b.glb      c.glb --width 2048 --height 2048
gltf-transform webp      c.glb      d.glb --quality 85
gltf-transform meshopt   d.glb      out.glb

gltfpack is the fastest path to a much smaller file and is excellent software. gltf-transform gives you passes you can reason about individually, which is what you want when something regresses.

Where a dedicated pipeline earns its keep

The limitation of a fixed flag set is that it applies the same recipe to a 200-triangle label and a 900,000-triangle scan. Per-asset tuning — choosing texture format and resolution per map, per material — is where the remaining headroom lives. On our published leaderboard that difference measures as a median 81% gzipped-size reduction across a 103-asset public benchmark, versus about 21% for `gltf-transform` at defaults. Every asset where we lose to a free tool is published alongside the wins at /benchmark, because a benchmark without losses is marketing.

The more important habit, whichever tool you use, is verification. "Smaller" is easy; "smaller and still correct" needs a before/after comparison — perceptual difference on rendered views, triangle and mesh counts, material parity. A pass that silently drops a UV set or inflates a vertex-coloured mesh is a regression regardless of what the size number says.

And if the asset is destined for iOS AR, remember that USDZ carries neither KTX2 nor WebP, so a texture strategy tuned purely for the web gets re-encoded on the way to Quick Look. Converter issues covers what else changes in that hop.

Common questions

Is Draco or meshopt better?
Draco usually produces smaller geometry on disk; meshopt decodes far faster and keeps a GPU-friendly vertex layout, and it also has broader support in non-browser toolchains. For product pages where time-to-first-render matters, meshopt is generally the safer default.
Does compressing a GLB reduce GPU memory usage?
Geometry codecs do not — Draco and meshopt shrink the file, not the decoded buffers. Attribute quantization does reduce memory. For textures, only GPU-compressed formats such as KTX2 stay compressed in video memory; WebP, JPEG and PNG all decode to uncompressed RGBA.
What is the single biggest win for a typical product model?
Reducing texture dimensions. Textures are usually 70–90% of a GLB, and halving a texture's width and height removes three quarters of its pixels — no other technique has that leverage.
Should I just use gltfpack?
Often, yes. It is free, fast, well-engineered, and sufficient for many assets. The case for anything more is per-asset tuning and verified quality, not a bigger number in a single run.
Check your file

Reading about it is slower than measuring it. Drop your .glb, .gltf, or .usdz in and get every blocker on this page checked against the real bytes — packaging, textures, geometry, AR parity — with the fix for each one.

Check your file free at /scorecardNo account needed to run a check.

Related

Sources