<canvas></canvas>
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    image-rendering: pixelated;
}
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');

const player = {
  x: 10,
  y: 10,
  speed: 3
};
const target = {
  x: 100,
  y: 300
};

const update = (time) => {
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    const angle = Math.atan2(target.y - player.y, target.x - player.x);
    const distance = Math.hypot(target.x - player.x, target.y - player.y);
    
    if (distance > player.speed) {
        player.x += Math.cos(angle) * player.speed;
        player.y += Math.sin(angle) * player.speed;    
    } else {
        player.x += Math.cos(angle) * distance;
        player.y += Math.sin(angle) * distance;
        
        target.x = Math.floor(Math.random() * canvas.width);
        target.y = Math.floor(Math.random() * canvas.height);
    }
    
    context.beginPath();
    context.moveTo(player.x, player.y);
    context.lineTo(player.x + Math.cos(angle) * 50, player.y + Math.sin(angle) * 50);
    context.closePath();
    context.strokeStyle = 'green';
    context.stroke();
    
    context.beginPath();
    context.arc(player.x, player.y, 3, 0, Math.PI * 2);
    context.closePath();
    context.fillStyle = 'red';
    context.fill();
    
    context.beginPath();
    context.arc(target.x, target.y, 3, 0, Math.PI * 2);
    context.closePath();
    context.fillStyle = 'blue';
    context.fill();
    
    requestAnimationFrame(update);
};

const resize = () => {
    [canvas.width, canvas.height] = [innerWidth, innerHeight];
};

const init = () => {
    const gui = new dat.GUI();
    gui.add(player, 'speed', 0, 100);
    
    resize();
    
    requestAnimationFrame(update);
};

window.addEventListener('load', init);
window.addEventListener('resize', resize);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js