From Blender to Launch: An End-to-End Compression Pipeline
Take a 50MB PBR model through the full pipeline, from Blender export to KTX2 textures and engine loading, and get it under 5MB. Scripts and cheat sheet included.
By now we've covered the tools, theory, and decision-making. In this final post, we'll stitch everything together into a real, runnable pipeline: starting from a real Blender model, compressing step by step, tracking file size, VRAM, and load time, and seeing if we can turn a 50MB beast into a 5MB model that loads instantly on a phone.
Target audience: those who've read the first 5 posts and are ready to do it for real. No new concepts here—just a repeatable process, commands, and scripts.
Starting Point: A Real PBR Model
Let's use a typical e-commerce product showcase model: a high-precision product with full PBR textures.
| Initial Metric | Value |
|---|---|
| Blender source file | ~120MB (includes unexported high-poly) |
| Exported GLB (float32 + PNG) | ~50MB |
| Vertex count | ~180k |
| Textures | 6x 4096×4096 (albedo, normal, roughness, metallic, AO, emissive) |
| VRAM usage (all textures decompressed) | ~520MB |
| Target | File ≤ 5MB, VRAM manageable, instant mobile load |
A 50MB file with 520MB of VRAM—this model would crash any mobile device. Let's go step by step.
Step 0: Exporting Correctly from Blender
The first compression checkpoint is actually the export itself. Many people lose blood here.
Key settings when exporting glTF from Blender:
- Format:
glTF Binary (.glb)(single file, easier to transfer) - Geometry: Check
Normals,Tangents(PBR normal maps need tangents) - UV: Ensure they're exported (on by default)
- Textures:
AutomaticorJPEG(texture format doesn't matter now—we'll recompress later, but make sure they're exported) - Compression: Do not check Blender's built-in Mesh compression; we'll use more professional tools
- Transform:
+Y Up(glTF standard) - Data: Only export what you need (animations, cameras, lights—if not needed, don't export them and reduce size)
After export, model.glb: 50MB, 6 PNG textures, float32 vertices. This is our baseline.
Common pitfall: Blender exports unused meshes, hidden helper objects, etc. by default. Before exporting, run
File > Clean Up > Purge Orphansand select only the objects you want to export in the outliner.
End-to-End Pipeline Overview
Let's visualize the whole pipeline:
Blender source file
│ Export .glb (float32 + PNG) 50MB
▼
[1] Deduplication + Weld duplicate vertices (gltf-transform) ~45MB
│
[2] Vertex compression: MeshOpt (gltfpack / gltf-transform) ~30MB
│
[3] Texture compression: PNG → KTX2 (ETC1S/UASTC) ~6MB
│
[4] (Optional) Geometry simplification LOD (simplify) ~4-5MB
▼
Final model-final.glb ~5MB
│
Engine loading (Three.js / Babylon.js) → runtime transcoding → Deploy
The numbers for each step will be tracked in the table below.
Toolchain: Which One to Choose
Several compression tools are available. Let's compare them so you don't pick the wrong one:
| Tool | Strengths | Weaknesses | Best For |
|---|---|---|---|
| gltf-transform | All-in-one, textures + vertices, scriptable | Not the absolute best compression ratio | Recommended primary tool for most scenarios |
| gltfpack | Professional vertex compression, native MeshOpt | Weak texture compression | Vertex-heavy models, need fine MeshOpt control |
| toktx | Most professional texture compression, full parameters | Textures only, not whole models | Fine-tuning single textures |
| gltf-pipeline | Old-school, supports Draco | Not actively maintained, limited features | Existing Draco projects |
| Online tools (gltf.report) | Zero installation | Not suitable for automation, batch jobs | Experiments, one-off tasks |
Main recommendation: Use gltf-transform for the entire pipeline. Supplement with gltfpack for vertices and toktx for individual textures when needed. All steps below use gltf-transform.
Step 1: Deduplication + Weld
Models often have duplicate vertices, unused nodes, and materials. Clean them up first.
gltf-transform optimize model.glb step1.glb --weld --prune
| Stage | File Size | VRAM | Change |
|---|---|---|---|
| Baseline | 50MB | ~520MB | — |
| Step 1 Dedup | 45MB | ~520MB | -5MB (VRAM unchanged, textures still there) |
VRAM barely moved—expected. Deduplication mainly saves vertices and structure; textures are the VRAM hog.
Step 2: Vertex Compression with MeshOpt
gltf-transform optimize step1.glb step2.glb --meshopt --weld --prune
--meshopt quantizes vertices to 16-bit and uses MeshOpt lossless encoding, automatically adding the EXT_meshopt_compression extension.
| Stage | File Size | VRAM | Change |
|---|---|---|---|
| Step 1 | 45MB | ~520MB | — |
| Step 2 + MeshOpt | 30MB | ~520MB | -15MB (vertex part) |
VRAM still ~520MB? Yes—because vertices take up a small portion of VRAM (10-20%). Cutting vertices has limited impact on VRAM. The real VRAM monster is textures, which we tackle next.
Step 3: Texture Compression PNG → KTX2
This step is the biggest bang for your buck.
gltf-transform optimize step2.glb step3.glb \
--texture-compress basisu \
--meshopt --weld --prune
--texture-compress basisu automatically determines each texture: color textures (albedo, emissive) use ETC1S, data textures (normal, roughness, metallic, AO) use UASTC.
| Stage | File Size | VRAM | Change |
|---|---|---|---|
| Step 2 | 30MB | ~520MB | — |
| Step 3 + KTX2 | 6MB | ~70MB | -24MB file / -450MB VRAM |
This step is the turning point of the entire pipeline:
- File size drops from 30MB to 6MB
- VRAM drops from 520MB to ~70MB—because the 6 4096 textures go from "decompressed raw pixels" to "block compressed", each dropping from ~87MB to ~11-14MB
VRAM is reduced by an order of magnitude. This is the key to whether a mobile device can handle it.
Step 4: (Optional) Geometry Simplification
If you want even smaller, and the scene can tolerate lower vertex precision, add geometry simplification.
gltf-transform optimize step3.glb final.glb \
--texture-compress basisu \
--meshopt \
--simplify --simplify-ratio 0.5 \
--weld --prune
--simplify-ratio 0.5 means keep about 50% of the vertices.
| Stage | File Size | VRAM | Change |
|---|---|---|---|
| Step 3 | 6MB | ~70MB | — |
| Step 4 + simplify 0.5 | 4.5MB | ~70MB | -1.5MB (VRAM nearly unchanged) |
Simplification mainly saves file size, with little impact on VRAM. The trade-off is reduced model detail—noticeable at close range. For e-commerce product pages, over-simplification is usually not recommended; it's great for architecture/large scenes.
Full Progress Tracking Table
Let's stack all four steps for a complete picture (based on the sample model, numbers are illustrative):
| Step | File Size | VRAM | Cumulative Reduction |
|---|---|---|---|
| Baseline (float32 + PNG) | 50MB | ~520MB | — |
| + Dedup + Weld | 45MB | ~520MB | -10% |
| + MeshOpt vertices | 30MB | ~520MB | -40% |
| + KTX2 textures | 6MB | ~70MB | -88% file / -87% VRAM |
| + Geometry simplify (0.5) | 4.5MB | ~70MB | -91% file |
Conclusion: Texture compression provides the vast majority of file size and VRAM savings. Vertex compression is the icing on the cake; texture compression is the lifesaver. This perfectly matches the conclusion from Part 1—textures make up 80% of the volume, so optimizing them yields the highest return.
One-Command Version: Lazy All-in-One
If you don't want to do it step by step, apply all optimizations at once:
gltf-transform optimize model.glb model-final.glb \
--texture-compress basisu \
--meshopt \
--simplify --simplify-ratio 0.5 \
--weld --prune
This single command = dedup + weld + MeshOpt vertices + KTX2 textures + geometry simplification. It covers 90% of scenarios. The step-by-step approach is mainly for understanding and tuning parameters.
Don't Want to Set Up an Environment? Use Any3D Online Compression
The above gltf-transform commands and scripts require installing Node, configuring the toolchain, and remembering a bunch of parameters. Any3D's online compression tool eliminates all that:
- No need to download scripts or set up an environment—just open a web page and use it
- Model never leaves your device—everything is processed locally in the browser
- Visual configuration—texture KTX2, vertex MeshOpt, geometry simplification—sliders for parameters, real-time preview
- One-click multi-platform—export compressed results for mobile / desktop / VR
Pick a GLB, choose a target platform, click, and get the compressed model. Under the hood it's the same engine as the command line (gltf-transform), but with zero learning curve—no terminal required.
Common Pitfalls in Practice
Model turns black / textures don't show after compression
- 99% of the time it's color space: color textures missing sRGB. In Three.js:
texture.colorSpace = THREE.SRGBColorSpace. - When using toktx, forgot
--srgbfor color textures.
Normal map lighting is wrong after compression
- Normal map was encoded with ETC1S; switch to UASTC.
- Normal map is in DirectX style (green channel pointing down), but the engine expects OpenGL style; need to flip the G channel.
Mobile loading hangs on the first screen
- Check if you're loading the Draco decoder wasm (extra request). Prefer MeshOpt for mobile.
- KTX2 transcoder path is misconfigured, causing fallback to CPU decompression.
Compressed file is larger than the original
- Texture is too small (< 128px). KTX2 isn't cost-effective for small textures due to block compression overhead.
- Model was already compressed; recompressing offers no benefit (or negative benefit).
Model breaks after simplification
--simplify-ratiois too low; try 0.7-0.8.- Simplification works well for hard surfaces (mechanical, architectural) but can break organic surfaces (characters).
KTX2 fails to load in some browsers
- Old Safari / old WebView don't support it. Prepare a PNG/WebP fallback, or use the
fallbackfield ofKHR_texture_basisuto provide backup textures.
Series Cheat Sheet
The essence of all 6 posts condensed into a single table. Bookmark this.
Volume Breakdown
| Component | Percentage | Optimization Tool |
|---|---|---|
| Textures | 70-85% | KTX2 (biggest gain) |
| Vertex data | 10-20% | MeshOpt / Quantization / Draco |
| Animation data | 0-15% | Reduce keyframes / compress |
| Other | < 2% | Deduplication |
VRAM Formula
Traditional format VRAM = width * height * 4 bytes * 1.333 (with mipmaps)
KTX2 block compressed VRAM ≈ above / 4 (ETC1S) or / 2 (UASTC)
Vertex Compression Selection
| Scenario | Recommended |
|---|---|
| Zero dependencies, simplest | Pure quantization (KHR_mesh_quantization) |
| Web balanced first choice | MeshOpt |
| Extreme compression ratio, can wait for decode | Draco |
| Mini programs / package-sensitive | Pure quantization / MeshOpt, avoid Draco |
Texture Compression Selection
| Texture Type | Recommended Codec |
|---|---|
| albedo / emissive (color) | KTX2 ETC1S |
| normal / roughness / metallic / AO (data) | KTX2 UASTC |
| Desktop web, prioritize download speed | WebP / AVIF |
| Small textures (< 128px) | Keep PNG, don't compress to KTX2 |
One-Click Command
# Full optimization (textures + vertices + simplification)
gltf-transform optimize model.glb model-final.glb \
--texture-compress basisu --meshopt \
--simplify --simplify-ratio 0.5 --weld --prune
Platform Quick Reference
| Platform | Textures | Vertices |
|---|---|---|
| Desktop Web | WebP / KTX2 | MeshOpt |
| Mobile Web | KTX2 mandatory | MeshOpt |
| VR | KTX2 mandatory | MeshOpt + LOD |
| Mini programs | KTX2 / WebP | MeshOpt / Quantization |
| Large scenes | KTX2 mandatory | MeshOpt + Draco + LOD |
Series Recap
Six posts take you through a complete chain:
- Why Are 3D Models So Big?: Understand volume composition and VRAM reality
- First Lesson in Model Slimming: Vertex Compression's Three Techniques: How quantization, MeshOpt, and Draco work, and how to choose between them
- Textures: The VRAM-Hungry Gluttons You Never Saw Coming: Why PNG/JPG are guilty in the GPU's eyes
- KTX2 in Practice: The Right Way to Use Texture Compression: ETC1S vs UASTC selection, toolchains, and engine loading
- Different Platforms, Different Fates: A Guide to Choosing Compression Schemes: A decision framework by platform and scenario
- This Post: The end-to-end pipeline, from Blender to launch
The core message in one line: figure out the bottleneck first (download / VRAM / frame rate), then pick your tools; texture compression brings the biggest gain, vertex compression is the icing on the cake, and different platforms play by different rules, so don't apply one formula to everything.
Follow the scripts and cheat sheet in this post, and taking a model from 50MB down to 5MB (with VRAM dropping from 520MB to 70MB) becomes a repeatable path. The rest is just doing it.