Skip to content
animorio
IT
All components

The prize wheel

Games, Physics, SVGGSAPAdvancedCompressed weight: Demo 6.8 KBGSAP 28 KBFreeDownload the zip

A trattoria's prize wheel: it is flicked with a finger or sent by the button and then goes on its own, slowing down by friction; every peg that passes knocks the flapper and takes a little of its speed, and if it stops on a peg it settles into a slice. The slice that wins comes out a little, the others go pale and strips of paper fly.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The prize wheel</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fredoka@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the prize wheel: flicked with a finger or sent round by Spin, it slows down on its own, every peg knocks the
         flapper, and where it stops the slice comes out and the paper flies -->
    <section class="game" aria-labelledby="game-title" data-game data-autoclic>
      <h2 class="game__title" id="game-title">Spin for a treat</h2>
      <div class="wheel" data-wheel-box>
        <svg class="wheel__disc" viewBox="-100 -100 200 200" role="img" aria-label="The prize wheel" data-wheel></svg>
        <svg class="wheel__flap" viewBox="-12 -14 24 44" aria-hidden="true" data-flap>
          <path d="M-7-8h14v4c0 7-3.5 15-7 26-3.5-11-7-19-7-26z" fill="#d63b2f"/>
          <path d="M-3.6-4v7" stroke="rgba(255,255,255,0.5)" stroke-width="2" stroke-linecap="round"/>
          <rect x="-11" y="-14" width="22" height="8" rx="3" fill="#3a2a22"/>
        </svg>
      </div>
      <button class="spin" type="button" data-spin><span>Spin</span></button>
      <div class="result" aria-live="polite" data-result>
        <p class="result__say" data-say>Drag the wheel or press Spin</p>
        <p class="result__prize" data-prize></p>
      </div>
    </section>
  </main>
  <div class="paper" aria-hidden="true" data-paper></div>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

45 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The prize wheel” from Animorio and fit it into my site

What it does

On a white background Spin for a treat, a wooden wheel with eight coloured slices (Coffee, 10% off, Dessert, Try again, Wine, 5% off, Starter, Try again), golden pegs on the rim and at the top a red flapper hanging from a bracket; under it Spin and a line that explains. The wheel is grabbed with a finger or the mouse and turns with the hand round its middle; let go with a flick it carries on by itself, and Spin sends it at a random speed. It slows down as it turns, and every peg that passes under the flapper pushes it aside: the flapper knocks, swings on its spring and brakes the wheel a little, so at the end it clicks from one slice to the next slower and slower. If it stops with a peg against the flapper, the wheel settles into a slice. Then the slice that won comes out from the middle with a bounce, the others go pale, You won appears below and the prize pops out (A free starter) while strips of paper burst from the flapper and fall turning; on Try again the slice trembles and the line says So close. Spin again. A flick too weak turns the wheel all the same but does not count: Give it a real spin.

How it works

The drawn wheel

The wheel is an SVG with its origin in the middle: the slices are wedges of a circle, the pegs sit on the borders, the words read from the middle outwards on the right half and from the rim inwards on the left half, so they are never upside down. Under the slices there is a pale disc: a slice gone dim turns pale instead of dark.

The physics

The wheel has an angle and a speed; every second it loses a steady brake plus a share of its speed (FRICTION), worked out on GSAP's ticker in steps of 4 milliseconds. The number of the peg under the flapper is floor((angle − half a slice) / slice): when it changes, a peg has passed (at speed even more than one in a step).

The flapper

The flapper is a spring: each peg gives it a push the way the wheel is going, stronger the faster the wheel runs, and takes PEG degrees a second from the wheel. Kept within 40 degrees, it bounces back if it touches them. When the wheel stops with a peg less than 2.5 degrees from the flapper, a tween takes it into the slice.

The hand

Grabbed, the wheel follows the pointer's angle round its middle (atan2), counting the turns without jumps. Let go, it takes the speed of the last tenth of a second; a hand that stopped before letting go gives no speed, and under 300 degrees a second the flick is too weak and does not count.

The prize, the paper and the preview

The slice that wins is the one with its middle under the flapper: it grows from the middle (svgOrigin 0 0) with back.out, the others go down to 0.35 opacity and the prize pops out with elastic.out. The paper is CONFETTI strips with gravity and air drag that spin and show their face (scaleY with the cosine) before fading. A click from a script (the library's preview) is a pretend hand that sends the wheel round, when it has stood still for a while.

Values to change

NameFileDefaultWhat it changes
SLICESscript.js[['Coffee', 'A free coffee', '#e4683f', '#ffffff'], …]The slices: the word on the wheel, the prize (empty: no prize), the colour and the colour of the word.
FRICTIONscript.js[45, 0.5]How the wheel slows down: a steady brake in degrees a second, and a share of its speed each second.
PEGscript.js6The degrees a second each peg takes from the wheel as it knocks the flapper.
SPINscript.js[900, 1500]The speed Spin gives, at random between these two, in degrees a second.
CONFETTIscript.js28How many strips of paper fly out for a prize.

Try them in the Playground

When to use it

For a restaurant's site or till, a shop's promotion, an event, a newsletter with a prize, a party: any draw that should feel like a real wheel.

On phones

The wheel is at most 340 pixels wide and fits the screen; the finger grabs it and turns it without scrolling the page (touch-action none on the wheel), and a quick flick sends it.

With reduced motion

Spin puts the wheel straight on a random slice, inside it, and the prize appears without pops or paper; the hand still turns the wheel, but let go it stops.

Pitfalls

  • The wheel and the flapper are written by hand every frame (style.transform): a GSAP tween on the same element would fight them, so the entrance scales the box that holds them.
  • The flick's speed must be taken from the last tenth of a second, and a hand that stopped before letting go must not send anything.
  • At speed several pegs can pass in one step: they all have to be counted, or the flapper stays still just when the wheel runs.
  • Slices dimmed with opacity over a dark background turn muddy: a pale disc is needed under them.
  • A wheel stopped exactly on a peg has no clear winner: it has to be settled into a slice, as the real flapper does.

Libraries

GSAP 3.15.0, Fredoka (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 6.8 KB; the libraries: GSAP 28 KB.

Testing

Tested on 3 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All components