Three.js From Zero · Article s6-05

S6-05 gltf-transform

Season 6 · Article 05

gltf-transform — the CLI your pipeline needs

One tool. Lints, optimizes, compresses, splits, merges, inspects glTF files. Composable commands. Your 100MB artist dump becomes a 3MB shippable in one script.

1. Install

npm install --global @gltf-transform/cli

# Verify
gltf-transform --version

2. The commands you'll use most

CommandPurpose
inspectDump file structure, sizes, extensions
validateRun Khronos validator, catch errors
dracoCompress geometry with Draco
meshoptOptimize + compress with Meshopt
uastc / etc1sCompress textures with Basis/KTX2
optimizeBundle of safe optimizations (recommended)
dedupDeduplicate repeated meshes/materials/textures
pruneRemove unused nodes/materials/textures
weldMerge near-identical vertices
simplifyQuadric-error-metric mesh simplification
resizeShrink oversized textures

3. The "big shrink" recipe

gltf-transform optimize input.glb output.glb \
  --compress meshopt \
  --texture-compress uastc \
  --simplify 0.5
# Plus if needed:
gltf-transform resize output.glb output.glb --size 2048 2048

optimize runs prune + dedup + weld + compressors in one go. Usually 5-10× smaller output.

4. Before/after: a real asset

StepSizeNotes
Raw FBX from Mixamo11 MB
→ glTF export (Blender)8.2 MBDefault settings
gltf-transform optimize2.1 MBPrune, dedup, weld
→ + draco720 KBGeometry compressed
→ + uastc420 KBTextures compressed

5. Programmatic API

CLI is built on a JS API you can script:

import { NodeIO } from '@gltf-transform/core';
import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
import { weld, dedup, prune, draco } from '@gltf-transform/functions';

const io = new NodeIO().registerExtensions(ALL_EXTENSIONS);
const doc = await io.read('input.glb');
await doc.transform(weld(), dedup(), prune(), draco());
await io.write('output.glb', doc);

Drop into a Node build. CI-friendly.

6. Inspection

gltf-transform inspect model.glb
┌ INFO ─────────────────────────────┐
│ version: 2.0                      │
│ generator: Khronos Blender exp... │
│ scenes: 1    nodes: 12    meshes: │
│ 8    materials: 5   textures: 14  │
└───────────────────────────────────┘
┌ MESHES ───────────────────────────┐
│  #  name    primitives  verts     │
│  0  chair       1        2104     │
│  1  table       1        5012     │
...

7. Authoring reminders

  • Always validate before committing.
  • Run prune — artists ship orphaned materials constantly.
  • dedup if multiple meshes share a texture — it'll ref once instead of duplicate.
  • weld before simplify — removes near-coincident vertices.

8. CI integration

# package.json
"scripts": {
  "assets:build": "gltf-transform optimize raw/*.glb public/assets/ \
    --compress meshopt --texture-compress uastc"
}

# GitHub Actions
- run: npm ci
- run: npm run assets:build
- uses: actions/upload-artifact@v4
  with: { path: public/assets }

9. Live demo — comparison

Loads DamagedHelmet in .gltf and .glb form. Same asset, different packaging. Check Network tab for transfer sizes.

10. Takeaways

  • gltf-transform is THE glTF Swiss Army knife.
  • One command: optimize runs prune + dedup + weld + compress. 90% of the win.
  • Programmatic API for CI.
  • Always inspect + validate before ship.
  • Stack with Draco/KTX2/Meshopt for final size.