*,html,body{margin:0px;padding:0px;overflow:hidden;}
let positions, velocities, forces, masses, fps, maxSpeed;

// Dracula theme colors
const colors = {
  background: "#282a36",
  currentLine: "#44475a",
  foreground: "#f8f8f2",
  comment: "#6272a4",
  cyan: "#8be9fd",
  green: "#50fa7b",
  orange: "#ffb86c",
  pink: "#ff79c6",
  purple: "#bd93f9",
  red: "#ff5555",
  yellow: "#f1fa8c"
};

function setup() {
  createCanvas(windowWidth, windowHeight);
  positions = [createVector(-100, -100), createVector(100, 100)];
  velocities = [createVector(0, 0), createVector(0, 0)];
  forces = [createVector(0, 0), createVector(0, 0)];
  masses = [1, 10];
  fps = 60;
  maxSpeed = 256;
}

function draw() {
  clear();
  background(colors.background);
  let i = width / 2;
  let o = height / 2;
  translate(i, o);

  let mousePos = createVector(mouseX - i, mouseY - o);

  for (let s = 0; s < positions.length; s++) {
    let e = positions[s].x + velocities[s].x;
    let t = positions[s].y + velocities[s].y;
    let n = positions[s].x + forces[s].x;
    let c = positions[s].y + forces[s].y;

    strokeWeight(2);
    stroke(colors.green);
    drawArrow(positions[s].x, positions[s].y, n, c);

    strokeWeight(1);
    stroke(colors.pink);
    drawArrow(positions[s].x, positions[s].y, e, t);
    drawCircleMarker(positions[s], 5 * sqrt(masses[s]));

    strokeWeight(0);
    fill(colors.foreground);
    drawLabel(
      positions[s].x + 5 * sqrt(masses[s]),
      positions[s].y,
      `P (${positions[s].x.toPrecision(4)}, ${positions[s].y.toPrecision(4)})`,
      LEFT
    );
    drawLabel(
      positions[s].x + 5 * sqrt(masses[s]),
      positions[s].y + 15,
      `M ${masses[s]}`,
      LEFT
    );
    drawLabel(
      e,
      t,
      `V (${velocities[s].x.toPrecision(4)}, ${velocities[s].y.toPrecision(
        4
      )})`,
      LEFT
    );
    drawLabel(
      n,
      c,
      `A (${forces[s].x.toPrecision(4)}, ${forces[s].y.toPrecision(4)})`,
      LEFT
    );

    forces[s] = p5.Vector.sub(mousePos, positions[s]).div(2);
    let r = forces[s].copy().div(masses[s]).div(fps);
    velocities[s].add(r);

    if (velocities[s].mag() > maxSpeed) {
      velocities[s].setMag(maxSpeed);
    }

    positions[s].add(velocities[s].copy().div(fps));

    if (positions[s].x > i || positions[s].x < -i) {
      velocities[s].x = -velocities[s].x;
    }
    if (positions[s].y > o || positions[s].y < -o) {
      velocities[s].y = -velocities[s].y;
    }
  }
}

function drawCircleMarker(i, o) {
  fill(colors.cyan);
  noStroke();
  ellipse(i.x, i.y, 2 * o, 2 * o);
}

function drawArrow(i, o, s, e) {
  line(i, o, s, e);
  let t = createVector(s - i, e - o)
    .normalize()
    .mult(4);
  line(s, e, s - t.y - t.x, e + t.x - t.y);
  line(s, e, s + t.y - t.x, e - t.x - t.y);
}

function drawLabel(i, o, s, e = CENTER) {
  push();
  textFont("monospace");
  textSize(14);
  textAlign(e);
  if (e == LEFT) {
    i += 6;
  }
  if (e == RIGHT) {
    i -= 6;
  }
  text(s, i, o);
  pop();
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/p5@latest/lib/p5.min.js