Texture Compression Using KTX2 for Optimized WebGL Performance in Three.js

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

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.

Leave a comment

Your email address will not be published. Required fields are marked *