Draco & Three.js: shrink your 3D models to a fifth of their size
What is Draco?
Draco is an open-source library by Google for compressing 3D geometries and point clouds. The goal is simple: make 3D models load faster on the web without any noticeable loss in visual quality.
An uncompressed .glb model can easily be several megabytes in size. With Draco, you can often reduce that to 10–20% of the original size – without any visible quality loss for the viewer.
How does Draco work?
Draco compresses the geometry data of a 3D model – vertices, normals, UV coordinates and indices. It does this using a combination of:
- Quantisation – coordinates are reduced to fewer bits (e.g. from 32-bit float to 11-bit integer)
- Predictive encoding – instead of absolute values, differences to the previous vertex are stored
- Entropy coding – frequent values get shorter codes (similar to ZIP compression)
The result is a .glb file with Draco-compressed buffers, which gets decoded in the browser at runtime.
Compressing a model with Draco
The best tool for this is gltf-transform – a Node.js CLI tool that works directly from the command line.
Installation:
npm install --global @gltf-transform/cli
Compress a model:
gltf-transform draco input.glb output.glb
With control over quantisation levels (fewer bits = smaller file, slightly less precision):
gltf-transform draco input.glb output.glb --quantize-position 10 --quantize-normal 8
As a rule of thumb: position at 10–14 bits, normals at 8 bits – that gives the best balance between file size and quality.
Loading a Draco model in Three.js
Three.js can't decode Draco-compressed models out of the box. You need the DRACOLoader as an add-on to the GLTFLoader.
First, copy the Draco decoder files from the Three.js package into your public folder:
node_modules/three/examples/jsm/libs/draco/
Copy the contents of that folder to e.g. public/draco/. Then in your code:
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('/models/mymodel.glb', (gltf) => {
scene.add(gltf.scene);
});Important:
setDecoderPathmust point to the folder containing the Draco WASM files (draco_decoder.wasmetc.). Three.js loads these at runtime – they must be publicly accessible.
Is Draco always worth it?
Not necessarily. A few things I've learned from real projects:
- Small models under ~100 KB barely benefit – the decoder overhead outweighs the savings
- Many small models on one page: better to merge them into a single
.glbfirst - Animated models compress less effectively since animation data isn't affected by Draco
- Create the
DRACOLoaderonce and reuse it – don't instantiate it fresh for every model
TL;DR
- Draco compresses 3D geometry down to 10–20% of the original size
- Compress with
gltf-transform draco input.glb output.glb - In Three.js: set up
DRACOLoaderand pass it to theGLTFLoader - Draco decoder files must be publicly accessible (
setDecoderPath) - For small models weigh the trade-off – not always worth it
Share
Leave a comment