This article describes how to compress textures using KTX2 format and integrate them into a Three.js pipeline. Texture compression is a critical optimization step in reducing VRAM usage, improving performance, and increasing compatibility across desktop, mobile, and XR platforms.
Why Texture Compression Matters
Uncompressed textures are one of the biggest consumers of GPU memory. A single 2048×2048 RGBA texture can occupy up to 16 MB of VRAM. In real-time 3D rendering applications like those built with Three.js, using compressed textures enables:
- Lower VRAM usage
- Faster load times
- Smoother performance on low-end and mobile devices
- Better visual fidelity with mipmaps
What is KTX2?
KTX2 is the Khronos Group’s container format for compressed GPU textures. It supports Basis Universal compression and is GPU-friendly across platforms.
Key Features:
- Single file works on multiple GPU vendors (transcoded at runtime)
- Supports mipmapping and alpha channels
- Reduces file size and GPU memory footprint
Recommended Compression Workflow
1. Install toktx CLI Tool
You can install the KTX2 compression tool from KTX Software Tools.
bash Copy Edit brew install ktx-software # macOS choco install ktx-software # Windows
Or build from source using CMake.
2. Compress PNG/JPG to KTX2
bash Copy Edit toktx --bcmp --t2 --genmipmap output.ktx2 input.png
Flags Explained:
--bcmp: Uses Basis Universal supercompression--t2: Outputs in KTX2 format--genmipmap: Automatically generates mipmaps
3. Replace PNG/JPG in GLTF with KTX2
To avoid manual editing, tools like gltf-transform can patch your GLTF/GLB with KTX2 textures:
bash Copy Edit gltf-transform etc1s input.glb output.glb --texture-compress
Or manually replace in Three.js during runtime.
Three.js Integration
Set Up KTX2Loader
javascript
Copy
Edit
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
const ktx2Loader = new KTX2Loader()
.setTranscoderPath('/basis/') // Path to Basis WASM binaries
.detectSupport(renderer);
Load Texture with Material
javascript
Copy
Edit
ktx2Loader.load('textures/diffuse.ktx2', (texture) => {
const material = new THREE.MeshStandardMaterial({ map: texture });
mesh.material = material;
});
Fallback Strategy
You can use THREE.TextureLoader as a fallback for unsupported devices:
javascript
Copy
Edit
if (ktx2Loader.isSupported()) {
ktx2Loader.load('texture.ktx2', (texture) => { ... });
} else {
new THREE.TextureLoader().load('texture.jpg', (texture) => { ... });
}
Benefits of Using KTX2
FeatureBenefitCross-platformAutomatically selects best GPU formatSmaller file sizeUp to 10x smaller than PNG with mipmapsLower GPU memory usageKeeps VRAM free for geometry & shadersHigh performanceLess bandwidth and faster renderingWebXR-friendlyEssential for mobile/VR runtime support
Compression Modes
- ETC1S (Basis Universal)
- Maximum compression
- Slight loss in quality
- Best for mobile/XR
- UASTC
- Higher quality, larger size
- Closer to original texture
Example ETC1S command:
bash Copy Edit toktx --bcmp --t2 --genmipmap output.ktx2 input.png
Example UASTC command:
bash Copy Edit toktx --uastc --t2 --genmipmap output.ktx2 input.png
Comparison
FormatSize (2048×2048)VRAM UsageLoad TimeMobile SupportPNG~12 MB~16 MBSlowHighJPG~1.5 MB~16 MBMediumHighKTX2 (ETC1S)~500 KB~4 MBFastExcellentKTX2 (UASTC)~2 MB~8 MBFastGood
Debugging & Tools
- Basis Universal Transcoder
- glTF Validator
- Spector.js for GPU inspection
- Chrome DevTools > Performance > Memory tab
Conclusion
Texture compression using KTX2 is a powerful optimization technique for web-based 3D experiences. By reducing both file size and GPU memory consumption, developers can achieve faster rendering, improved performance, and broader hardware compatibility—especially crucial in XR and mobile use cases. Integrating KTX2 into your Three.js workflow is highly recommended for any production-level project.