It was the Eurovision final last week, and I wanted to try an idea I had been toying with for a few years already: a generative bingo card.


There are websites that allow you to create bingo cards, but they're loaded with advertisements and trackers, and are heavy and not very nice to play with, in general. Other websites let you download one (ONE!) PDF file for you to... PRINT! What is this, the 20th century?

I knew we could do better, so I made a small web game that shows you a bingo card. You can tick off options as you watch, and eventually you get to a line, maybe even to a BINGO!

There are no rewards other than the sheer pleasure of ticking things off. And making an old idea a reality!

I had lots of fun building this, it's been a while since I had built something webby just for the sake of it and in such a short period of time—a few hours on Friday.

The pros (AKA sole is very smug)

There are several aspects I'm pleased with:

It's built with TDD

In the past when I have built games it has always ended up being a bit tedious to change things as the complexity increased, but by using this approach it made it much easier and faster to modify the code as I added features, without fearing that I'd break something. So if someone tells you that TDD will make you slow: maybe no, if you know what you're doing.

My combination was: vitest as unit tests runner, and playwright for the end to end tests.

I started writing the business logic, and while I am doing that I have vitest running in watch mode. I literally start by describing a test that is going to fail, because either the class is not implemented yet, or the function, etc, or I wrote additional expectations and the code does not satisfy all of them anymore. Then I write the logic that makes the test pass. This does force you to be very clear on what you're doing i.e. "what does it mean to pass this test?", and does focus you on doing just one thing at a time (which brings you a level of clarity that helps you do more things rather than getting side tracked by "ideas"). Helpful when the deadline for the project you started today is... today.

Once the logic enables me to write UI code, and I write sufficient UI that can be repeatedly interacted with, I will quickly write e2e tests to describe the thing that I am performing repeatedly, so that the computer runs the things over and over, not me.

Since the "game" in this case is quite simple, there aren't many e2e tests, but they are already very helpful for testing things that require the user to click repeatedly. For example, you need to 'win' by clicking 15 times on the right place on a card. So the time advantage is there, as no matter how quick you are, you'll never click something 15 times fast enough to beat an automated test runner, and that's how TDD can tangibly save you time. You get to automate user interactions AND you get to notice if something you changed broke something else, before it's too late or complex to fix!

It works well on a desktop and on a phone

A careful application of the classic: build it for a small viewport and then make it take more space in bigger viewports guarantees all but success. Yay!

I knew I was not going to be watching the final in front of my computer, but in the sofa with my partner. And we both were using our phones to play the game (sometimes screaming at the acts when they were not delivering the missing tick for a winning bingo card: why are you not on a wheel!? It's the only one I'm missing!!!...).

There is more padding and more shadows in a bigger viewport, but you can still see the content on a phone without installing a separate 'app'.

It's keyboard navigable

By making judicious use of native HTML elements (label and input[type=checkbox] and dialog), the autofocus attribute and the focus method you could play this over and over without actually clicking on anything—just tapping on your keyboard.

This in theory should make it accessible if you have a visual impairment, but I haven't been able to test it: I have been trying to get VoiceOver to cooperate with me, but it has been proving really temperamental and not wanting to work, so if you test it out and wish to tell me how it went, I'd be happy to hear about it!

It's incredibly small

The build size is only 22kb (and it's not even fully optimised, e.g. the CSS is not compressed because I did not use minifiers, etc). Compare that with most phone apps which start at... MEGAbyte size... 🙄 ... sigh.

Of course, it helps that I am not using any framework to implement this, just good old HTML, CSS and JavaScript. And I'm not crowbaring a CSS 'library' just to align things on the screen; instead I used the right properties with the right values in the right places, thanks to the ever-winning combination of 'reading the documentation and using the devtools to play with values when unsure' (would you believe it!?).

A cool side effect of this being so small is that at no point did I lose the tab on my device. This happens 80% of times when I come back to a tab on my phone after locking the screen, I guess because the tab content took up too much memory and was kicked out of the phone when inactive. This meant that we did not lose the progress of our cards, even when we got to tick the Boring act (toilet break) box, and I did not need to implement persistence via local storage or something. It would not have been hard, but I did not feel like doing it!

Yes, I'm smug.

The cons

Schrödinger's game

This is not a "fully featured" game if by that you mean score keeping or history, or anything of the sort. Either you win or you don't—so in that regard it is 'feature complete'.

Sometimes, you just can't win...

The biggest issue I found is that because I hadn't tested it exhaustively with real users and data, I didn't realise how sometimes you just can't win with the card you got.

For example, if the randomiser picked options that were hard to achieve at that point in the night, and you didn't realise and didn't request a new card, then you would not be able to tick everything off. This is why, in real bingo halls, people play with more than one card at once. Or so I guess, because I've never been at a bingo hall. But the internet told me!

One solution could be to allow playing with more than one card at once. I am not sure as to what the UI would look like. Maybe with swiping between cards? Tabbing? I haven't quite thought about it; I find it would be much easier on a tablet that on a phone, but maybe doable either way.

Another one which I just vaguely formulated in my brain, because it requires a bit more of nuance, would be to break down the possible pool of options into two groups, such as frequent features that are present in most of the songs (e.g. key change, fire, strobe), and then infrequent features such as "someone falls", and then make sure that at least one line in a card is made up of options in the frequent features. So people would always at least get one line.

But then it would not be that random anymore... and yet, since the goal of the game is not to "defeat the bank", but to have fun, it would not be that much of a problem.

I just didn't implement either.

Ooops, reload

Another thing that became evident with the real user testing was that it's possible to accidentally reload the game by accidentally dragging (and triggering the pull to reload) rather than scrolling. And this gets you a new card and you lose all of your progress!

This could be fixed if I implemented persistence, heh!

Responsiveness varies and I have no clue why

And with that I mean the fact that the time elapsed between tapping on the screen and something happening fluctuates widely between devices and browsers. For example, there was a substantial delay on Firefox on iOS on an iPhone Pro 11, but then no delay whatsoever in Safari on an iPhone 11 (not pro). Do you understand this? Me neither: my understanding is that it is the same rendering engine in both cases (Safari).

I don't have any Android device handy so I could not test this there. I hope it does, because ✨it's the Web!✨

What are you doing reading this?

Play the game!