Skip to content

Projectile Motion

Launch a ball with initial velocity, apply gravity, bounce on the floor, and draw a fading trail.

Paste into main.js in the editor, then click Restart.

Complete example

javascript
if (!state.init) {
  state.init = true;
  state.x = 80;
  state.y = h - 60;
  state.vx = 200;
  state.vy = -300;
  state.trail = [];
}

const g = 400;
state.vy += g * dt;
state.x += state.vx * dt;
state.y += state.vy * dt;

if (state.y > h - 20) {
  state.y = h - 20;
  state.vy *= -0.6;
  state.vx *= 0.8;
}

state.trail.push({ x: state.x, y: state.y });
if (state.trail.length > 200) state.trail.shift();

const trailLines = [];
for (let i = 1; i < state.trail.length; i++) {
  trailLines.push({
    x1: state.trail[i - 1].x, y1: state.trail[i - 1].y,
    x2: state.trail[i].x, y2: state.trail[i].y,
    color: 'rgba(139,92,246,' + (i / state.trail.length * 0.6) + ')',
    width: 1.5,
  });
}

return {
  particles: [
    { x: state.x, y: state.y, r: 8, color: '#8B5CF6', label: 'ball' },
  ],
  lines: [
    ...trailLines,
    { x1: 0, y1: h - 20, x2: w, y2: h - 20, color: 'rgba(255,255,255,0.1)', width: 1 },
  ],
  vectors: [
    { x: state.vx * 0.2, y: state.vy * 0.2, ox: state.x, oy: state.y, color: '#22C55E', label: 'v' },
  ],
  text: {
    vx: state.vx.toFixed(0) + ' px/s',
    vy: state.vy.toFixed(0) + ' px/s',
    x: state.x.toFixed(0),
    y: state.y.toFixed(0),
  },
};

What's going on

PieceRole
vx, vyLaunch velocity (px/s). Negative vy is up on screen
g = 400Downward acceleration (Y increases downward)
Floor bounceRestitution 0.6, friction on vx
TrailCap at 200; alpha fades with age

MATH

Ideal range (no bounce): ( R = v_0^2 \sin 2\theta / g ). Here units are pixels — tune g and launch speed for a readable arc.

Try next

  • Click to relaunch from state.clickX/Y
  • Draw the analytic parabola as a dashed lines overlay
  • Add air drag: vx *= (1 - c * dt)