Skip to content

Energy on a Track

A ball follows a sinusoidal track while KE / PE bars show the energy trade-off.

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

Complete example

javascript
const groundY = h - 80;
const trackAmp = 60;
const trackFreq = 0.015;

function trackY(x) {
  return groundY - trackAmp * Math.sin(trackFreq * x);
}

if (!state.init) {
  state.init = true;
  state.x = 50;
  state.vx = 120;
  state.trail = [];
}

// Simplified slope-driven acceleration along x
const slopeTerm = trackAmp * trackFreq * Math.cos(trackFreq * state.x);
const g = 300;
state.vx += g * slopeTerm * slopeTerm * dt * 0.001;
state.x += state.vx * dt;
state.trail.push({ x: state.x, y: trackY(state.x) });
if (state.trail.length > 300) state.trail.shift();

const py = trackY(state.x);
const KE = 0.5 * state.vx * state.vx * 0.01;
const PE = (groundY - py) * 0.5;
const total = KE + PE;

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.5) + ')',
    width: 1.5,
  });
}

const trackLines = [];
for (let x = 0; x < w; x += 4) {
  trackLines.push({
    x1: x, y1: trackY(x), x2: x + 4, y2: trackY(x + 4),
    color: 'rgba(255,255,255,0.12)', width: 1,
  });
}

return {
  lines: [...trackLines, ...trailLines],
  particles: [
    { x: state.x, y: py, r: 10, color: '#8B5CF6', label: 'ball' },
  ],
  bars: [
    { x: 20, y: h - 30 - KE, w: 40, h: KE, color: '#EF4444', label: 'KE' },
    { x: 80, y: h - 30 - PE, w: 40, h: PE, color: '#3B82F6', label: 'PE' },
    { x: 140, y: h - 30 - total, w: 40, h: total, color: '#22C55E', label: 'Total' },
  ],
  text: {
    x: state.x.toFixed(0),
    vx: state.vx.toFixed(0),
  },
};

Reading the bars

BarMeaning
KEGrows when the ball is fast (often in valleys)
PEHigher on peaks (larger groundY - py)
TotalRough conserved quantity in this toy model

Scales are chosen for visibility, not SI units.

TIP

If the track lines array gets large on wide canvases, step x by 6–8 instead of 4, or cache the track polyline in state on init / resize.

Try next

  • Wrap state.x when it leaves [0, w]
  • Compare with the Spring energy bars (true 1D Hooke)
  • Derive acceleration from the analytic derivative of trackY for a more physical slope model