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

@newkrok/nape-js

v3.42.3

Published

High-performance 2D physics engine for TypeScript & JavaScript — rigid bodies, constraints, fluid simulation, raycasting, and deterministic multiplayer. Tree-shakeable, zero dependencies.

Readme

@newkrok/nape-js

npm version npm downloads jsDelivr hits CI codecov bundle size license docs sponsor

Fully typed 2D physics engine — a modern TypeScript rewrite of the Nape Haxe physics engine.

This is the actively maintained successor to the original Nape. The Haxe engine saw its last release in 2016 and its last commit that same year (the community's Haxe 4 fork, nape-haxe4, last shipped in 2020); nape-js carries the same simulation model forward as pure TypeScript — no dependencies, no build step, works in Node.js and every browser.

Homepage & Interactive Demos | API Reference | Examples | Games built on nape-js | Guides | Multiplayer Demo

Cookbook | Troubleshooting | Anti-Patterns

  • Originally created in Haxe by Luca Deltodesco
  • Ported to TypeScript by Istvan Krisztian Somoracz

Installation

npm install @newkrok/nape-js
# optional: PixiJS v8 integration
npm install @newkrok/nape-pixi pixi.js

Quick Start

import { Space, Body, BodyType, Vec2, Circle, Polygon } from "@newkrok/nape-js";

// Create a physics world with downward gravity
const space = new Space(new Vec2(0, 600));

// Static floor
const floor = new Body(BodyType.STATIC, new Vec2(400, 550));
floor.shapes.add(new Polygon(Polygon.box(800, 20)));
floor.space = space;

// Dynamic box
const box = new Body(BodyType.DYNAMIC, new Vec2(400, 100));
box.shapes.add(new Polygon(Polygon.box(40, 40)));
box.space = space;

// Dynamic circle
const ball = new Body(BodyType.DYNAMIC, new Vec2(420, 50));
ball.shapes.add(new Circle(20));
ball.space = space;

// Game loop
function update() {
  space.step(1 / 60);

  for (const body of space.bodies) {
    console.log(`x=${body.position.x.toFixed(1)} y=${body.position.y.toFixed(1)}`);
  }
}

API Reference

Full API documentation: TypeDoc Reference

Core Classes

| Class | Description | | ------- | ----------------------------------------------------------------------------------------- | | Space | Physics world — add bodies, step simulation, deterministic mode for rollback/prediction | | Body | Rigid body with position, velocity, mass | | Vec2 | 2D vector — pooling, clone(), equals(), lerp(), fromAngle() | | Vec3 | 3D vector for constraint impulses — clone(), equals() | | AABB | Axis-aligned bounding box — clone(), equals(), fromPoints() | | Mat23 | 2×3 affine matrix — clone(), equals(), transform, inverse | | Ray | Raycasting — clone(), fromSegment(), spatial queries |

Shapes

| Class | Description | | --------- | ---------------------------------------------------------------------------- | | Circle | Circular shape | | Polygon | Convex polygon (with Polygon.box(), Polygon.rect(), Polygon.regular()) | | Capsule | Capsule shape (Capsule.create(), Capsule.createVertical()) | | Shape | Base class with material, filter, sensor support |

Physics Properties

| Class | Description | | ------------------- | ----------------------------------------- | | Material | Elasticity, friction, density | | BodyType | STATIC, DYNAMIC, KINEMATIC | | InteractionFilter | Bit-mask collision/sensor/fluid filtering | | FluidProperties | Density, viscosity for fluid shapes |

Constraints

| Class | Description | | --------------- | ---------------------------------- | | PivotJoint | Pin two bodies at a shared point | | DistanceJoint | Constrain distance between anchors | | WeldJoint | Fix relative position and angle | | AngleJoint | Constrain relative angle | | MotorJoint | Apply angular velocity | | LineJoint | Slide along a line | | PulleyJoint | Constrain combined distances |

Callbacks

| Class | Description | | --------------------- | --------------------------------------------------- | | InteractionListener | Collision/sensor/fluid events | | BodyListener | Body wake/sleep events | | ConstraintListener | Constraint events | | PreListener | Pre-collision filtering | | CbType | Tag interactors for filtering | | CbEvent | BEGIN, ONGOING, END, WAKE, SLEEP, BREAK |

Utilities

| Class | Description | | ------------- | ---------------------------------------------------------------------- | | BodyList, ShapeList, … | Typed engine lists — for...of, at(), add(), remove(), push(), pop() | | MatMN | Variable-sized M×N matrix — clone(), equals(), multiply, transpose | | VERSION | Engine version string; also queryable from the console as __NAPE_JS__ after any import (three.js-style) |

Helpers

Higher-level building blocks layered on top of the engine — opt-in modules.

| Helper | Description | | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | CharacterController | Velocity-based 2D platformer controller — ground / slope / wall raycasts, coyote-time, one-way platforms, moving-platform inheritance, runtime-mutable down for radial-gravity worlds, frictionless walls by default (wallFriction) so a held direction key never pins the character to a wall | | RadialGravityField / RadialGravityFieldGroup | Point-source gravity well — inverse-square / inverse / constant / custom falloff, maxRadius / softening, body filter, mass scaling. Replaces hand-rolled body.force = ... loops | | ParticleEmitter / ParticleEmitterGroup | Physics-aware particle emitter — pooled bodies, continuous / periodic / manual spawning, configurable spawn / velocity patterns, deterministic RNG, lifecycle hooks (onSpawn / onUpdate / onDeath / onCollide), self-excluding filter generation | | buildTilemapBody / meshTilemap | Greedy-meshed collision body from a 2D tile grid. 5–50× fewer shapes than one-polygon-per-cell. Includes tiledLayerToGrid and ldtkLayerToGrid parsers | | TriggerZone | Sensor zone with onEnter / onStay / onExit callbacks — wraps the BEGIN/ONGOING/END InteractionListener plumbing | | fractureBody | Voronoi-based polygon shatter — fragmentCount, explosionImpulse, deterministic via random | | createConcaveBody | Decompose a concave outline into convex polygons and pack them into a single body |

Serialization

Full physics state snapshot/restore — suitable for save/load, replay, and multiplayer server↔client synchronization.

import "@newkrok/nape-js";
import { spaceToJSON, spaceFromJSON } from "@newkrok/nape-js/serialization";

// Serialize
const snapshot = spaceToJSON(space);
const json = JSON.stringify(snapshot);

// Restore (e.g. on another machine / after network transfer)
const restored = spaceFromJSON(JSON.parse(json));
restored.step(1 / 60);

The /serialization entry point is a separate export, but it still loads the engine (measured ~145 KB gzip bundled on its own) — the snapshot format is defined in terms of engine types. The snapshot captures bodies, shapes, materials, interaction filters, fluid properties, all constraint types (except UserConstraint), and compounds. Arbiters and broadphase tree state are reconstructed automatically on the first step.

Replay (Recorder + Player)

Record a deterministic simulation and play it back later — same machine, another machine, days later. Built on top of /serialization plus space.deterministic = true.

import "@newkrok/nape-js";
import { Recorder, Player, encodeReplay, decodeReplay } from "@newkrok/nape-js/replay";

// Record — user supplies the input payload type and the apply function.
space.deterministic = true;
const recorder = new Recorder<MyInput>(space, { keyframeEvery: 60 });
for (let f = 0; f < 600; f++) {
  recorder.recordFrame(readUserInput()); // null = no input this frame
  applyUserInput(space); // user's own logic
  space.step(1 / 60);
}
const replay = recorder.finish();
const blob = encodeReplay(replay); // Uint8Array — store, share, transfer

// Replay — anywhere, including a different machine on the same platform.
const replay2 = decodeReplay<MyInput>(blob);
const player = new Player(replay2, (input, space, frame) => {
  if (input.fire) somebody.applyImpulse(new Vec2(0, -200));
});
const sp = player.restore();
while (!player.finished) player.step();
// player.stepTo(150) — random-access scrub via keyframes

The /replay entry point is a separate export but still loads the engine (measured ~145 KB gzip bundled on its own). The library is intentionally a thin layer: it owns the snapshot + input-log plumbing, and the user owns the applyInput callback. This keeps the replay deterministic as long as the callback is a pure function of (input, space, frame).

Web Worker

Run physics off the main thread for smooth rendering even with hundreds of bodies.

import "@newkrok/nape-js";
import { PhysicsWorkerManager } from "@newkrok/nape-js/worker";

const mgr = new PhysicsWorkerManager({ gravityY: 600, maxBodies: 256 });
await mgr.init();

const id = mgr.addBody("dynamic", 100, 50, [{ type: "circle", radius: 20 }]);
mgr.start();

// Read transforms on the main thread (zero-copy with SharedArrayBuffer)
function render() {
  const t = mgr.getTransform(id);
  if (t) drawCircle(t.x, t.y, t.rotation);
  requestAnimationFrame(render);
}
render();

Uses SharedArrayBuffer for zero-copy transform sharing when COOP/COEP headers are present, with automatic postMessage fallback otherwise.

Contributing

Pull requests are welcome! Every PR is reviewed collaboratively with Claude Code using the playbook in .claude/skills/pr-review/SKILL.md. See CONTRIBUTING.md for the full review process, pre-push checklist, and what gets flagged as a blocker vs. a nit.

Support

nape-js is developed and maintained in my own time. If it saves you some of yours, you can support the work:

Starring the repo and reporting bugs help just as much.

License

MIT