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)
Why did you decide on text paths, rather than using simple html elements with a
transform: rotate
applied?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.
Thanks for the detailed reasoning, I think you made the right decision.
This looks really cool.
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.
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.
Superb work!
The animation feels much natural and the site helps me a lot.
Would love to have a look at the source code.
Clever !
Your last thing to improve is a greeeeeat idea: I am looking for that!
Making custom wheels embeddable as an would be very useful!
Is there any possibility to take a peek at code responsible for generating audio? I have trouble with clipping sounds. Thanks! :)