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

@idevgames/gravity-js

v1.2.0

Published

Gravity.js is a lightweight Box2D-inspired physics engine that renders using CSS. Configure physics bodies through data attributes, no JavaScript required. Integrates seamlessly with State.js and Keys.js to form a complete browser-based game engine with c

Readme

License CodePen Demos npm bundle size

Gravity.js is a lightweight physics engine that renders using CSS. Configure physics bodies, collisions, and dynamics entirely through data attributes - no JavaScript required for basic physics interactions.

🎮 View Live Demos & Examples on CodePen 🎮

Perfect for creating browser-based games, interactive experiences, and physics simulations without the overhead of canvas rendering. Native DOM elements become physics bodies!


What is Gravity.js?

Gravity.js brings real physics to the browser using:

  • CSS Rendering: No canvas overhead - uses native DOM positioning
  • Data Attribute Configuration: Set up physics bodies with HTML attributes
  • Collision Detection: SAT (Separating Axis Theorem) for OBB (Oriented Bounding Box)
  • Zero Dependencies: Standalone library, works great with State.js and Keys.js
  • Lightweight: Minimal footprint, maximum performance

Features

✅ Physics Body Types: Dynamic, Static, and Kinematic bodies ✅ Shape Support: Box (OBB with rotation) ✅ Realistic Physics: Mass, restitution, friction, forces, impulses, and rotation ✅ Collision Detection: SAT-based OBB collision with automatic resolution and events ✅ Collision Sensors: Trigger zones without physical response ✅ Collision Groups: Filter which bodies can collide ✅ CSS Variables: Access physics state in your CSS ✅ State.js Integration: Collisions trigger state changes ✅ Keys.js Integration: Player input for interactive games ✅ Sleep System: Bodies at rest are optimized automatically


Installation

NPM

npm i @idevgames/gravity-js

Direct Include

<script src="/js/gravity.js"></script>

CDN

<script src="https://cdn.jsdelivr.net/npm/@idevgames/gravity-js/src/gravity.js"></script>

Quick Start

Basic Physics Body

<div data-gravity data-gravity-type="dynamic">
    I'm a physics body!
</div>

Create a Ground

<div data-gravity data-gravity-type="static" style="position:absolute; bottom:0; width:100%; height:40px;">
    Ground
</div>

Complete Example

<!DOCTYPE html>
<html>
<head>
    <script src="gravity.js"></script>
</head>
<body data-gravity-world-gravity="9.8">
    <!-- Falling box -->
    <div data-gravity
         data-gravity-type="dynamic"
         data-gravity-mass="1"
         data-gravity-restitution="0.5"
         style="position:absolute; top:50px; left:100px; width:50px; height:50px; background:red;">
    </div>

    <!-- Static ground -->
    <div data-gravity
         data-gravity-type="static"
         style="position:absolute; bottom:0; width:100%; height:40px; background:green;">
    </div>
</body>
</html>

Physics Body Types

Set the body type with data-gravity-type:

Dynamic

<div data-gravity data-gravity-type="dynamic">
  • Affected by gravity and forces
  • Collides with all body types
  • Default body type

Static

<div data-gravity data-gravity-type="static">
  • Never moves
  • Infinite mass
  • Perfect for ground, walls, platforms

Kinematic

<div data-gravity data-gravity-type="kinematic">
  • Moves but has infinite mass
  • Not affected by forces
  • Perfect for moving platforms

Shape Types

Set collision shape with data-gravity-shape:

Box (OBB)

<div data-gravity data-gravity-shape="box">
  • Default shape
  • Uses element's width and height
  • Oriented Bounding Box collision using SAT
  • Supports rotation at any angle
  • Automatically detects rotation from CSS transform: rotate()

Physics Properties

Mass

<div data-gravity data-gravity-mass="2">
  • Affects momentum and force response
  • Default: 1

Restitution (Bounciness)

<div data-gravity data-gravity-restitution="0.8">
  • How much velocity is retained after collision
  • Range: 0 (no bounce) to 1 (perfect bounce)
  • Default: 0.3

Friction

<div data-gravity data-gravity-friction="0.7">
  • Surface friction during collisions
  • Range: 0 (slippery) to 1 (sticky)
  • Default: 0.5

Density

<div data-gravity data-gravity-density="2">
  • Material density
  • Default: 1

Fixed Rotation

<div data-gravity data-gravity-fixed-rotation="true">
  • Prevents body from rotating
  • Useful for characters and upright objects
  • Default: false

Initial Rotation

