npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@h00w/basis-universal-transcoder

v2.0.2

Published

WebAssembly-based Basis Universal texture transcoder with TypeScript bindings

Readme

@h00w/basis-universal-transcoder

A WebAssembly-based Basis Universal texture transcoder with TypeScript bindings.

Note: This library requires explicit WASM loading to prevent automatic WASM inlining during bundling, which could cause issues when users compile this library in their own projects.

Features

  • 🚀 High-performance WebAssembly implementation
  • 📦 TypeScript support with full type definitions
  • 🎯 Support for KTX2 files only
  • 🔧 Multiple output formats (BC1-7, ASTC, PVRTC, ETC, uncompressed)
  • 🌐 Works in both browser and Node.js environments
  • ⚡ Built with Vite

Installation

npm install @h00w/basis-universal-transcoder

Quick Start

Basic Usage

import { BasisUniversal } from '@h00w/basis-universal-transcoder';
import wasmUrl from '@h00w/basis-universal-transcoder/basis_capi_transcoder.wasm';

// Helper function to create WASM instantiator
function createWasmInstantiator(wasmUrl: string) {
  return async (imports: WebAssembly.Imports) => {
    const fetchPromise = fetch(wasmUrl);
    // Try streaming instantiation first (better performance)
    if (WebAssembly.instantiateStreaming) {
      try {
        return await WebAssembly.instantiateStreaming(fetchPromise, imports);
      } catch (e) {
        console.warn('Streaming instantiation failed, falling back to ArrayBuffer method:', e);
      }
    }
    // Fallback to manual compilation
    const response = await fetchPromise;
    const buffer = await response.arrayBuffer();
    return WebAssembly.instantiate(buffer, imports);
  };
}

// Initialize the module
const basisUniversal = await BasisUniversal.getInstance(createWasmInstantiator(wasmUrl));

// Load a KTX2 file
const fileData = await fetch('texture.ktx2').then(r => r.arrayBuffer());
const ktx2Data = new Uint8Array(fileData);

// Create a KTX2 transcoder
const ktx2Transcoder = basisUniversal.createKTX2Transcoder();

// Initialize with KTX2 data
if (ktx2Transcoder.init(ktx2Data) && ktx2Transcoder.startTranscoding()) {
    // Transcode to RGBA32 format
    const result = ktx2Transcoder.transcodeImageLevel({
        format: TranscoderTextureFormat.cTFRGBA32,
        level: 0  // mip level 0 (full size)
    });
    
    if (result) {
        console.log('Transcoded:', result.width, 'x', result.height);
        console.log('Data size:', result.data.length, 'bytes');
        
        // Use the transcoded data...
        displayTexture(result.data, result.width, result.height);
    }
}

// Clean up
ktx2Transcoder.dispose();

Format Detection

import { detectBestFormat, isFormatSupported, getFormatName } from '@h00w/basis-universal-transcoder';

// Detect the best format for the current platform (requires WebGL context)
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const bestFormat = detectBestFormat(gl);
console.log('Best format:', getFormatName(bestFormat));

// Check if a specific format is supported
if (isFormatSupported(TranscoderTextureFormat.cTFBC7_RGBA)) {
    console.log('BC7 is supported on this platform');
}

Advanced Usage

import { BasisUniversal, KTX2Transcoder, TranscoderTextureFormat } from '@h00w/basis-universal-transcoder';
import wasmUrl from '@h00w/basis-universal-transcoder/basis_capi_transcoder.wasm';

// Use the createWasmInstantiator helper function from the basic usage example
const transcoder = await BasisUniversal.getInstance(createWasmInstantiator(wasmUrl));
const ktx2Transcoder = transcoder.createKTX2Transcoder();
ktx2Transcoder.init(data);

// Get basis texture format
const basisFormat = ktx2Transcoder.getBasisTextureFormat();
console.log('Basis format:', basisFormat);

// Transcode multiple mip levels
const results = [];
ktx2Transcoder.startTranscoding();
for (let level = 0; level < 4; level++) { // Adjust based on your texture
    const result = ktx2Transcoder.transcodeImageLevel({
        format: TranscoderTextureFormat.cTFBC7_RGBA,
        level: level
    });
    if (result) {
        results.push(result);
    }
}

ktx2Transcoder.dispose();

⚠️ Important: Memory Management

Data Persistence Warning

Critical: The TranscodeResult.data returned by transcodeImageLevel() references WASM-managed memory and will become invalid after the next call to transcodeImageLevel().

