canvas-spin-wheel
v0.2.0
Published
A tiny, dependency-free, canvas-based spin wheel widget for the browser. Weighted entries, realistic spin physics, TypeScript types included.
Maintainers
Readme
canvas-spin-wheel
A tiny, dependency-free, canvas-based spin wheel widget for the browser. Weighted entries, realistic spin physics, and full TypeScript types, in about 6 KB minified.
import { SpinWheel } from "canvas-spin-wheel";
const wheel = new SpinWheel(document.querySelector("canvas"), {
entries: [
{ text: "Pizza", color: "#3498db" },
{ text: "Burger", color: "#e74c3c" },
{ text: "Sushi", color: "#2ecc71" },
],
onSpinEnd: (winner) => console.log("Winner:", winner.text),
});
// Spins on click by default, or trigger it yourself:
wheel.spin();Install
npm install canvas-spin-wheelFeatures
- Zero runtime dependencies
- Weighted entries (a
weight: 3entry is 3x as likely, and takes a proportionally bigger slice) - Physics-based spin (air drag, bearing friction, peg drag) instead of a linear ease-out, so every spin feels a little different
- Cryptographically random winner selection (
crypto.getRandomValues, with aMath.randomfallback) - Automatic text color contrast per slice
- Full TypeScript types, ESM + CJS builds
API
new SpinWheel(canvas, options)
| Option | Type | Description |
| --- | --- | --- |
| entries | WheelEntry[] | Required. { text, color, weight? } |
| size | number | Rendered size in CSS px. Defaults to the canvas's own width. |
| fontFamily | string | CSS font stack for labels. |
| spinOnClick | boolean | Spin when the canvas is clicked. Default true. |
| pointerColor | string | Color of the fixed top pointer. Default #e11d48. |
| highlightColor | string | Fill flashed over the winning slice after landing. Default rgba(255, 255, 255, 0.35). |
| highlightDuration | number | How long the winning slice stays highlighted before onSpinEnd fires, in ms. Default 700. |
| physics | Partial<PhysicsConfig> | Override any spin-physics constant (air drag, bearing friction, turn count, ...). See PhysicsConfig in the types. |
| onSpinStart | () => void | Called when a spin begins. |
| onSpinEnd | (winner: WheelEntry, index: number) => void | Called after the winner is highlighted. |
| onSegmentCross | (index: number) => void | Called each time the pointer crosses a slice, useful for a tick sound. |
Instance methods
| Method | Description |
| --- | --- |
| wheel.spin() | Trigger a spin. No-op if already spinning or there are no entries. |
| wheel.setEntries(entries) | Replace the entries. Ignored mid-spin. |
| wheel.resize(size) | Resize the canvas (CSS px), accounting for device pixel ratio. |
| wheel.isSpinning() | Returns whether a spin is currently in progress. |
| wheel.getRotation() | Current wheel rotation, in radians. |
| wheel.getEntries() | The wheel's current entries. |
| wheel.destroy() | Cancel any in-flight spin and remove event listeners. Call on teardown. |
What this package doesn't do
This is the lite, single-file engine: colored slices, labels, and physics. It doesn't include image entries, background images, sound/confetti plugins, or a UI for building and sharing a wheel.
For the full editor (drag-and-drop entry list, images, sound and confetti, saving and sharing a wheel by link, and a library of ready-made wheels), see Wheel of Names, which this package's spin physics is extracted from.
License
MIT
