How to compress a GLB without losing visual quality
The average furniture GLB exported from Blender or 3ds Max is 20–80 MB. The average web viewer should load in under 3 seconds on a 10 Mbps connection — which means the target file size is under 3–4 MB. That gap closes with four compression steps, none of which require paid software.
Why file size matters for the web
File size affects three things that matter for conversion:
- Time to first interaction. A 50 MB GLB takes 40+ seconds to download on a 10 Mbps connection. Buyers leave before the model appears. Cylindo's data shows that viewer sessions abandoned before interaction cost brands an average of 2.3 sales per week at median furniture price points.
- AR load time. iOS Quick Look requires a USDZ download before the AR session starts. Files over 50 MB frequently time out on mobile. Target under 20 MB USDZ.
- CDN costs. Every file transfer costs money. A 50 MB file served to 10,000 buyers per month is 500 GB of egress — significantly more expensive than a 5 MB file at the same traffic volume.
Step 1 — Geometry optimization
Before any compression, reduce the geometry:
- Remove hidden geometry. Delete interior surfaces, bottom panels, and any geometry that's never in view. A sideboard with a detailed interior that's never opened can lose 20–40% of its triangles this way.
- Decimate where it doesn't show. In Blender: Mesh → Clean Up → Decimate Geometry. Target: keep areas that are in-camera high-poly; reduce areas that are background or never at close range.
- Merge vertices. Exported models often have duplicate vertices at seams. Blender: Mesh → Clean Up → Merge by Distance (0.001 threshold).
import bpy
for obj in bpy.data.objects:
if obj.type == 'MESH':
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.delete_loose()
bpy.ops.object.mode_set(mode='OBJECT')Step 2 — Texture compression
Textures are usually 70–90% of a GLB's file size. Aggressive texture compression has the highest return of any optimization step.
| Texture type | Max resolution | Format |
|---|---|---|
| Albedo (base color) | 2048×2048 | JPEG at 85% for opaque; PNG for alpha |
| Normal map | 2048×2048 | PNG (JPEG artefacts break normals) |
| Roughness + metalness | 1024×1024 combined (ORM channel pack) | PNG |
| Emissive | 1024×1024 | JPEG at 85% |
Channel-packing roughness and metalness into a single ORM texture (Occlusion R, Roughness G, Metalness B) halves the texture count without any quality loss — glTF supports ORM natively.
Step 3 — Draco compression
Draco is Google's geometry compression codec for glTF. It compresses mesh data (vertex positions, normals, UVs) by 40–80% with no visual quality loss.
# Install
npm install -g gltf-pipeline
# Compress with Draco
gltf-pipeline -i model.glb -o model-draco.glb --draco.compressionLevel 7
# compressionLevel: 1 (fast/less) to 10 (slow/most)
# 7 is the practical sweet spot for furnitureStep 4 — KTX2 / Basis Universal textures
KTX2 with Basis Universal encoding is the next level: textures are transcoded on the GPU at load time, which means they stay compressed in GPU memory (unlike JPEG/PNG, which decompress fully before upload). This reduces GPU memory usage by 4–6× and improves load time on mobile.
# Install: https://github.com/KhronosGroup/KTX-Software
toktx --t2 --bcmp albedo.ktx2 albedo.png
toktx --t2 --bcmp --normal_mode normal.ktx2 normal.png
# Embed into GLB with gltf-transform
gltf-transform etc1s model.glb model-ktx2.glbSize targets after full optimization
| Product type | Before | After full pipeline | Reduction |
|---|---|---|---|
| 3-seat sofa | 45 MB | 3.2 MB | 93% |
| Dining chair (fabric) | 18 MB | 1.4 MB | 92% |
| Dining table (marble top) | 31 MB | 2.8 MB | 91% |
| Wardrobe (full interior) | 82 MB | 6.1 MB | 93% |
| Side table (metal + wood) | 8 MB | 0.7 MB | 91% |
Fenicher runs this full pipeline automatically on every uploaded GLB. You upload the raw export from Blender; the platform serves an optimized version to web viewers and generates a separate USDZ for AR.