If you need to persist the transcoded data, you must create a copy:

const result = ktx2Transcoder.transcodeImageLevel({
  format: TranscoderTextureFormat.cTFBC7_RGBA,
  level: 0
});

if (result) {
  // ❌ WRONG: This data will become invalid after next transcodeImageLevel() call
  const imageData = result.data;
  
  // ✅ CORRECT: Create a copy to persist the data
  const persistentData = new Uint8Array(result.data);
  // or
  const persistentData = result.data.slice();
}

Performance Optimization

Tip: Reuse the same KTX2Transcoder instance for multiple textures by calling init() multiple times. This reduces memory allocation overhead:

import { BasisUniversal, TranscoderTextureFormat } from '@h00w/basis-universal-transcoder';
import wasmUrl from '@h00w/basis-universal-transcoder/basis_capi_transcoder.wasm';

// Use the createWasmInstantiator helper function from the basic usage example
const transcoder = await BasisUniversal.getInstance(createWasmInstantiator(wasmUrl));
const ktx2Transcoder = transcoder.createKTX2Transcoder();

// Process multiple textures efficiently
async function processTextures(textureDataArray: Uint8Array[]) {
  for (const textureData of textureDataArray) {
    // Reuse the same transcoder instance
    if (ktx2Transcoder.init(textureData)) {
      ktx2Transcoder.startTranscoding();
      
      const result = ktx2Transcoder.transcodeImageLevel({
        format: TranscoderTextureFormat.cTFBC7_RGBA,
        level: 0
      });
      
      if (result) {
        // Create copy if you need to persist the data
        const persistentData = new Uint8Array(result.data);
        // Process the texture data...
      }
    }
  }
}

// Don't forget to dispose when done
ktx2Transcoder.dispose();

API Reference

Classes

BasisUniversal

Main class for managing the transcoder module.

  • static getInstance(instantiateWasmAsync: InstantiateWasmAsync): Promise<BasisUniversal>
  • createKTX2Transcoder(): KTX2Transcoder

KTX2Transcoder

Handles KTX2 file transcoding. Can be reused for multiple textures by calling init() multiple times for better performance.

  • init(data: Uint8Array): boolean - Initialize with KTX2 file data (can be called multiple times)
  • getBasisTextureFormat(): BasisTextureFormat - Get the basis texture format
  • startTranscoding(): boolean - Start transcoding (call after init)
  • transcodeImageLevel(options: TranscodeOptions): TranscodeResult | null - Transcode a specific level
  • dispose(): void - Clean up resources

Enums

TranscoderTextureFormat

Supported output texture formats:

  • cTFBC1_RGB - BC1 RGB (DXT1)
  • cTFBC3_RGBA - BC3 RGBA (DXT5)
  • cTFBC7_RGBA - BC7 RGBA
  • cTFASTC_4x4_RGBA - ASTC 4x4 RGBA
  • cTFPVRTC1_4_RGBA - PVRTC1 4bpp RGBA
  • cTFRGBA32 - 32-bit RGBA uncompressed
  • And more...

Type Definitions

type InstantiateWasmAsync = (imports: WebAssembly.Imports) => Promise<WebAssembly.WebAssemblyInstantiatedSource>;

Direct WASM Access

You can also directly import and use the WASM file for custom loading scenarios:

// Import WASM file directly
import wasmUrl from '@h00w/basis-universal-transcoder/basis_capi_transcoder.wasm';

// Custom WASM loading with your own instantiator
const basisUniversal = await BasisUniversal.getInstance(createWasmInstantiator(wasmUrl));

Utility Functions

  • detectBestFormat(gl: WebGLRenderingContext): TranscoderTextureFormat - Detect best format for WebGL context
  • isFormatSupported(format: TranscoderTextureFormat): boolean - Check if format is supported on current platform
  • getFormatName(format: TranscoderTextureFormat): string - Get human-readable format name

Development

Building from Source

# Install dependencies
npm install

# Build WASM module (requires Emscripten)
../../scripts/build-wasm.sh

# Build everything (WASM + package)
../../scripts/build-all.sh

# Start development server
npm run dev

Demo

The package includes a comprehensive demo that shows how to use the transcoder:

npm run dev

Then open your browser to see the interactive demo.

Browser Support

  • Modern browsers with WebAssembly support
  • Chrome 57+, Firefox 52+, Safari 11+, Edge 16+

Node.js Support

  • Node.js 16+ with WebAssembly support

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

Credits

This package is built on top of the excellent Basis Universal library by Binomial LLC.