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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@series-inc/stowkit-three-loader

v0.1.40

Published

Three.js loader for StowKit asset packs

Readme

@series-inc/stowkit-three-loader

Three.js loader for StowKit asset packs. Provides a simple, high-level API for loading meshes, skinned meshes, animations, textures, and audio from .stow files.

Installation

npm install @series-inc/stowkit-three-loader three

Quick Start

import { StowKitLoader, AssetType } from '@series-inc/stowkit-three-loader';

const pack = await StowKitLoader.load('assets.stow');

const mesh = await pack.loadMesh('character');
scene.add(mesh);

const character = await pack.loadSkinnedMesh('player');
scene.add(character);

const { mixer } = await pack.loadAnimation(character, 'walk');

function animate() {
    const delta = clock.getDelta();
    mixer.update(delta);
    renderer.render(scene, camera);
}

Features

  • Static Meshes - Draco-compressed meshes with automatic material/texture loading
  • Skinned Meshes - Skeletal meshes with bone hierarchy
  • Animations - Skeletal animations with automatic mixer setup
  • Textures - KTX2/Basis Universal GPU-compressed textures
  • Audio - OGG/MP3 audio with Three.js Audio integration
  • Multiple Packs - Load multiple .stow files simultaneously with isolated state
  • WASM Parsing - All binary parsing done in WASM for performance
  • Type Safe - Full TypeScript support
  • Zero Config - Works out of the box

API Reference

Exports

export { StowKitLoader } from '@series-inc/stowkit-three-loader';
export { StowKitPack } from '@series-inc/stowkit-three-loader';
export { AssetMemoryCache } from '@series-inc/stowkit-three-loader';
export { AssetType, PerfLogger } from '@series-inc/stowkit-three-loader';

export type { StowKitLoaderOptions } from '@series-inc/stowkit-three-loader';
export type { MeshGeometryInfo, MaterialPropertyValue, MaterialData, Node, MeshMetadata } from '@series-inc/stowkit-three-loader';

StowKitLoader

Static loader class for opening .stow pack files.

StowKitLoader.load(url, options?)

Loads a .stow pack from a URL.

const pack = await StowKitLoader.load('assets.stow', {
    wasmPath: '/stowkit/stowkit_reader.wasm',
    basisPath: '/stowkit/basis/',
    dracoPath: '/stowkit/draco/'
});

Options:

  • wasmPath (string) - Path to WASM reader module (default: '/stowkit/stowkit_reader.wasm')
  • basisPath (string) - Path to Basis Universal transcoder files (default: '/stowkit/basis/')
  • dracoPath (string) - Path to Draco decoder files (default: '/stowkit/draco/')

Returns: Promise<StowKitPack>

StowKitLoader.loadFromMemory(data, options?, cacheKey?)

Loads a .stow pack from memory.

const response = await fetch('assets.stow');
const buffer = await response.arrayBuffer();
const pack = await StowKitLoader.loadFromMemory(buffer);

// With a cache key for CDN-loaded files
const pack = await StowKitLoader.loadFromMemory(buffer, undefined, 'https://cdn.example.com/assets.stow');

Parameters:

  • data (ArrayBuffer | Blob | File) - The .stow file data
  • options (StowKitLoaderOptions, optional) - Loader options
  • cacheKey (string, optional) - Unique identifier for caching (recommended for CDN-loaded files)

Returns: Promise<StowKitPack>

StowKitPack

Represents an opened .stow pack with methods to load assets.

Asset Manifest

pack.listAssets(): AssetListItem[]

Get the complete manifest of all assets in the pack.

const pack = await StowKitLoader.load('assets.stow');
const manifest = pack.listAssets();

console.log(`Pack contains ${manifest.length} assets`);

manifest.forEach(asset => {
    console.log(`[${asset.index}] ${asset.name || asset.id}`);
    console.log(`  Type: ${asset.type}`);
    console.log(`  Size: ${asset.dataSize} bytes`);
    console.log(`  Has Metadata: ${asset.hasMetadata}`);
});

const meshes = manifest.filter(a => a.type === AssetType.STATIC_MESH);
const textures = manifest.filter(a => a.type === AssetType.TEXTURE_2D);
const animations = manifest.filter(a => a.type === AssetType.ANIMATION_CLIP);

pack.getAssetCount(): number

Get total number of assets in the pack.

pack.getAssetInfo(index: number): AssetInfo | null

Get detailed info about a specific asset.

pack.readAssetData(index: number): Uint8Array | null

Read raw binary data of an asset by index.

pack.readAssetMetadata(index: number): Uint8Array | null

Read raw metadata bytes of an asset by index.

Loading Assets

Static Meshes

// Load by string ID
const mesh = await pack.loadMesh('models/building.mesh');
scene.add(mesh);

// Load by index
const mesh = await pack.loadMeshByIndex(5);
scene.add(mesh);

Returns a THREE.Group containing the mesh hierarchy with materials and textures applied.

Skinned Meshes

// Load by string ID
const character = await pack.loadSkinnedMesh('characters/player.skinned');
scene.add(character);

// Load by index
const character = await pack.loadSkinnedMeshByIndex(8);
scene.add(character);

Returns a THREE.Group containing the skinned mesh with skeleton and bones in bind pose.

Animations

// Load and play animation (returns mixer, action, clip)
const { mixer, action, clip } = await pack.loadAnimation(
    skinnedMeshGroup,
    'animations/walk.anim'
);

// Or by index
const { mixer, action, clip } = await pack.loadAnimationByIndex(
    skinnedMeshGroup,
    9
);

// Update in your animation loop
const clock = new THREE.Clock();
function animate() {
    requestAnimationFrame(animate);
    mixer.update(clock.getDelta());
    renderer.render(scene, camera);
}

