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

mana-engine

v0.1.1

Published

A small, fast 3D engine for the web: one scene graph, two backends (WebGPU + WebGL2), Z-up, shader hooks, and an optional React renderer.

Readme

Mana Engine

A small, high-performance 3D engine for the web that renders the same scene on WebGPU or WebGL2, with an optional React renderer. If you've used Three.js, React Three Fiber, or Drei, you already know the API — Mana Engine is a deliberate subset of all three, trimmed down to what a real-time game actually uses, and written in TypeScript with a right-handed Z-up world.

It was purpose-built for Mana Blade, a low-poly multiplayer online RPG, so everything in it is tested in a shipped game.

import {
  BoxGeometry,
  DirectionalLight,
  Mesh,
  MeshLambertMaterial,
  PerspectiveCamera,
  Scene,
  WebGPURenderer,
} from 'mana-engine'

const scene = new Scene()
const camera = new PerspectiveCamera(50, width / height, 0.1, 100)
camera.position.set(0, -6, 4) // Z-up: pull back along -Y, up along +Z
camera.lookAt(0, 0, 0)

const light = new DirectionalLight(0xffffff, 1)
light.position.set(4, -5, 6)
light.castShadow = true
scene.add(light)

scene.add(new Mesh(new BoxGeometry(1, 1, 1), new MeshLambertMaterial({ color: 0x88ccff })))

const renderer = new WebGPURenderer({ canvas, antialias: true })
await renderer.init()
renderer.shadowMap.enabled = true
renderer.setSize(width, height)
renderer.render(scene, camera)

Or the same thing in React:

import { useRef } from 'react'
import { Canvas, useFrame } from 'mana-engine/react'

function Spinner() {
  const ref = useRef()
  useFrame((_, dt) => (ref.current.rotation.z += dt))
  return (
    <mesh ref={ref} castShadow>
      <boxGeometry args={[1, 1, 1]} />
      <meshLambertMaterial color="skyblue" />
    </mesh>
  )
}

export default () => (
  <Canvas shadows camera={{ position: [0, -6, 4] }}>
    <ambientLight intensity={0.5} />
    <directionalLight position={[4, -5, 6]} castShadow />
    <Spinner />
  </Canvas>
)

Features

  • One scene graph, two backends. WebGPU and WebGL2 render identical output from the same scene. <Canvas> picks WebGPU when available and falls back to WebGL2 — including at runtime if the GPU device is ever lost mid-session.
  • Custom shaders in plain GLSL and WGSL. Materials accept shader hooks: hand-written snippets injected at fixed points in the built-in shaders (vertex displacement, albedo, lighting, post-lighting tints, billboard placement), with typed uniforms, attributes, and varyings. No shader graph, no compiler — what you write is what runs.
  • Characters and effects. GPU skinning, InstancedMesh, and billboard sprites with per-instance attributes for stateless GPU particle systems.
  • Directional shadows. Soft PCF shadows that automatically get cheaper in the distance, an optional per-vertex mode for dense foliage, and a freeze switch so a static zone-wide shadow map renders once and gets reused.
  • Baked-lighting friendly. A pluggable indirect-lighting hook and 3D textures make it easy to feed in baked GI or irradiance volumes; ambient occlusion and wrap (half-lambert) lighting are built into the lambert material.
  • Smooth loading. Shaders compile in parallel on both backends (async pipelines on WebGPU, KHR_parallel_shader_compile on WebGL2) and never stall a frame; renderer.isCompiling() lets you hold a loading screen until everything is warm.
  • Compact assets. GLTFLoader decodes Draco-compressed GLBs through pure-TypeScript minidraco — no WASM files to host — and gputex compresses textures to BC/ASTC/ETC2 on the GPU at load time.
  • A React renderer you already know. Canvas, JSX intrinsics for every object, useFrame with priorities, useThree, Suspense-based useLoader/useGLTF, pointer events with bubbling, an Html overlay, and familiar components like OrbitControls (touch included), Stats, useProgress, and PerformanceMonitor.
  • Fast raycasting. A built-in triangle BVH (MeshBVH) accelerates Raycaster against large meshes like terrain.
  • In-shader color grading. Photoshop-style levels (blackPoint/whitePoint) applied at the end of every fragment shader — free of any post-processing pass.

Installation

npm install mana-engine   # or: bun add mana-engine

React (and react-dom for the Html overlay) are optional peer dependencies — install them only if you use mana-engine/react.

Entry points

| Import | What you get | | ------------------------------ | ------------------------------------------------------------------ | | mana-engine | The engine: math, scene graph, materials, loaders, both renderers. | | mana-engine/react | The React renderer: Canvas, hooks, events, components. | | mana-engine/shader-templates | Low-level shader template builders, for external shader tooling. |