<div data-gravity style="transform: rotate(45deg);">
  • Automatically reads CSS transform: rotate() for initial rotation
  • Collision box rotates with the element
  • Can also use data-gravity-rotation="45" (in degrees)

Container Bounds

<div data-gravity-container style="width:800px;height:600px;">
    <div data-gravity data-gravity-type="dynamic">Contained!</div>
</div>
  • Add data-gravity-container to a parent element
  • Creates invisible walls, ceiling, and floor
  • Dynamic bodies bounce off container edges
  • Perfect for keeping objects in view with cursor/mouse interaction

Initial Velocity & Forces

Initial Velocity (Static)

<div data-gravity
     data-gravity-velocity-x="10"
     data-gravity-velocity-y="-5">
  • Set starting velocity with X/Y values
  • X: horizontal, Y: vertical
  • Accepts CSS variables: data-gravity-velocity-x="--cursor-x"

Initial Velocity (Directional)

<div data-gravity
     data-gravity-velocity-right="300"
     data-gravity-velocity-up="200">
  • Set starting velocity with directional values
  • Right/Left are combined: right - left
  • Up/Down are combined: down - up
  • Also accepts CSS variables: data-gravity-velocity-right="--cursor-x"
  • Perfect for jumps: data-gravity-velocity-up="300"

Continuous Forces (Static)

<div data-gravity
     data-gravity-force-x="2"
     data-gravity-force-y="0">
  • Applied every physics update
  • Use for constant propulsion

Dynamic Forces (CSS Variables)

<div data-gravity
     data-gravity-force-right="--key-d, --key-right"
     data-gravity-force-left="--key-a, --key-left"
     data-gravity-force-up="--key-w, --key-up"
     data-gravity-force-down="--key-s, --key-down"
     data-gravity-force-multiplier="80">
  • Read forces from CSS variables (perfect for Keys.js integration)
  • Accepts comma-separated CSS variable names (values are summed)
  • Right/Left forces are combined: (right - left) * multiplier
  • Up/Down forces are combined: (down - up) * multiplier
  • Use with Keys.js for declarative player control

CSS Variables

Gravity.js exposes physics state as CSS variables for dynamic styling:

.my-element {
    /* Position */
    left: var(--gravity-x);
    top: var(--gravity-y);

    /* Rotation */
    transform: rotate(var(--gravity-rotation));

    /* Velocity-based effects */
    opacity: calc(var(--gravity-speed) / 100);

    /* Collision-based effects */
    background: calc(var(--gravity-collision) * 255, 0, 0);
}

Available CSS Variables

  • --gravity-x - Horizontal position (px)
  • --gravity-y - Vertical position (px)
  • --gravity-rotation - Rotation angle (deg)
  • --gravity-velocity-x - Horizontal velocity
  • --gravity-velocity-y - Vertical velocity
  • --gravity-speed - Total speed magnitude
  • --gravity-collision - 1 when colliding, 0 otherwise

Collision Detection

Collisions are automatically detected and resolved. When a collision occurs:

CSS Classes Added

.gravity-colliding {
    /* Applied to bodies currently in collision */
}

.gravity-collision-playerId {
    /* Applied when colliding with element id="playerId" */
}

Custom Events Dispatched

element.addEventListener('gravitycollision', (e) => {
    console.log('Collision with:', e.detail.other);
    console.log('Other ID:', e.detail.otherId);
});

element.addEventListener('gravityexit', (e) => {
    console.log('Stopped colliding with:', e.detail.other);
});

Data Attributes Updated

<!-- Automatically updated on collision -->
<div data-collision-with="coinId">

Collision Sensors (Triggers)

Create trigger zones that detect collisions without physical response:

<div data-gravity
     data-gravity-type="static"
     data-gravity-sensor="true"
     id="checkpoint"
     style="position:absolute; left:200px; top:100px; width:50px; height:50px;">
</div>

Perfect for:

  • Checkpoint zones
  • Collectible items
  • Trigger areas
  • Event zones

Collision Groups

Filter which bodies can collide:

<!-- Player group -->
<div data-gravity data-gravity-group="player"></div>

<!-- Enemy group -->
<div data-gravity data-gravity-group="enemy"></div>

<!-- Default group (collides with everything) -->
<div data-gravity data-gravity-group="default"></div>

Bodies only collide with:

  • Other bodies in the same group
  • Bodies in the default group

World Settings

Configure global physics on the <body> element:

<body data-gravity-world-gravity="9.8"
      data-gravity-world-gravity-x="0"
      data-gravity-world-gravity-y="9.8"
      data-gravity-damping="0.99"
      data-gravity-angular-damping="0.99">