The loadAnimation methods automatically:

  • Create an AnimationMixer on the correct root object
  • Set the animation to loop infinitely
  • Start playing immediately
  • Retarget bone names if they don't match exactly
  • Return the mixer, action, and clip

Animation Clips (Without Playback)

// Load just the clip by string ID
const clip = await pack.loadAnimationClip('animations/walk.anim');

// Or by index
const clip = await pack.loadAnimationClipByIndex(9);

Returns a THREE.AnimationClip without creating a mixer or starting playback.

Textures

// Load by string ID
const texture = await pack.loadTexture('textures/wood.ktx2');
material.map = texture;

// Load by index
const texture = await pack.loadTextureByIndex(2);

Returns a THREE.CompressedTexture (KTX2/Basis Universal format). Textures are cached per-pack so loading the same texture twice returns the same instance.

Audio

const listener = new THREE.AudioListener();
camera.add(listener);

// Load by string ID
const bgm = await pack.loadAudio('sounds/music.ogg', listener);
bgm.setLoop(true);
bgm.play();

// Or create HTML5 audio element for preview
const audioElement = await pack.createAudioPreview(3);
document.body.appendChild(audioElement);

Metadata Helpers

All metadata is parsed in WASM for performance and reliability.

Animation Metadata

const animData = pack.getAnimationMetadata(index);

console.log(animData.stringId);        // "Clip_Walking"
console.log(animData.targetMeshId);    // "Character_Skinned_Tpose"
console.log(animData.duration);        // 0.97
console.log(animData.ticksPerSecond);  // 30
console.log(animData.channelCount);    // 104
console.log(animData.boneCount);       // 65

Audio Metadata

const audioData = pack.getAudioMetadata('sounds/bgm.ogg');

console.log(audioData.sampleRate);  // 44100
console.log(audioData.channels);    // 2 (stereo)
console.log(audioData.durationMs);  // 180000 (3 minutes)

Texture Metadata

const texData = pack.getTextureMetadata(index);

console.log(texData.width);          // 1024
console.log(texData.height);         // 1024
console.log(texData.channels);       // 3 (RGB) or 4 (RGBA)
console.log(texData.channelFormat);  // 1 (RGB) or 2 (RGBA)

Cleanup

pack.dispose()

Close the pack and free resources. Clears the texture cache and closes the underlying WASM reader.

pack.dispose();

Complete Example

import * as THREE from 'three';
import { StowKitLoader } from '@series-inc/stowkit-three-loader';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
const clock = new THREE.Clock();

const listener = new THREE.AudioListener();
camera.add(listener);

const pack = await StowKitLoader.load('game.stow');

const environment = await pack.loadMesh('levels/level1.mesh');
scene.add(environment);

const character = await pack.loadSkinnedMesh('characters/player.skinned');
character.position.set(0, 0, 0);
scene.add(character);

const { mixer } = await pack.loadAnimation(character, 'animations/idle.anim');

const bgm = await pack.loadAudio('sounds/theme.ogg', listener);
bgm.setLoop(true);
bgm.play();

function animate() {
    requestAnimationFrame(animate);
    mixer.update(clock.getDelta());
    renderer.render(scene, camera);
}

animate();

Loading Multiple Packs

Each pack is fully isolated with its own WASM reader instance. You can load multiple packs simultaneously without interference:

const [environmentPack, characterPack, audioPack] = await Promise.all([
    StowKitLoader.load('environment.stow'),
    StowKitLoader.load('characters.stow'),
    StowKitLoader.load('audio.stow')
]);

const level = await environmentPack.loadMesh('level1');
const player = await characterPack.loadSkinnedMesh('player');
const bgm = await audioPack.loadAudio('theme', listener);

scene.add(level);
scene.add(player);
bgm.play();

console.log(`Environment: ${environmentPack.getAssetCount()} assets`);
console.log(`Characters: ${characterPack.getAssetCount()} assets`);
console.log(`Audio: ${audioPack.getAssetCount()} assets`);

Dispose packs when no longer needed:

environmentPack.dispose();

Asset Types

import { AssetType } from '@series-inc/stowkit-three-loader';

| Enum | Value | Description | |------|-------|-------------| | AssetType.STATIC_MESH | 1 | Draco-compressed 3D models | | AssetType.TEXTURE_2D | 2 | KTX2/Basis Universal textures | | AssetType.AUDIO | 3 | OGG/MP3 audio files | | AssetType.MATERIAL_SCHEMA | 4 | Material template definitions | | AssetType.SKINNED_MESH | 5 | Skeletal meshes with bones | | AssetType.ANIMATION_CLIP | 6 | Bone animation keyframes |

Public Folder Setup

The package automatically copies required files on install:

public/
└── stowkit/
    ├── stowkit_reader.wasm
    ├── basis/
    │   ├── basis_transcoder.js
    │   └── basis_transcoder.wasm
    └── draco/
        ├── draco_decoder.js
        ├── draco_decoder.wasm
        └── draco_wasm_wrapper.js

Just run npm install and everything is set up automatically.

Performance

  • WASM Parsing: All binary parsing done in WASM (10-50x faster than JavaScript)
  • Draco Compression: Meshes are 80-90% smaller than uncompressed
  • KTX2 Textures: GPU-native compression, fast loading and rendering
  • Lazy Loading: Assets loaded on-demand, not all at once
  • Memory Efficient: Minimal copying between WASM and JavaScript
  • Texture Caching: Duplicate texture loads return cached promises

Troubleshooting

Animations appear broken/offset from origin

Make sure your .stow file was packed with the latest packer that writes bone parent indices correctly.

Textures not loading

Ensure /stowkit/basis/ folder contains the Basis Universal transcoder files (auto-copied on install).

Audio not playing

Make sure you've created an AudioListener and attached it to your camera.