PhysicsJS

Basic Usage

Hello, world - Setting the stage

The foundation for all physics simulations is a world object. You must create a world, add objects and behaviors, and then advance the simulation.

There are several ways to create a world object, all of which involve calling Physics as a function. The following three worlds are equivalent:

var world = Physics();
Physics(function(world){
    // use "world"
});
Physics(function(){
    var world = this;
    // use "world"
});

The return value, the function argument, and the “this” variable all refer to the same world. The benefit of using the last two methods is that it encourages a coding style that does not polute the global scope. Many worlds can be created this way which will be properly scoped and modular.

Example:

var ballSim = function(world){
    // create ball simulation...
};

var chainSim = function(world){
    // create chains...  
};

var init = function(){
    var ballWorld = Physics(ballSim);
    var chainWorld = Physics(chainSim);
    //...
};

Configure your world

Several configuration options are settable when constructing a world. To set any options, pass in a configuration object as the first argument to Physics.

Example:

Physics({
    // set the timestep
    timestep: 1000.0 / 160,
    // maximum number of iterations per step
    maxIPF: 16,
    // set the integrator (may also be set with world.add())
    integrator: 'verlet'
}, function(world){
    // use "world"
});

Making things “go” - Advancing the simulation

In order to advance the simulation by one frame, simply call the .step() method with the current time as a parameter. This can be done in any way you like, but usually this will be called inside an animation loop, using window.requestAnimationFrame or similar.

A helper is provided with PhysicsJS to facilitate animation loops: Physics.util.ticker. The ticker methods will use requestAnimationFrame so you will need to polyfill it if necessary. To use the ticker, just call .on() to subscribe to the event, and call the start() method.

Example:

// subscribe to the ticker
Physics.util.ticker.on(function( time ){
    world.step( time );
});
// start the ticker
Physics.util.ticker.start();

Add things to the world

The “Swiss Army Knife” method of the world, is world.add(). This method will accept several types of “things” that can be added or replaced in the world: bodies (objects), behaviors, renderers, and integrators.

Any number of bodies and behaviors may be added to the world, but only one renderer and one integrator can be added to any one world at any one time.

Bodies

Read more about Bodies on the wiki.

Bodies are the “what” of a physics simulation. They represent physical objects that can be rendered as DOM Elements, or drawn on canvas, or however you’d like to display them. They are not by default tied to any particular “view” or visual representation.

Bodies, like points, circles, or convex polygons, are available as extensions.

Creating a body is done using the Physics.body() factory function.

Example:

var ball = Physics.body('circle', {
    x: 50, // x-coordinate
    y: 30, // y-coordinate
    vx: 0.2, // velocity in x-direction
    vy: 0.01, // velocity in y-direction
    radius: 20
});
// add the circle to the world
world.add( ball );

Custom bodies can also be created and bodies can be extended. Read the wiki entry on bodies to learn more.

Behaviors (or Behaviours)

Read more about Behaviors on the wiki.

Behaviors (or Behaviours) are the “how” of a physics simulation. They are rules applied to the world that act on bodies during every timestep to simulate specific physical laws. A world without any behaviors will act as an infinite, frictionless vacuum. (No behaviors are included in the core library)

The most familiar example of a behavior is adding “gravity” to a simulation. What is most often meant by “gravity” is a constant acceleration in the downward (positive “y”) direction. Because of the frequent need for this, a “constant-acceleration” behavior is available as an extra.

Example:

// add some gravity
var gravity = Physics.behavior('constant-acceleration', {
    acc: { x : 0, y: 0.0004 } // this is the default
});
world.add( gravity );
// later... flip the world upside down!
gravity.setAcceleration({ x: 0, y: -0.0004 });

Some behaviors act as “detectors”, which don’t modify bodies directly. Instead they detect specific events and announce them to the world’s pubsub system so other behaviors can take appropriate actions. One example of this is collision detection and response. There are separate behaviors for collision detection, collision response, and even so-called “sweep and prune” optimization algorithms.

Custom behaviors can also be created and behaviors can be extended. Read the wiki entry on behaviors to learn more.

Renderers

Read more about Renderers on the wiki.

Renderers are the user’s viewport into the simulation. At this stage, renderers are primitive and their capabilities limited. Simple renderers exist for DOM and HTML Canvas rendering. It is likely that for any involved simulations or animations, you will have to write your own renderer… but never fear, it’s not all that bad.

Adding an HTML Canvas renderer to the world is simple.

var renderer = Physics.renderer('canvas', {
    el: 'element-id',
    width: 500,
    height: 300,
    meta: false, // don't display meta data
    styles: {
        // set colors for the circle bodies
        'circle' : {
            strokeStyle: 'hsla(60, 37%, 17%, 1)',
            lineWidth: 1,
            fillStyle: 'hsla(60, 37%, 57%, 0.8)',
            angleIndicator: 'hsla(60, 37%, 17%, 0.4)'
        }
    }
});
// add the renderer
world.add( renderer );

In order to render the state of the world to the canvas, you will have to call the world.render() method on every step.

var renderer = Physics.renderer('canvas', {
    //...
});
// add the renderer
world.add( renderer );
world.on('step', function(){
    // Note: equivalent to just calling world.render() after world.step()
    world.render();
});

Custom renderers can be created and renderers can be extended. Read the wiki entry on renderers to learn more.

Integrators

Read more about Integrators on the wiki.

An integrator is the mathematical workhorse of a simulation. An integrator will numerically integrate the physical properties of the bodies. In other words, it will move them to their next positions and velocities every “tick”.

Different integrators have different benefits and drawbacks. Currently the default integrator is the verlet integrator because of its benefits for rigid constraints. An “improved euler” integrator exists and community contributions are welcome (wishlist: RK4).

The integrator for the world can be set when the world is created, or afterwards.

Physics({
    integrator: 'verlet'
}, function(world){
    // set up...
});
// equivalent to...
Physics(function(world){
    world.add( Physics.integrator('verlet') );
    // set up...
});

Custom integrators can be created and integrators can be extended. Read the wiki entry on integrators to learn more.

Full Example

The example code snippets from these basic instructions can be found in this little example simulation:

Physics(function(world){

  var viewWidth = 500;
  var viewHeight = 300;

  var renderer = Physics.renderer('canvas', {
    el: 'viewport',
    width: viewWidth,
    height: viewHeight,
    meta: false, // don't display meta data
    styles: {
        // set colors for the circle bodies
        'circle' : {
            strokeStyle: '#351024',
            lineWidth: 1,
            fillStyle: '#d33682',
            angleIndicator: '#351024'
        }
    }
  });

  // add the renderer
  world.add( renderer );
  // render on each step
  world.on('step', function(){
    world.render();
  });

  // bounds of the window
  var viewportBounds = Physics.aabb(0, 0, viewWidth, viewHeight);

  // constrain objects to these bounds
  world.add(Physics.behavior('edge-collision-detection', {
      aabb: viewportBounds,
      restitution: 0.99,
      cof: 0.99
  }));

  // add a circle
  world.add(
      Physics.body('circle', {
        x: 50, // x-coordinate
        y: 30, // y-coordinate
        vx: 0.2, // velocity in x-direction
        vy: 0.01, // velocity in y-direction
        radius: 20
      })
  );

  // ensure objects bounce when edge collision is detected
  world.add( Physics.behavior('body-impulse-response') );

  // add some gravity
  world.add( Physics.behavior('constant-acceleration') );

  // subscribe to ticker to advance the simulation
  Physics.util.ticker.on(function( time, dt ){

      world.step( time );
  });

  // start the ticker
  Physics.util.ticker.start();

});