I wanted to make a specific bit of UI to help me roll some dice, and it turned out quite interesting.

My requirements are:

  • Roll some standard D&D dice!
  • Make it as quick as possible — keyboard shortcuts preferred over mouse clicks
  • Be forgiving about the input, and allow correcting mistakes, or cancelling a roll

I've been using Improved Initiative, which has a shortcut for pressing d to roll some dice. It's quite good, and I'm undecided whether it's worth switching tabs, but I think my version is a lot quicker.

Unconventionally, I'm using the r key as a sort of modifier. You hold the r key to roll dice, and then you type the numbers you want. For example, r 2 4 would roll two d4s, aka four-sided dice. Try clicking in this iframe and rolling some dice.

This continues my trend of writing in quite a TDD style for my side projects. I don't love TDD for UI work, but here I'd identified a pure function to test: given a growing list of user inputs, what should the computed dice roll be?

I started this project using Deno, a lovely TypeScript runtime and growing competitor to Node. Deno includes a test suite, so I didn't have to configure Jest or anything.

My tests look like this (shown: about a third of them):

I wanted some nice shortcuts in there, like being able to type 00 for a d100, omitting the 1. But shortcuts do lead to more complex code, so the testing was important to make sure I hadn't broken anything.

I then built a fairly simple UI in React, using the Pantone Colour of the Year (of course). One interesting part about the UI was a new hook I wrote:

Generally, this UI was very reactive to keystrokes, which was somewhat tricky to manage in a declarative way. The useEffect pattern can sometimes lead to uselessly destroying and re-creating global event listeners, something I was keen to avoid. I used the useRefState hook for a boolean variable to track whether the UI was in the "rolling" state or not. This meant that the UI could reactively update, but the change was immediately and synchronously available for the keyDown/keyUp callback functions, and those callback functions could be maintained without concerns about stale state.

A future enhancement would be to roll combinations of dice, like 2d8 + 4d4. This goes along with the other missing feature, which is adding constant numbers to the dice rolls. Honestly though, that's not a feature I've ever loved — mental maths is part of the fun of D&D (possibly one of the nerdiest sentences ever, but I'm owning it). And this UI is quick enough to just roll the dice in succession without worrying about combos. Still, that might be the next challenge for me!

I'm having my first game with it tonight, so it'll be battle-tested very soon!