assistant: HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>Ruleta de premios</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <h1>Ruleta de premios</h1>
    <form>
      <label for="csv-file">Cargar participantes:</label>
      <input type="file" id="csv-file">
    </form>
    <canvas id="canvas" width="400" height="400"></canvas>
    <button id="spin-button">Girar la ruleta</button>
    <script src="ruleta.js" type="module"></script>
  </body>
</html>
JavaScript (ruleta.js):

// Variables globales
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
let startAngle = 0;
const arc = Math.PI / 4;
let spinTimeout = null;
let spinArcStart = 10;
let spinTime = 0;
let spinTimeTotal = 0;
let participants = [];

// Función para cargar los participantes desde un archivo CSV
function loadParticipants(file) {
  const reader = new FileReader();
  reader.onload = function(event) {
    const csv = event.target.result;
    const lines = csv.split("\n");
    for (let i = 0; i < lines.length; i++) {
      const data = lines[i].split(",");
      participants.push(data[0]);
    }
  };
  reader.readAsText(file);
}

// Función para dibujar la ruleta
function drawRouletteWheel() {
  const colors = ["#FF0000", "#FFA500", "#FFFF00", "#008000", "#0000FF", "#4B0082", "#9400D3"];
  let colorIndex = 0;
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.strokeStyle = "#000000";
  context.lineWidth = 2;
  context.font = "bold 12px Arial";
  for (let i = 0; i < participants.length; i++) {
    const angle = startAngle + i * arc;
    context.fillStyle = colors[colorIndex];
    colorIndex = (colorIndex + 1) % colors.length;
    context.beginPath();
    context.arc(200, 200, 100, angle, angle + arc, false);
    context.lineTo(200, 200);
    context.closePath();
    context.fill();
    context.stroke();
    context.save();
    context.translate(200, 200);
    context.rotate(angle + arc / 2);
    context.fillStyle = "#000000";
    context.fillText(participants[i], 75, 0);
    context.restore();
  }
}

// Función para girar la ruleta
function spinRouletteWheel() {
  spinTime += 30;
  if (spinTime >= spinTimeTotal) {
    stopRotateWheel();
    return;
  }
  const spinAngle = spinArcStart - easeOut(spinTime, 0, spinArcStart, spinTimeTotal);
  startAngle += (spinAngle * Math.PI / 180);
  drawRouletteWheel();
  spinTimeout = setTimeout(spinRouletteWheel, 30);
}

// Función para detener la rotación de la ruleta
function stopRotateWheel() {
  clearTimeout(spinTimeout);
  const degrees = startAngle * 180 / Math.PI + 90;
  const arcd = arc * 180 / Math.PI;
  const index = Math.floor((360 - degrees % 360) / arcd);
  context.save();
  context.font = "bold 20px Arial";
  context.fillText(participants[index] + " ha ganado!", 200 - context.measureText(participants[index] + " ha ganado!").width / 2, 350);
  context.restore();
}

// Función para calcular la función de aceleración
function easeOut(t, b, c, d) {
  const ts = (t /= d) * t;
  const tc = ts * t;
  return b + c * (tc + -3 * ts + 3 * t);
}

// Evento para cargar el archivo CSV y dibujar la ruleta
document.getElementById("csv-file").addEventListener("change", event => {
  const file = event.target.files[0];
  loadParticipants(file);
  setTimeout(() => {
    drawRouletteWheel();
  }, 1000);
});

// Evento para girar la ruleta
document.getElementById("spin-button").addEventListener("click", () => {
  spinTime = 0;
  spinTimeTotal = Math.random() * 3000 + 5000;
  spinRouletteWheel();
});