DEV Community

Cover image for Spin the Wheel — a customizable roulette wheel in less than 30kb with a no-javascript fallback.
Nathaniel
Nathaniel

Posted on

Spin the Wheel — a customizable roulette wheel in less than 30kb with a no-javascript fallback.

Here's the link: missingdice.com/spin-the-wheel. I'd really appreciate any feedback on improving it, i've been staring at it for a week!

I've been building a website of simple tools for boardgame players. For various reasons there are times when you need to roll dice, flip cards, or spin a spinner online.

The site is for simple things like that.

I want the site to be a success, so i first took a look at the competition and — for reasons I'll elaborate on in another post — decided that:

  • the site should be as accessible as possible
  • no page should be larger than 30kB
  • every tool should have a no javascript fallback

For this tool there we're some interestng hurdles to overcome:


Satisfying Click

It was important that the spinner have a satisfying clicking sound as it spun.

I found an mp3 of a click, but even at less than 1 second long, it weighed 7kB. Using it would have put me over the 30kB budget.

I'm sure there are lots of clever ways of reducing the file size of audio. But instead I chose to generate a click with JavaScript and the Web Audio API Something I hadn't even heard of before now.

Luckily for me, I know a synth enthusiast, and he explained some of the terminology to me.

I found this tutorial on synthesizing drum sounds and tweaked the hi-hat example to fit.

This ended up being just ~1.2kB of js - with room for further optimization.


Creating a no-javascript version

To make the spinner work without js was surprisingly simple.

If the browser has JavaScript disabled, instead of generating and spinning the wheel on the client-side, clicking spin the wheel submits a form…

Then the server:

  • builds a spinner with the user's custom values
  • randomly selects a winner
  • generates in advanced a css animation that spins the wheel
  • sends the html back to the client

It works surprisingly well.

I did this with Netlify Functions, so I'm not running an entire server for the tiny number of people who'll use the site without js.


Animating SVG

While animating SVG is generally fine, some browsers really struggle with it (Safari). After a lot of tinkering, it turns out the best fix is just to use seperate SVGs for each animated component, and put them each in their own <div> — then animate the <div>.


Timing the clicks

The spinner spins at different rates, durations, and with a random number of rotations — that way it stays surprising and dramatic.

For the spin to be really satisfying, it needed a little ticker on top. (like on the gameshow "wheel of fortune").

That meant having "pins" around the rim of the wheel, and animating a ticker each time it "hit" a "pin."

For performance reasons I thought it would be better to calculate the timings of the animation (and click sound) in advance.

This turns out to be a seriously complicated task, and after 3 days of learning calculus I gave up.

Instead it uses requestAnimationFrame and measures the current rotation of the spinner. If the rotation is between certain ranges it produces a click.

This works okay, but it does make mistakes.

It also means the no-javascript version has no ticker animation.


Spinning with 1000s of values

One issue was allowing people to add 1000s of values to the spinner.

I figured there is a use case where someone might want to paste an entire spreadsheet of values to have one picked at random.

So, I decided to use a <textarea> as the input, with a new line for each new value. Then, if the user pastes a comma-seperated list, it will reformat it before generating the wheel.

The big problem here is performance.

To make it work, the spinner <svg> gets less complex as more values are added.

  • The patterns are removed.
  • The number of pins on the rim of the wheel is capped at 60
  • The text paths become simpler.

I've only tested it on my fancy new computer, but it works fine up to around 6000 values. Feel free to test it out and let me know!


Things to Improve!

  • The overall look and feel could do with a polish — particularly on the alternate color-schemes.
  • The click sound could do with a tweak.
  • Finding an accurate way of measuring the click animation in advanced would be amazing.
  • Making custom wheels embeddable as an <iframe> would be cool.

Let me know what you think and what can be improved?

Top comments (10)

Collapse
 
georgewl profile image
George WL

Why did you decide on text paths, rather than using simple html elements with a transform: rotate applied?

Collapse
 
shadowfaxrodeo profile image
Nathaniel

That's a good question.

  • Firstly, becuase when it can, the text curves along with the edges of the wheel — not the simplest thing to do, but I felt like it looked nicer and was probably the expectation of the user. That's only possible with an svg <textPath>.

  • The text paths reuse some of the same functions used to draw the rest of the wheel.

  • I think there's a performance reason too. I haven't actually tested this, but my understanding is that the browser treats an inline svg in a similar way to a single image. So performing animations on it is less expensive to compute — i'd have to double check that.

  • The svg scales to fit the user's device. I suppose it is possible to set a font-size relative the parent's height, but that didn't occur to me at the time.

Collapse
 
georgewl profile image
George WL

Thanks for the detailed reasoning, I think you made the right decision.

Collapse
 
alvaromontoro profile image
Alvaro Montoro

This looks really cool.

Collapse
 
alvaromontoro profile image
Alvaro Montoro • Edited

I have "prefers reduce motion" activated and the animation is not there in that case (which is great). Maybe an improvement for that: there's no animation, so the selected option is visible immediately, but the banner and the button are disabled until the end of the animation, so there's a gap of a couple of seconds in which the user sees the selected option, but it is not announced and you cannot click on the button to generate a new one, which is a bit weird.

Collapse
 
shadowfaxrodeo profile image
Nathaniel

That's great to know. thank you. That's a javascript thing so I'll add something to detect it.

Interesting to come up with a non-spinning version that still has the same drama? Maybe a fade-in or something?

In the meantime I'll update it to show immediately.

Collapse
 
ujjwalgupta49 profile image
UjjwalGupta49

Superb work!
The animation feels much natural and the site helps me a lot.
Would love to have a look at the source code.

Collapse
 
souksyp profile image
Souk Syp.

Clever !

Collapse
 
tr11 profile image
Tiago Rangel

Your last thing to improve is a greeeeeat idea: I am looking for that!
Making custom wheels embeddable as an would be very useful!

Collapse
 
b000stman profile image
b00stman

Is there any possibility to take a peek at code responsible for generating audio? I have trouble with clipping sounds. Thanks! :)