World Attributes

  • data-gravity-world-gravity - Sets both X and Y gravity
  • data-gravity-world-gravity-x - Horizontal gravity (default: 0)
  • data-gravity-world-gravity-y - Vertical gravity (default: 9.8)
  • data-gravity-damping - Velocity damping (default: 0.99)
  • data-gravity-angular-damping - Rotation damping (default: 0.99)

JavaScript API

While Gravity.js works without JavaScript, you can use these methods for advanced control:

Apply Force

gravity.applyForce('playerId', 50, 0); // Apply continuous force

Apply Impulse

gravity.applyImpulse('playerId', 0, -300); // One-time force (jump)

Set Velocity

gravity.setVelocity('playerId', 10, 0); // Set velocity directly

Get Body Data

const body = gravity.getBody('playerId');
console.log(body.x, body.y, body.velocityX, body.velocityY);

Pause/Resume Simulation

gravity.pauseSimulation();
gravity.resumeSimulation();

Integration with State.js

Gravity.js works seamlessly with State.js for game state management:

<!-- Player with state tracking -->
<div id="player"
     data-gravity
     data-state
     data-state-watch="collision-with,velocity-x,velocity-y">
</div>

<!-- Coin sensor that triggers state changes -->
<div id="coin"
     data-gravity
     data-gravity-sensor="true"
     data-state
     data-state-trigger
     data-state-bind="score"
     data-state-increment="10">
</div>

<!-- Score display -->
<div id="score"
     data-state
     data-score="0"
     data-state-watch="score">
    Score: <span data-state-display="score">0</span>
</div>

When player collides with coin:

  1. Gravity.js detects collision
  2. Updates data-collision-with attribute
  3. State.js watches attribute change
  4. Increments score automatically

Integration with Keys.js

Combine with Keys.js for player input - NO JavaScript required!

Declarative Integration (Recommended)

Keys.js sets CSS variables that Gravity.js can read automatically:

<script src="https://cdn.jsdelivr.net/npm/@idevgames/keys-js/src/keys.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@idevgames/gravity-js/src/gravity.js"></script>

<!-- Container with Keys.js watching WASD keys -->
<div data-keys
     data-keys-watch="w,a,s,d"
     data-keys-prevent="w,a,s,d"
     data-keys-var="true">

    <!-- Player controlled by keyboard - ZERO JavaScript needed! -->
    <div id="player"
         data-gravity
         data-gravity-type="dynamic"
         data-gravity-fixed-rotation="true"
         data-gravity-force-right="--key-d, --key-right"
         data-gravity-force-left="--key-a, --key-left"
         data-gravity-force-down="--key-s, --key-down"
         data-gravity-force-up="--key-w, --key-up"
         data-gravity-force-multiplier="80">
    </div>
</div>

How it works:

  • Keys.js sets --key-* CSS variables (0 when released, 1 when pressed)
  • Gravity.js reads these via data-gravity-force-right/left/up/down attributes
  • Each attribute accepts comma-separated CSS variable names (e.g., --key-d, --key-right)
  • Multiple variables are summed together automatically
  • Forces are automatically combined: right - left for X, down - up for Y
  • data-gravity-force-multiplier scales the force strength
  • No JavaScript required - pure CSS variable integration!

JavaScript Integration (Advanced)

For more control, use the JavaScript API:

<script src="keys.js"></script>
<script src="gravity.js"></script>

<script>
    // Jump on spacebar
    document.addEventListener('keydown', () => {
        if (keys.isKeyDown('space')) {
            gravity.applyImpulse('player', 0, -300);
        }
    });

    // Move with arrow keys
    setInterval(() => {
        const vec = keys.getArrowVector();
        gravity.applyForce('player', vec.x * 50, 0);
    }, 16);
</script>

Complete Game Example

<!DOCTYPE html>
<html>
<head>
    <script src="state.js"></script>
    <script src="keys.js"></script>
    <script src="gravity.js"></script>
    <style>
        .player {
            position: absolute;
            width: 40px;
            height: 40px;
            background: blue;
        }
        .ground {
            position: absolute;
            bottom: 0;
            width: 100%;
            height: 40px;
            background: green;
        }
        .coin {
            position: absolute;
            width: 30px;
            height: 30px;
            background: gold;
            border-radius: 50%;
        }
        .coin.gravity-colliding {
            display: none;
        }
    </style>
</head>
<body data-gravity-world-gravity="9.8">
    <!-- Score -->
    <div data-state data-score="0" data-state-watch="score">
        Score: <span data-state-display="score">0</span>
    </div>

    <!-- Player -->
    <div id="player" class="player"
         data-gravity
         data-gravity-type="dynamic"
         data-gravity-mass="1"
         data-gravity-fixed-rotation="true"
         style="top:100px; left:100px;">
    </div>

    <!-- Coin -->
    <div id="coin" class="coin"
         data-gravity
         data-gravity-type="static"
         data-gravity-sensor="true"
         data-gravity-radius="15"
         data-state
         data-state-trigger
         data-state-bind="score"
         data-state-attr="score"
         data-state-increment="10"
         style="top:200px; left:300px;">
    </div>

    <!-- Ground -->
    <div class="ground"
         data-gravity
         data-gravity-type="static">
    </div>

    <script>
        // Jump on spacebar
        document.addEventListener('keydown', () => {
            if (keys.isKeyDown('space')) {
                gravity.applyImpulse('player', 0, -300);
            }
        });

        // Move with arrow keys
        setInterval(() => {
            const vec = keys.getArrowVector();
            if (vec.x !== 0) {
                gravity.applyForce('player', vec.x * 100, 0);
            }
        }, 16);
    </script>
</body>
</html>

Browser Support

Gravity.js works in all modern browsers:

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)

Requires:

  • ES6 class support
  • requestAnimationFrame
  • CSS custom properties (variables)
  • Map and Set

Performance

Gravity.js is optimized for performance:

  • ✅ Spatial grid for broad-phase collision detection
  • ✅ Sleep system for bodies at rest
  • ✅ Efficient attribute caching
  • ✅ Fixed timestep physics simulation
  • ✅ requestAnimationFrame for smooth rendering

Companion Libraries

Complete Browser Game Engine Stack

Gravity.js (Physics) Physics simulation, collision detection, and dynamics

State.js (State Management) Game state, variables, and reactive data binding

Keys.js (Input) Keyboard input handling and key state tracking

Together they form a complete, zero-dependency game engine for the browser!


Data Attribute Reference

Body Configuration

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-gravity | - | - | Enables physics on element | | data-gravity-type | dynamic\|static\|kinematic | dynamic | Body type | | data-gravity-shape | box | box | Collision shape | | data-gravity-mass | number | 1 | Body mass | | data-gravity-restitution | 0-1 | 0.3 | Bounciness | | data-gravity-friction | 0-1 | 0.5 | Surface friction | | data-gravity-density | number | 1 | Material density | | data-gravity-fixed-rotation | boolean | false | Prevent rotation | | data-gravity-sensor | boolean | false | Trigger-only collider | | data-gravity-group | string | default | Collision group |

Initial Motion

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-gravity-velocity-x | number | 0 | Initial horizontal velocity | | data-gravity-velocity-y | number | 0 | Initial vertical velocity | | data-gravity-force-x | number | 0 | Continuous horizontal force | | data-gravity-force-y | number | 0 | Continuous vertical force |

Directional Motion

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-gravity-velocity-right | number | 0 | Initial rightward velocity | | data-gravity-velocity-left | number | 0 | Initial leftward velocity | | data-gravity-velocity-up | number | 0 | Initial upward velocity (perfect for jumps!) | | data-gravity-velocity-down | number | 0 | Initial downward velocity | | data-gravity-force-right | string | - | CSS variable(s) for rightward force (e.g., --key-d, --key-right) | | data-gravity-force-left | string | - | CSS variable(s) for leftward force (e.g., --key-a, --key-left) | | data-gravity-force-up | string | - | CSS variable(s) for upward force (e.g., --key-w, --key-up) | | data-gravity-force-down | string | - | CSS variable(s) for downward force (e.g., --key-s, --key-down) | | data-gravity-force-multiplier | number | 100 | Multiplier for directional force values |

World Settings (on <body>)

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-gravity-world-gravity | number | 9.8 | Global gravity (X and Y) | | data-gravity-world-gravity-x | number | 0 | Horizontal gravity | | data-gravity-world-gravity-y | number | 9.8 | Vertical gravity | | data-gravity-damping | 0-1 | 0.99 | Velocity damping | | data-gravity-angular-damping | 0-1 | 0.99 | Rotation damping |


License

MIT License - feel free to use in personal and commercial projects!


Author

Created by iDev Games

Follow for updates:


Contributing

Contributions welcome! Please open issues and pull requests on GitHub.


Gravity.js - Turn the browser into a physics-powered game engine! 🎮⚡