Optimizing Mobile WebXR: Choosing Between Draco-Compressed and Uncompressed glTF in A-Frame
Decision guide for mobile WebXR asset loading in A-Frame comparing Draco-compressed glTF via a-asset-item and DRACOLoader against uncompressed glTF for bandwidth, decode cost and first-interaction constraints.
04 Aug 2026, 14:27 UTC

Decision for mobile WebXR glTF loading
For A-Frame 1.x with three.js r0.160+ and no server-side streaming, use Draco-compressed glTF with and three.js DRACOLoader for mobile WebXR scenes with multiple meshes when first contentful paint must stay under 3s on 4G, 60fps is required on mid-tier Android, and GPU memory is limited.
The decision hinges on bandwidth versus main-thread decode cost. Draco reduces transfer size 30-70% but adds WASM download and decode spikes at scene start.
Options comparison
The table compares supported loading paths for glTF in A-Frame.
| Strategy | Transfer Size | Decode Cost | Implementation Complexity | Best Use Case |
|---|---|---|---|---|
| Uncompressed glTF | High | Negligible | Low | Single low-poly models, high-end devices |
| Draco compressed glTF | Low | High WASM | Medium | Complex scenes, bandwidth constrained |
| Draco + Basis/KTX2 | Very Low | Very High | High | GPU memory limits, enterprise assets |
Trade-offs and constraints
Bandwidth vs main-thread blocking
Draco shrinks vertex and index buffers. The browser must first download the Draco WASM decoder, about 200-300KB, then decompress on the main thread. On mid-tier Android this can cause a visible hitch during scene start.
Memory pressure
Uncompressed glTF is easier on CPU but keeps raw vertex data in GPU buffers, increasing memory use. Devices with less than 2GB RAM are more likely to hit out-of-memory with large uncompressed models.
Small model paradox
For total model size under ~1MB, the decoder download can outweigh transfer savings, making uncompressed glTF faster to first interaction.
Concrete implementation
Preload with and configure the decoder path before the scene loads. Keep color management on and disable shadows on mobile.
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script>
window.addEventListener('load', () => {
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
// A-Frame uses the global three.js GLTFLoader which will pick up the DRACOLoader
});
</script>
<a-scene color-management="true">
<a-assets>
<a-asset-item id="model" src="model.drc.glb"></a-asset-item>
</a-assets>
<a-entity gltf-model="#model" position="0 1 -3"></a-entity>
</a-scene>
Decoder path must be CORS-accessible and match the three.js version bundled with A-Frame. A-Frame does not bundle DracoLoader by default.
Validation
- Measure load time via scene 'loaded' event and Performance API.
- Compare total transferred bytes in Network panel for .glb versus decoder.wasm.
- Monitor frame time with stats.js during first 5s. Accept Draco if transfer saving is greater than 40% and 95th percentile frame time stays below 16.7ms after decode completes.
Rollback to uncompressed by replacing the Draco source with a standard .glb and removing decoder configuration. No architectural changes are required.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.