Skip to content

Instantly share code, notes, and snippets.

@trohit
Created July 11, 2025 15:35
Show Gist options
  • Select an option

  • Save trohit/95ddfe84d91f6bc3744a777334bbc617 to your computer and use it in GitHub Desktop.

Select an option

Save trohit/95ddfe84d91f6bc3744a777334bbc617 to your computer and use it in GitHub Desktop.
simple javascript spinwheel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Spinning Wheel with URLs</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
canvas {
border-radius: 9999px;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col items-center py-10 px-4">
<h1 class="text-3xl font-bold mb-6">🎯 Spinning Wheel with Links</h1>
<div class="w-full max-w-xl">
<label class="block font-medium text-lg mb-1">Enter Items (comma-separated)</label>
<textarea id="entries" class="w-full p-3 rounded border border-gray-300 mb-4" rows="2">Apple,Banana,Cherry,Date,Egg,Fig</textarea>
<label class="block font-medium text-lg mb-1">Enter URLs (comma-separated)</label>
<textarea id="urls" class="w-full p-3 rounded border border-gray-300 mb-4" rows="2">https://apple.com,https://banana.com,https://cherry.com,https://date.com,https://egg.com,https://fig.com</textarea>
<button onclick="setupWheel()" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Set Entries</button>
</div>
<canvas id="wheel" width="400" height="400" class="my-6 shadow-xl"></canvas>
<button id="spin-btn" onclick="spin()" class="bg-green-500 text-white px-6 py-3 rounded-full text-lg hover:bg-green-600">🎲 Spin</button>
<div id="result" class="text-xl font-semibold mt-4"></div>
<script>
const canvas = document.getElementById("wheel");
const ctx = canvas.getContext("2d");
const resultEl = document.getElementById("result");
let entries = [];
let urls = [];
let startAngle = 0;
let spinning = false;
let spinAngle = 0;
function setupWheel() {
const entryInput = document.getElementById("entries").value;
const urlInput = document.getElementById("urls").value;
entries = entryInput.split(",").map(e => e.trim()).filter(Boolean);
urls = urlInput.split(",").map(u => u.trim()).filter(Boolean);
if (entries.length < 2 || urls.length !== entries.length) {
alert("Please ensure both entries and URLs are provided and match in count.");
return;
}
drawWheel();
}
function drawWheel(highlightIndex = null) {
const radius = canvas.width / 2;
const arc = 2 * Math.PI / entries.length;
ctx.clearRect(0, 0, canvas.width, canvas.height);
entries.forEach((item, i) => {
const angle = startAngle + i * arc;
ctx.beginPath();
ctx.fillStyle = (i === highlightIndex) ? "#FFD700" : `hsl(${i * 360 / entries.length}, 70%, 60%)`;
ctx.moveTo(radius, radius);
ctx.arc(radius, radius, radius, angle, angle + arc);
ctx.fill();
// Add labels
ctx.save();
ctx.translate(radius, radius);
ctx.rotate(angle + arc / 2);
ctx.textAlign = "right";
ctx.fillStyle = "#000";
ctx.font = "16px sans-serif";
ctx.fillText(item, radius - 10, 5);
ctx.restore();
});
// Draw center circle
ctx.beginPath();
ctx.arc(radius, radius, 20, 0, 2 * Math.PI);
ctx.fillStyle = "#fff";
ctx.fill();
// Draw pointer
ctx.beginPath();
ctx.moveTo(radius, 0);
ctx.lineTo(radius - 10, 20);
ctx.lineTo(radius + 10, 20);
ctx.closePath();
ctx.fillStyle = "#111";
ctx.fill();
}
function spin() {
if (spinning || entries.length < 2) return;
spinning = true;
resultEl.textContent = "";
const duration = 4000;
const extraSpins = 5 * 2 * Math.PI;
const finalAngle = Math.random() * 2 * Math.PI;
const target = extraSpins + finalAngle;
const start = performance.now();
function animate(time) {
const elapsed = time - start;
const progress = Math.min(elapsed / duration, 1);
spinAngle = easeOutCubic(progress) * target;
startAngle = spinAngle % (2 * Math.PI);
drawWheel();
if (progress < 1) {
requestAnimationFrame(animate);
} else {
const index = getSelectedIndex();
const selected = entries[index];
const url = urls[index];
drawWheel(index);
resultEl.innerHTML = `
🎉 You got: <b>${selected}</b><br>
<button id="go-btn" class="mt-2 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Take me there</button>
`;
spinning = false;
document.getElementById("go-btn").onclick = () => {
window.open(url, "_blank");
};
}
}
requestAnimationFrame(animate);
}
// FIXED getSelectedIndex
/*
function getSelectedIndex() {
const numberOfSegments = entries.length;
const arc = 2 * Math.PI / numberOfSegments;
// Adjust angle by subtracting Pi (180 degrees) to fix pointer offset
let angle = (2 * Math.PI - startAngle + Math.PI / 2 - Math.PI) % (2 * Math.PI);
let index = Math.floor(angle / arc) % numberOfSegments;
return index;
}
*/
function getSelectedIndex() {
const numberOfSegments = entries.length;
const arc = 2 * Math.PI / numberOfSegments;
// Adjust angle for pointer offset, then subtract a tiny epsilon
let angle = (2 * Math.PI - startAngle + Math.PI / 2 - Math.PI - 1e-10) % (2 * Math.PI);
let index = Math.floor(angle / arc) % numberOfSegments;
// Ensure index is always valid
if (index < 0) index += numberOfSegments;
return index;
}
function easeOutCubic(t) {
return 1 - Math.pow(1 - t, 3);
}
setupWheel(); // Init
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment