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

@thai-kit/level-runtime

v0.2.5

Published

Loads a level baked by the thaikit level editor into a three.js game: cells with LOD, lights and lightmap, physics colliders, per-cell BVH. Headless entry for servers.

Readme

@thai-kit/level-runtime

Load a baked thaikit level into a three.js game. One GLB is the whole map — geometry, LOD tiers, lightmaps, sky, lights, colliders and spawn points — and loadLevel() is the only call you make.

npm i @thai-kit/level-runtime three
import * as THREE from 'three';
import { loadLevel } from '@thai-kit/level-runtime';

const level = await loadLevel('/levels/soi.glb', {
  scene, renderer, camera,
  transcoderPath: '/basis/',      // where you serve basis_transcoder.{js,wasm}
});

const spawn = level.spawns.pick();
camera.position.fromArray(spawn.position);

renderer.setAnimationLoop(() => {
  level.update(clock.getDelta(), camera.position);
  renderer.render(scene, camera);
});

level.update() steps physics, switches LOD tiers by distance to each cell's box, keeps the moon's shadow frustum snapped around the camera, re-faces billboards and drifts the sky. loadLevel adds the level root to your scene itself, builds the sky as domes, and sets scene.environment from a PMREM probe prefiltered off the sky's own shader.

What you must not do

The level arrives already lit. Do not add an ambient or hemisphere light, and do not add a sun — the bake put sky light and bounce in the lightmap, and the manifest's directional light comes back with its shadow settings intact. Adding yours on top double-counts the bake. Set camera.far to at least 2000; the sky domes sit at radius 1200. And as with three itself: two copies of three and nothing draws.

Full integration guide, including migrating a game that loads one GLB per prop: docs/using-a-baked-level.md.

Physics is your choice

Colliders ship as plain data — boxes, cylinders, spheres and capsules in metres — so no engine is baked in. A Rapier adapter is included (@thai-kit/level-runtime/physics/rapier), or implement four methods on PhysicsAdapter, or use none: level.raycast(ray) is a three-mesh-bvh query against real lod0 triangles and works regardless.

Ladders are data too. A placement tagged ladder in the editor (or labelled ladder_* in Unreal) ships its collider entry with tags: ['ladder'], and level.colliders.ladders lists them as { placement, shapes, tags, bounds }: the shapes are still part of the static solid (a ladder is stood on as well as climbed) and bounds is a world AABB around them grown by 0.35 m of reach, so "the player is at a ladder" is a box test. Test the box against the CAPSULE, not its axis alone: a player pressed against the rungs has their axis one player radius plus the controller's skin off the face, which is just outside a box padded by reach only -- grow it by the radius in XZ as well, or pass a larger reach to ladderVolumes(manifest, { reach }). The runtime does not move the player; your controller decides what a grab, a climb and a boost are. A level baked before tags were carried lists none.

Headless

@thai-kit/level-runtime/node never imports three. A game server reads the same GLB and builds the same bodies from the same manifest:

import { loadLevelHeadless } from '@thai-kit/level-runtime/node';
const level = await loadLevelHeadless('./level.glb', { physics });

This is why three is an optional peer dependency: the browser entries all require it, the /node entry genuinely does not.

MIT.

Levels built in Unreal

A level laid out in Unreal Editor loads through the same loadLevel() with no option and no second code path. The route is: export the Unreal level with Unreal's own glTF Exporter (metres, lights, cameras, baked materials), run npm run level:import-unreal -- --level <id> to turn that file into the bake pipeline's raw scene, and bake it with npm run level:bake -- --level <id> --baker unreal (adopting Unreal 5.6+'s exported lightmaps) or --baker blender (re-lighting the same geometry and lamps in Cycles). The result is an ordinary baked level: cells, LOD tiers, one KTX2 lightmap, colliders rebuilt from the thaikit compounds wherever a SM_TK_* Static Mesh was placed, spawns from the spawn_* cameras. manifest.source says it came from Unreal and which lightmap it carries; everything else is identical, which is the point. The whole procedure is docs/unreal-level-export.md in the thaikit repo.

Scenery without baked lighting

Schema-3 levels can contain cells marked bakeLighting: false. These retain spatial LOD switching but receive no lightmap binding and cast no shadows. Their authored live or unlit materials remain active. Distant tree billboards are individual nodes that face the camera during level.update().

The source runtime supports manifest schemas 1, 2 and 3. A game loading a new schema-3 level must use a runtime release containing this support; previously published runtimes may reject that level.