JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Shooter 2D</title>
  <style>
    body {
      margin: 0;
      background: #1a1a1a;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    canvas {
      display: block;
      cursor: none;
    }
  </style>
</head>
<body>
  <canvas id="gameCanvas"></canvas>
  <script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');

    const MAP_SIZE = 600;
    canvas.width = MAP_SIZE;
    canvas.height = MAP_SIZE;

    // Grass offscreen
    const grassCanvas = document.createElement('canvas');
    grassCanvas.width = MAP_SIZE;
    grassCanvas.height = MAP_SIZE;
    const gCtx = grassCanvas.getContext('2d');
    function buildGrass() {
      gCtx.fillStyle = '#4a7c3f';
      gCtx.fillRect(0, 0, MAP_SIZE, MAP_SIZE);
      for (let i = 0; i < 800; i++) {
        const x = Math.random() * MAP_SIZE;
        const y = Math.random() * MAP_SIZE;
        gCtx.fillStyle = Math.random() > 0.5 ? '#3d6b34' : '#56a046';
        gCtx.fillRect(x, y, 3, 3);
      }
      gCtx.strokeStyle = '#2d4f25';
      gCtx.lineWidth = 4;
      gCtx.strokeRect(2, 2, MAP_SIZE - 4, MAP_SIZE - 4);
    }
    buildGrass();

    // Player
    const RADIUS = 20;
    const SPEED  = 3;
    const player = { x: MAP_SIZE / 2, y: MAP_SIZE / 2, angle: 0, hp: 5 };

    const keys  = {};
    const mouse = { x: MAP_SIZE / 2, y: MAP_SIZE / 2 };

    document.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
    document.addEventListener('keyup',   e => keys[e.key.toLowerCase()] = false);

    canvas.addEventListener('mousemove', e => {
      const rect = canvas.getBoundingClientRect();
      mouse.x = e.clientX - rect.left;
      mouse.y = e.clientY - rect.top;
      player.angle = Math.atan2(mouse.y - player.y, mouse.x - player.x);
    });

    // Bullets
   ...