Sync HTML Elements with Matter.js Using DOM Plugin
This article explains how to use matter-dom-plugin to
bind standard HTML elements to Matter.js physics bodies. By replacing or
augmenting the default HTML5 Canvas renderer with DOM-based rendering,
you can apply real-time 2D physics—including gravity, collisions, and
friction—directly to text boxes, images, buttons, and custom layout
components using CSS transforms.
What is matter-dom-plugin?
Matter.js typically renders physics simulations to an HTML5
<canvas> element using its built-in
Matter.Render module. The matter-dom-plugin
extends Matter.js so that each physical body's position (x,
y) and rotation angle (angle) automatically
map to the CSS transform properties
(translate3d and rotate) of a designated DOM
node. This allows standard web elements to inherit realistic physical
movement while retaining interactive features such as clickable links,
selectable text, and CSS styling.
Installation and Setup
To get started, install both Matter.js and the DOM plugin via npm or include them via a script tag.
Using npm:
npm install matter-js matter-dom-pluginUsing standard script imports:
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
<script src="path/to/matter-dom-plugin.js"></script>Initializing the Plugin
Register the plugin with Matter.js before creating your engine and world.
import Matter from 'matter-js';
import MatterDomPlugin from 'matter-dom-plugin';
// Register the plugin with Matter.js
Matter.use(MatterDomPlugin);If you are using script tags in a browser environment, the plugin
typically auto-registers itself with the global Matter
object.
HTML and CSS Configuration
Prepare your container and the elements you wish to simulate. The container must have absolute or relative positioning, and the physics elements must have absolute positioning so transforms calculate correctly from the container's top-left origin.
<div id="viewport" style="position: relative; width: 800px; height: 600px; overflow: hidden; border: 1px solid #ccc;">
<div id="box" style="position: absolute; width: 100px; height: 100px; background-color: #ff5722;">
Interactive Box
</div>
</div>Ensure transform-origin on the DOM elements is set to
center center to align with the center-of-mass coordinates
used by Matter.js bodies:
#box {
transform-origin: center center;
top: 0;
left: 0;
margin-top: -50px; /* Half of height */
margin-left: -50px; /* Half of width */
}Creating Bodies and Linking DOM Elements
To bind an element, pass the DOM node reference inside the body
options under the dom property.
const { Engine, Runner, Bodies, Composite } = Matter;
// Create engine
const engine = Engine.create();
const world = engine.world;
// Select DOM element
const domElement = document.getElementById('box');
// Create a physical body matching the element's size and link the DOM element
const boxBody = Bodies.rectangle(400, 100, 100, 100, {
restitution: 0.8,
dom: {
element: domElement
}
});
// Create static boundaries to keep the body inside the viewport
const ground = Bodies.rectangle(400, 590, 810, 20, { isStatic: true });
const leftWall = Bodies.rectangle(0, 300, 20, 600, { isStatic: true });
const rightWall = Bodies.rectangle(800, 300, 20, 600, { isStatic: true });
// Add bodies to the world
Composite.add(world, [boxBody, ground, leftWall, rightWall]);Running the Simulation
Initialize the DomRender provided by the plugin (or the
standard runner loop configured for DOM updates) to synchronize physics
states with the DOM tree on every frame.
const { DomRender, Runner } = Matter;
// Initialize the DOM renderer
const domRender = DomRender.create({
engine: engine,
container: document.getElementById('viewport')
});
// Run renderer and engine
DomRender.run(domRender);
const runner = Runner.create();
Runner.run(runner, engine);Handling Interactions
Once integrated, DOM nodes support standard event listeners without
interrupting the physics engine. You can attach native mouse listeners
or apply Matter.js constraints, such as
Matter.MouseConstraint, to drag and toss DOM elements
across the viewport.