Writing a custom shader

A material's hooks object carries paired snippets — GLSL for WebGL2, WGSL for WebGPU. Provide one language to support one backend, both to support both:

import { MeshBasicMaterial, type ShaderHooks } from 'mana-engine'

const hooks: ShaderHooks = {
  key: 'stripes',
  uniforms: { uSpeed: { type: 'float', value: 1 } },
  colorGLSL: `
  float wave = sin(vUv.x * 10.0 + time * uSpeed);
  mana_color = vec4(mix(vec3(0.0), vec3(1.0, 0.5, 0.0), wave), opacity);`,
  colorWGSL: `
  let wave = sin(vUv.x * 10.0 + time * mana_u.uSpeed);
  mana_color = vec4f(mix(vec3f(0.0), vec3f(1.0, 0.5, 0.0), wave), mana_obj.opacity);`,
}

const material = new MeshBasicMaterial()
material.hooks = hooks
hooks.uniforms!.uSpeed!.value = 2 // uniforms are live — mutate .value to animate

Snippets see a stable set of names that works identically in both languages (mana_position, mana_color, time, cameraPosition, …), and everything the engine injects is prefixed mana_ so it never collides with your code. Hooks with the same key share compiled programs. The full contract is documented in src/renderer/hooks-contract.ts.

Loading assets

import { useGLTF, useGputex } from 'mana-engine/react'

const { scene, animations } = useGLTF('/models/hero.glb') // Draco-decoded via minidraco
const albedo = useGputex('/textures/atlas.png', { hint: 'color', mipmaps: true }) // GPU-compressed via gputex

The imperative equivalents (loadGLTF, GLTFLoader, GputexLoader) live in the core entry.

What's in the box

  • MathVector2/3/4, Matrix3/4, Quaternion, Euler, Color, Box3, Sphere, Plane, Ray, Frustum, Triangle, MathUtils, and the interpolant family.
  • Scene graph & objectsObject3D, Group, Mesh, SkinnedMesh, Bone, Skeleton, Sprite, InstancedMesh, lines, Scene, Fog, Raycaster, Clock, Layers.
  • Cameras & lightsPerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight with shadows.
  • MaterialsMeshBasicMaterial (unlit), MeshLambertMaterial, SpriteMaterial, LineBasicMaterial — all hookable, with additive / subtractive / multiply / overlay blending.
  • Geometries — box, sphere, capsule, cone, cylinder, plane, circle, ring, torus, icosahedron, octahedron, polyhedron, shape (with the full curve family and earcut triangulation). Standard names are Y-up like Three.js; *ZUp variants (ConeGeometryZUp, …) stand upright in a Z-up world with no rotation.
  • TexturesTexture, DataTexture, Data3DTexture, CompressedTexture.
  • AnimationAnimationMixer, clips, actions, and typed keyframe tracks, fed by the GLTF loader.
  • ExtrasSkeletonUtils for cloning rigs, geometry merge/weld utilities (mergeGeometries, mergeVertices, toCreasedNormals), and the BVH toolkit.

Conventions

  • Z-up, right-handed, no surprises. Object3D.DEFAULT_UP is (0, 0, 1) and cameras, controls, shadows, and loaders all agree — GLB data authored Z-up passes through untouched.
  • Performance is a feature. Hot paths avoid allocation, uniforms only upload when they change, and draw calls are sorted for state coherence. renderer.info reports draw stats, and opt-in GPU timestamp queries measure real GPU time.
  • Both backends or nothing. Every feature ships on WebGPU and WebGL2 together, with matched output — including workarounds for real-world mobile driver bugs.

What it doesn't do

Mana Engine is deliberately small. There is no postprocessing stack, no point/spot lights, no environment maps or PMREM, no morph targets, and no WebXR. If a stylized real-time game doesn't need it, it's probably not here — features land when a real use case demands them, and always on both backends at once.

Development

bun install
bun run typecheck
bun test

test-pages/ contains standalone scenes exercising the engine end to end, and scripts/ has headless render-regression drivers. The package's exports point at the TypeScript source directly; publishing builds a bundled dist/ (bun run build).

Mana Engine currently lives in Mana Blade's private repository. I will sync this repo and publish NPM versions for external use every now and then, but please keep in mind that my first priority is the game itself. The public version might not always be fully up-to-date, and I will likely reject proposed changes that are not relevant for Mana Blade.

License

MIT. The math, scene-graph, geometry, and animation classes are re-authored to match the Three.js r185 API shape; the React layer is modeled on React Three Fiber and Drei; polygon triangulation is a verbatim port of Mapbox's earcut (ISC). Full attribution in LICENSE-THIRD-PARTY.md.