Summary
Add a Trail renderable that draws a fading, tapering ribbon behind a moving object. This is one of the most common visual effects in 2D games — sword slashes, dash speed lines, bullet trails, magic arcs, cursor trails — and currently requires developers to build it from scratch.
Use Cases
- Weapon slashes — short lifetime, wide, white or colored
- Dash/speed lines — behind a character mid-dash
- Bullet/projectile trails — thin, long lifetime
- Magic/spell arcs — textured, colorful
- Touch/cursor trails — follows pointer input
- Moving platform indicators — shows recent path
API Sketch
Auto-follow mode — attach to a target, samples position every frame:
import { Trail } from "melonjs";
const trail = new Trail({
target: player, // sample position from this renderable
length: 20, // max points in buffer
lifetime: 300, // each point lives 300ms
minDistance: 4, // skip point if moved less than 4px (avoids clustering)
widthStart: 12, // px width at head
widthEnd: 0, // tapers to nothing at tail
colorStart: "#44aaff", // opaque at head
colorEnd: "#44aaff00", // transparent at tail
});
container.addChild(trail);
Manual mode — add points from game logic (e.g., sword tip during attack):
const slash = new Trail({ lifetime: 200, widthStart: 24, widthEnd: 2 });
container.addChild(slash);
// called each frame during the attack animation
slash.addPoint(swordTip.x, swordTip.y);
Textured ribbon — stretch a texture along the trail (fire, lightning, energy beam):
const beam = new Trail({
target: projectile,
texture: assets.getImage("beam_texture"),
lifetime: 500,
widthStart: 16,
widthEnd: 8,
});
Implementation
Data Structure
A ring buffer of trail points. Each point stores { x, y, age }. On each update(dt):
- If a target is set, sample
target.pos — skip if distance from last point < minDistance
- Push new point into the ring buffer
- Age all points by
dt, remove any where age > lifetime
Rendering — Triangle Strip Tessellation
For each consecutive pair of points:
- Compute the perpendicular (normal) vector to the segment direction
- Scale it by the interpolated width (
widthStart → widthEnd based on age / lifetime)
- Emit two vertices (left and right of center line) with interpolated color/alpha
- Connect as a triangle strip (each new pair of vertices forms two triangles with the previous pair)
UVs for textured mode are straightforward: U maps 0→1 along the trail length, V maps 0→1 across the width.
Both Renderers — Full Feature Parity
WebGL renderer: draw the triangle strip via the existing primitive batcher (untextured) or mesh batcher (textured).
Canvas renderer: use the same per-triangle affine texture mapping technique already used by drawMesh() — clip to triangle path, compute UV-to-screen transform, drawImage. For untextured trails, draw as line segments with interpolated lineWidth and strokeStyle.
No renderer-specific limitations — both get tapering width, color/alpha fading, textured ribbons, and all blend modes.
Class Design
Trail extends Renderable:
Parameters
| Parameter |
Type |
Default |
Description |
target |
Renderable | Vector2d |
null |
Auto-follow target |
length |
number |
20 |
Max points in buffer |
lifetime |
number |
500 |
Point lifetime in ms |
minDistance |
number |
4 |
Min px between samples |
widthStart |
number |
10 |
Width at head (px) |
widthEnd |
number |
0 |
Width at tail (px) |
colorStart |
Color | string |
"#ffffff" |
Color at head |
colorEnd |
Color | string |
"#ffffff00" |
Color at tail |
texture |
HTMLImageElement |
null |
Optional texture for ribbon mode |
Optional Enhancements (future)
- Curve smoothing — Catmull-Rom interpolation between points for smoother curves at low sample rates
- Width/color curves — arbitrary curves instead of linear start→end interpolation
Summary
Add a
Trailrenderable that draws a fading, tapering ribbon behind a moving object. This is one of the most common visual effects in 2D games — sword slashes, dash speed lines, bullet trails, magic arcs, cursor trails — and currently requires developers to build it from scratch.Use Cases
API Sketch
Auto-follow mode — attach to a target, samples position every frame:
Manual mode — add points from game logic (e.g., sword tip during attack):
Textured ribbon — stretch a texture along the trail (fire, lightning, energy beam):
Implementation
Data Structure
A ring buffer of trail points. Each point stores
{ x, y, age }. On eachupdate(dt):target.pos— skip if distance from last point <minDistancedt, remove any whereage > lifetimeRendering — Triangle Strip Tessellation
For each consecutive pair of points:
widthStart → widthEndbased onage / lifetime)UVs for textured mode are straightforward: U maps 0→1 along the trail length, V maps 0→1 across the width.
Both Renderers — Full Feature Parity
WebGL renderer: draw the triangle strip via the existing primitive batcher (untextured) or mesh batcher (textured).
Canvas renderer: use the same per-triangle affine texture mapping technique already used by
drawMesh()— clip to triangle path, compute UV-to-screen transform,drawImage. For untextured trails, draw as line segments with interpolatedlineWidthandstrokeStyle.No renderer-specific limitations — both get tapering width, color/alpha fading, textured ribbons, and all blend modes.
Class Design
TrailextendsRenderable:blendMode(additive for glowing trails, etc.)shaderproperty for custom per-trail shader effects (Add built-in ShaderEffect presets for common sprite effects #1366)Parameters
targetRenderable | Vector2dnulllengthnumber20lifetimenumber500minDistancenumber4widthStartnumber10widthEndnumber0colorStartColor | string"#ffffff"colorEndColor | string"#ffffff00"textureHTMLImageElementnullOptional Enhancements (future)