JSFiddle - React, Tailwind, and code Playground

by Will Stott

HTML

<canvas id="cvs" width="512", height="160"></canvas>
<br>
<input id="inp" type="range" min="0" max="100" value="50"/>
<button onclick="reset()" text="reset">Reset</button>
<br><br>
<label>Force</label><input id="force" type="number" value="50"/><br>
<label>Fricton</label><input id="friction" type="number"value="11"/><br>
<!--<label>Damping</label><input id="damping" type="number"value="3"/><br>-->
<label>Slew</label><input id="slew" type="number" value="180"/><br>
<label>Max Accel</label><input id="maxacc" type="number" value="400"/><br>

CSS

canvas {
  background-color: #ffeeee;
  border: 1px solid gray;
}
label {
  display: inline-block;
  width: 6em;
}
.affecting {
  border-right: solid 4px red;
}

JavaScript

let val = 50;
let speed = 0;
const dof = {
	force: () => parseFloat(document.getElementById("force").value || 0),
  friction: () => parseFloat(document.getElementById("friction").value || 0),
  // damping: () => parseFloat(document.getElementById("damping").value || 0),
  clampSpeed: (speed) => {
    let dom = document.getElementById("slew");
    let slew = parseFloat(dom.value || 0);
  	let speed2 = clamp(speed, -slew, slew);
    dom.classList.toggle("affecting", speed2 !== speed);
    return speed2;
  },
  clampAccel: (accel) => {
    let dom = document.getElementById("maxacc");
    let max = parseFloat(dom.value || 0);
  	let accel2 = clamp(accel, -max, max);
    dom.classList.toggle("affecting", accel2 !== accel);
    return accel2;
  },
};

function run (demand, timeDelta) {
  const error = demand - val;
  let acceleration = error * dof.force() - dof.friction() * speed;
  acceleration = dof.clampAccel(acceleration);
  speed += acceleration * timeDelta;
  // speed = clamp(Math.abs(speed) - dof.damping(), 0, 10000000000) * Math.sign(speed);
  speed = dof.clampSpeed(speed);
  val += speed * timeDelta;
  // console.debug(demand, val, speed, acceleration, error);
  return val;
}

function clamp(v, min, max) {
	if (v < min)
  	return min;
  if (v > max)
  	return max;
  return v;
}

let lastTime = null;
function reset() {
  lastTime = null;
  val = 50;
  speed = 0;
  demands.fill(50);
  values.fill(50);
  slider.value = 50;
}

const dataHeightBorder = 20;
const dataWidth = 400;
const demands = new Array(dataWidth);
const values = new Array(dataWidth);

const canvas = document.getElementById("cvs");
const slider = document.getElementById("inp");
const ctx = canvas.getContext("2d");

function main() {
	reset();

  function point (ctx, i, val) {
    let h = canvas.height - dataHeightBorder * 2;
    ctx.lineTo(
      i  / dataWidth * canvas.width,
      (((100 - val) / 100) * h) + dataHeightBorder
    );
  }

  let lastTime = null;
  function onTick (t) {
    if...