How to Show Velocity Vectors in Matter.js
Visualizing velocity vectors in Matter.js is an essential debugging
technique for physics simulations. While Matter.js provides a built-in
debug flag for quick checks, the most reliable and customizable method
is using the afterRender event to draw vectors directly
onto the canvas using native HTML5 2D Canvas context methods. This guide
covers both the built-in renderer option and the custom canvas drawing
implementation to visualize the speed and direction of moving
bodies.
Method 1: Using the Built-In Debug Option
Matter.js has a built-in flag within its default renderer
specifically for displaying velocity vectors. You can enable it when
initializing the Render module or modify it dynamically on
an existing instance.
const render = Matter.Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
wireframes: false,
showVelocity: true // Enables built-in velocity vectors
}
});When showVelocity is set to true, Matter.js
renders lines from the center of each moving body indicating its current
trajectory. However, this feature is restricted to wireframe mode in
some Matter.js builds and offers limited control over vector scaling,
color, and line thickness.
Method 2: Custom
Drawing with afterRender
For complete control over vector length, styling, and arrowheads,
hook into the afterRender event. This fires immediately
after Matter.js clears and draws the scene, allowing you to draw custom
graphics on top of the physics bodies.
Matter.Events.on(render, 'afterRender', () => {
const context = render.context;
const bodies = Matter.Composite.allBodies(engine.world);
const vectorScale = 5; // Multiplier to make vectors clearly visible
context.beginPath();
bodies.forEach(body => {
// Skip static bodies since they have zero velocity
if (body.isStatic) return;
const startX = body.position.x;
const startY = body.position.y;
const endX = startX + body.velocity.x * vectorScale;
const endY = startY + body.velocity.y * vectorScale;
// Draw the velocity line
context.moveTo(startX, startY);
context.lineTo(endX, endY);
});
context.lineWidth = 2;
context.strokeStyle = '#ff3333'; // High-contrast color (red)
context.stroke();
});Adding Arrowheads to Vectors
To indicate direction clearly, you can extend the
afterRender logic by drawing an arrowhead at the tip of
each vector line.
function drawArrow(ctx, fromX, fromY, toX, toY, headLength = 8) {
const dx = toX - fromX;
const dy = toY - fromY;
const angle = Math.atan2(dy, dx);
// Only draw arrowheads if the body is actually moving
if (Math.hypot(dx, dy) < 1) return;
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
// Draw arrowhead branches
ctx.lineTo(
toX - headLength * Math.cos(angle - Math.PI / 6),
toY - headLength * Math.sin(angle - Math.PI / 6)
);
ctx.moveTo(toX, toY);
ctx.lineTo(
toX - headLength * Math.cos(angle + Math.PI / 6),
toY - headLength * Math.sin(angle + Math.PI / 6)
);
}
Matter.Events.on(render, 'afterRender', () => {
const ctx = render.context;
const bodies = Matter.Composite.allBodies(engine.world);
const vectorScale = 4;
ctx.beginPath();
ctx.strokeStyle = '#00ffcc';
ctx.lineWidth = 2;
bodies.forEach(body => {
if (body.isStatic) return;
const startX = body.position.x;
const startY = body.position.y;
const endX = startX + body.velocity.x * vectorScale;
const endY = startY + body.velocity.y * vectorScale;
drawArrow(ctx, startX, startY, endX, endY);
});
ctx.stroke();
});Key Considerations
- Scale Factor: Raw velocity values in Matter.js are
usually small (pixels per update step). Always multiply
body.velocity.xandbody.velocity.yby a constant scale factor (such as3to10) to make the lines readable. - Sleeping Bodies: If you enable sleep states via
engine.enableSleeping, checkbody.isSleepingalongsidebody.isStaticto avoid running drawing calculations for stationary objects.