TypeScript icon, indicating that this package has built-in type declarations

1.2.3 • Public • Published


p2-es is a 2D rigid body physics engine written in JavaScript. Features include collision detection, contacts, friction, restitution, motors, springs, advanced constraints and various shape types.

Demos | Examples | Documentation

This is a maintained fork of p2.js, originally created by Stefan Hedman @schteppe. It is a type-safe flatbundle (esm and cjs) which allows for tree shaking and usage in modern environments.

If you're using three.js in a React environment with react-three-fiber, check out use-p2! It's a wrapper around p2-es that runs in a web worker.

Getting Started


npm install p2-es
yarn add p2-es


You can also import the esm bundle with unpkg:

<script type="module">
    // import a specific version
    import * as p2 from 'https://www.unpkg.com/p2-es@1.1.6/dist/p2-es.js'

    // or import latest
    import * as p2 from 'https://www.unpkg.com/p2-es/dist/p2-es.js'

If you would like to use ordinary Array instead of Float32Array, define P2_ARRAY_TYPE globally before loading the library.

<script type="text/javascript">
    P2_ARRAY_TYPE = Array
<script type="module">
    import * as p2 from 'p2-es.js'

Sample code

The following example uses the World, Circle, Body and Plane classes to set up a simple physics scene with a ball on a plane.

import * as p2 from 'p2-es'

// Create a physics world, where bodies and constraints live
const world = new p2.World({
    gravity: [0, -9.82],

// Create an empty dynamic body
const circleBody = new p2.Body({
    mass: 5,
    position: [0, 10],

// Add a circle shape to the body
const circleShape = new p2.Circle({ radius: 1 })

// ...and add the body to the world.
// If we don't add it to the world, it won't be simulated.

// Create an infinite ground plane body
const groundBody = new p2.Body({
    mass: 0, // Setting mass to 0 makes it static
const groundShape = new p2.Plane()

// To animate the bodies, we must step the world forward in time, using a fixed time step size.
// The World will run substeps and interpolate automatically for us, to get smooth animation.
const fixedTimeStep = 1 / 60 // seconds
const maxSubSteps = 10 // Max sub steps to catch up with the wall clock
let lastTime = 0

// Animation loop
function animate(time) {

    // Compute elapsed time since last render frame
    const deltaTime = (time - lastTime) / 1000

    // Move bodies forward in time
    world.step(fixedTimeStep, deltaTime, maxSubSteps)

    // Render the circle at the current interpolated position

    lastTime = time

// Start the animation loop

To interact with bodies, you need to do it after each internal step. Simply attach a "postStep" listener to the world, and make sure to use body.position here - body.interpolatedPosition is only for rendering.

world.on('postStep', function (event) {
    // Add horizontal spring force
    circleBody.force[0] -= 100 * circleBody.position[0]

Supported collision pairs

Circle Plane Box Convex Particle Line Capsule Heightfield Ray
Circle Yes - - - - - - - -
Plane Yes - - - - - - - -
Box Yes Yes Yes - - - - - -
Convex Yes Yes Yes Yes - - - - -
Particle Yes Yes Yes Yes - - - - -
Line Yes Yes (todo) (todo) - - - - -
Capsule Yes Yes Yes Yes Yes (todo) Yes - -
Heightfield Yes - Yes Yes (todo) (todo) (todo) - -
Ray Yes Yes Yes Yes - Yes Yes Yes -

Note that concave polygon shapes can be created using Body.fromPolygon.


Make sure you have git, Node.js v14+ and Yarn installed

git clone https://github.com/pmndrs/p2-es.git && cd p2-es

# install deps

# build p2-es
(cd packages/p2-es && yarn build)

# build the website
yarn build

npx http-server apps/p2-es-website/dist

Package Sidebar


npm i p2-es

Weekly Downloads






Unpacked Size

675 kB

Total Files


Last publish


  • drcmda
  • joergjaeckel
  • isaacmason