Polygon Budgets, Draco Mesh & KTX2 Texture Compression
Deep-dive guide to geometry quantization and GPU-native texture compression for instant mobile WebAR loading and low VRAM usage.
Why PNG and JPEG Hurt Mobile WebGL Memory
Traditional web images like JPEG and PNG compress well on disk and over HTTP, but GPUs cannot sample them directly. Before rendering a single frame, the browser must decode a 2048x2048 PNG into raw uncompressed RGBA pixels in video memory—consuming 16 MB (or ~21.3 MB with mipmaps) per texture map regardless of how small the PNG file looked.
KTX2 containers encoded with Basis Universal (ETC1S or UASTC) solve this bottleneck by transcoding directly on the client into hardware-native GPU block formats such as ASTC on iOS/modern Android and BC7 on desktop. Because blocks remain compressed inside GPU VRAM, video memory consumption drops by 4x to 8x while eliminating main-thread image decode stalls.
- Use UASTC encoding inside KTX2 for high-frequency normal maps, fine logos, and hero albedo textures.
- Use ETC1S encoding for large ambient occlusion, rough surfaces, and low-detail trim textures.
- Generate full mipmap chains offline inside the KTX2 file so GPUs avoid runtime mipmap generation pauses.
Draco vs. Meshopt Geometry Compression
Geometry buffers containing 32-bit float positions, normals, and UVs quickly bloat multi-part 3D models. Google Draco (`KHR_draco_mesh_compression`) quantizes vertex attributes to configurable bit depths and applies prediction coding, routinely shrinking mesh payloads by 75% to 90%.
Alternatively, Meshoptimizer (`EXT_meshopt_compression`) pairs vertex-cache reordering with fast SIMD byte decoding that decompresses several times faster than Draco on mobile CPUs, especially when combined with HTTP Brotli/gzip compression. Choosing between Draco and Meshopt depends on whether raw byte size or decode milliseconds dominates your latency profile.
- Quantize vertex positions to 14 bits and normals/UVs to 10-12 bits to avoid visible stepping artifacts.
- Avoid over-quantizing tiny eyewear hinges or watch hands below 12 bits where vertices can snap together.
- Test decode time in a Web Worker using `gltf-transform` benchmarks on mid-tier mobile hardware.
Setting Practical Triangle and Vertex Budgets
Modern smartphone GPUs push hundreds of thousands of triangles effortlessly in native games, yet browser WebAR shares GPU cycles with live camera decoding, CV neural networks, and DOM compositing. Keeping individual wearable assets between 15,000 and 50,000 triangles leaves ample frame headroom while looking completely smooth under Phong/PBR shading.
Rather than applying blind uniform decimation—which flattens curved eyeglass rims into jagged hexagons—artists should retain edge loops along silhouette contours and reduce polygon density across flat backsides and interior fasteners.
- Allocate vertex density proportionally to silhouette curvature rather than flat interior surfaces.
- Use smooth vertex normals and high-resolution normal maps to fake small bevels instead of dense chamfers.
- Verify wireframe density around lenses, watch bezels, and shoe toe boxes before final compression.
Level of Detail (LOD) and Progressive Asset Streaming
Even with Draco and KTX2, complex multi-component furniture or luxury watch scenes can reach 3 to 5 MB. Serving a two-stage progressive experience—loading a 300 KB LOD0 proxy mesh with 512px textures first and swapping in the full 2048px asset asynchronously—gives shoppers immediate interactivity within half a second.
When users view a catalog grid of 12 3D thumbnails simultaneously, never instantiate 12 live WebGL contexts (most browsers cap active WebGL contexts at 8 to 16). Render static posters for grid cards and activate the live WebGL canvas only on the hovered or selected product.
- Enforce a single shared WebGL context or max 1-2 active canvases per browser tab.
- Combine `gltf-transform` CLI pipelines with automated visual diff checks after compression.
- Monitor both transfer bytes (`Content-Length`) and runtime GPU VRAM allocation per SKU.
Compression Format Comparison for Mobile WebAR
| Technology | Target Data | Compression Ratio | Runtime GPU VRAM Impact |
|---|---|---|---|
| PNG (Baseline) | 2D Textures | 1x (baseline) | High (uncompressed RGBA in VRAM) |
| KTX2 (UASTC + Zstd) | Hero Albedo & Normals | 3x - 5x transfer | 4x - 6x lower VRAM (ASTC/BC7) |
| KTX2 (ETC1S) | Matte / Secondary Maps | 8x - 15x transfer | 6x - 8x lower VRAM (ETC2/BC1) |
| Draco (KHR_draco) | Mesh Geometry | 5x - 10x transfer | Decompressed vertex buffers |
| Meshopt + Brotli | Mesh + Animations | 4x - 8x transfer | Fastest CPU decode + cache-friendly |
KTX2 Basis Universal reduces both download size and GPU memory footprint; Draco/Meshopt optimize download and geometry decode.
Worked Illustration: Calculating GPU VRAM Savings with KTX2 Transcoding
This worked illustration compares video memory usage for a single 3D shoe model using three 2048x2048 textures (Albedo, Normal, ORM).
- Worked illustration step 1: Calculate uncompressed RGBA8 VRAM for one 2048x2048 texture: 2048 x 2048 x 4 bytes = 16.0 MB, plus 33% for mipmap chain = 21.3 MB per texture.
- Worked illustration step 2: Multiply by three texture maps (Albedo + Normal + ORM): 3 x 21.3 MB = 63.9 MB of GPU VRAM consumed just for textures.
- Worked illustration step 3: Re-encode the same three maps as KTX2 UASTC (8 bits per pixel block average = 1.0 byte/texel) and transcode to ASTC 4x4 on mobile: 2048 x 2048 x 1 byte x 1.33 mipmaps = 5.32 MB per texture.
- Worked illustration step 4: Sum KTX2 GPU footprint: 3 x 5.32 MB = 15.96 MB total texture VRAM (and even lower at 1024x1024 for ORM maps).
Worked Illustration Outcome: Switching from PNG to KTX2 UASTC cuts runtime GPU texture memory by 75% (from 63.9 MB down to 15.96 MB), preventing mobile Safari tab crashes.
Compression & Performance Verification Checklist
- Verify zero uncompressed PNG/JPEG textures remain inside production WebAR GLBs.
- Confirm normal maps use KTX2 UASTC rather than lossy ETC1S to prevent block-artifact shading.
- Check Draco quantization bit depths (14-bit position, 10-bit normal/UV) show no seam cracks.
- Test asset load and decode timing on mid-range mobile hardware over throttled 4G.
- Ensure browser tab stays under one active WebGL context per product viewer.
- Verify pre-generated mipmaps inside KTX2 eliminate shimmering on fine fabric weaves.
Frequently Asked Questions
Why shouldn't I use ETC1S compression on normal maps?
ETC1S uses aggressive color-endpoint quantization that introduces blocky staircase artifacts across smooth tangent-space gradients, resulting in ugly faceted surface shading. Always use higher-precision UASTC for normal maps.
Does Apple AR Quick Look support Draco and KTX2 inside USDZ?
No. USDZ uses Apple's native USD packaging with PNG/JPEG textures and standard mesh buffers. Your build pipeline should keep a clean master glTF and output Draco+KTX2 GLB for web alongside a standard USDZ for iOS Quick Look.
How does Draco affect skeletal animations or morph targets?
Draco compresses static vertex attribute buffers cleanly, though morph target deltas and skinning weights require careful attribute preservation. Meshopt often handles complex skinned garments with faster decode performance.
What is an ideal total GLB file size for e-commerce virtual try-on?
Aiming for 1.0 MB to 2.5 MB per GLB ensures sub-1.5-second load times on standard mobile 4G networks while preserving sharp PBR surface detail.
Primary Standards & Technical References
- Khronos Group — KTX 2.0 File Format & KHR_texture_basisu Specification
- Khronos Group — KHR_draco_mesh_compression glTF Extension
- Binomial — Basis Universal GPU Texture Codec Technical Documentation