Draw Glowing Trails Behind Matter.js Bodies

This article explains how to implement custom glowing motion trails behind fast-moving physics bodies in a Matter.js canvas. By tracking historical coordinates during the engine's update cycle and tapping into the Matter.js render lifecycle, you can use the HTML5 Canvas 2D API to render vibrant, glowing trails using canvas shadows, alpha fading, and additive blend modes.

The Core Concept

Matter.js separates the physics calculation (Matter.Engine) from visual rendering (Matter.Render). The default renderer draws standard body shapes on an HTML5 <canvas>. To draw custom effects such as glowing trails, you must:

  1. Track historical positions of target bodies when their speed exceeds a specified threshold.
  2. Intercept the render loop using the afterRender event.
  3. Use the 2D canvas rendering context to draw fading lines with glow properties (shadowBlur and shadowColor) or additive blending (globalCompositeOperation = 'lighter').

Tracking Body History

Create a data structure to store recent coordinates for bodies that qualify as "fast-moving." You can register an event listener on Matter.Events.on(engine, 'afterUpdate', ...) to capture points each frame.

const trailHistory = new Map();
const MAX_TRAIL_LENGTH = 20;
const SPEED_THRESHOLD = 3;

Matter.Events.on(engine, 'afterUpdate', () => {
  const bodies = Matter.Composite.allBodies(engine.world);

  bodies.forEach(body => {
    // Only track bodies that are dynamic and moving fast enough
    if (body.isStatic) return;

    if (!trailHistory.has(body.id)) {
      trailHistory.set(body.id, []);
    }

    const history = trailHistory.get(body.id);

    if (body.speed > SPEED_THRESHOLD) {
      history.push({ x: body.position.x, y: body.position.y });
      if (history.length > MAX_TRAIL_LENGTH) {
        history.shift();
      }
    } else {
      // Gradually clear the trail when the body slows down
      if (history.length > 0) {
        history.shift();
      }
    }
  });
});

Rendering the Glowing Trail

Hook into afterRender on your render instance. This ensures your custom drawing is executed on top of or beneath the bodies without disrupting Matter.js's internal drawing logic. Always wrap your custom canvas operations in context.save() and context.restore() to prevent style bleed.

Matter.Events.on(render, 'afterRender', () => {
  const context = render.context;

  trailHistory.forEach((history, bodyId) => {
    if (history.length < 2) return;

    context.save();

    // Enable additive blending for a glowing light effect
    context.globalCompositeOperation = 'lighter';

    // Configure glow aesthetics
    context.shadowColor = '#00e5ff';
    context.shadowBlur = 15;
    context.lineCap = 'round';
    context.lineJoin = 'round';

    // Draw trail segments with decreasing opacity and thickness
    for (let i = 0; i < history.length - 1; i++) {
      const start = history[i];
      const end = history[i + 1];
      const progress = (i + 1) / history.length; // 0 (oldest) to 1 (newest)

      context.beginPath();
      context.moveTo(start.x, start.y);
      context.lineTo(end.x, end.y);

      context.strokeStyle = `rgba(0, 229, 255, ${progress * 0.8})`;
      context.lineWidth = progress * 8; // Taper down toward the tail
      context.stroke();
    }

    context.restore();
  });
});

Performance Considerations