JSFiddle - React, Tailwind, and code Playground

HTML

<canvas></canvas>
<div>WASD to move</div>

JavaScript

// Shared code.
const INITIAL_POSITION = { x: 300, y: 300 };

// Server-side code.
const server_state = { x: INITIAL_POSITION.x, y: INITIAL_POSITION.y };
const UPDATE_TICK_RATE = 10;
const PHYSICS_TICK_RATE = 60;

setInterval(() => {
  // Simulate server physics update.
  const w = input[87] ? 1 : 0;
  const a = input[65] ? 1 : 0;
  const s = input[83] ? 1 : 0;
  const d = input[68] ? 1 : 0;

  const vertical = w ? 1 : s ? -1 : 0;
  const horizontal = d ? 1 : a ? -1 : 0;

  server_state.x += horizontal * 5;
  server_state.y -= vertical * 5;
}, 1000 / PHYSICS_TICK_RATE)

setInterval(() => {
	// Simulate server sending updates.
  target_pos_x = server_state.x;
  target_pos_y = server_state.y;
}, 1000 / UPDATE_TICK_RATE);


// Client-side code.
let target_pos_x = INITIAL_POSITION.x, target_pos_y = INITIAL_POSITION.y;
let input = [];

window.addEventListener('keydown', e => input[e.keyCode] = true);
window.addEventListener('keyup', e => input[e.keyCode] = false);

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const W = canvas.width = 600;
const H = canvas.height = 600;

const circle = {
  position: { x: INITIAL_POSITION.x, y: INITIAL_POSITION.y },
  tick: function() {
  	let t = 0.1;
    this.position.x = lerp(this.position.x, target_pos_x, t);
    this.position.y = lerp(this.position.y, target_pos_y, t);

    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, 3, 0, 2 * Math.PI);
    ctx.fillStyle = 'white'
    ctx.fill();
  }
}

const reference_point = {
  position: { x: 240, y: 240 },
  tick: function() {
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, 3, 0, 2 * Math.PI);
    ctx.fillStyle = 'red'
    ctx.fill()
  }
}

function tick(now) {
	clear();
  circle.tick();
  reference_point.tick();
  camera_follow();
  window.requestAnimationFrame(tick);
}

window.requestAnimationFrame(tick);

function clear() {
  ctx.save();
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0, 0, W, H);
 ...