are-there-random-numbers-in-css?

CSS allows you to create dynamic layouts and interfaces on the web, but as a language, it is static: once a value is set, it cannot be changed. The idea of randomness is off the table. Generating random numbers at runtime is the territory of JavaScript, not so much CSS. Or is it? If we factor in a little user interaction, we actually can generate some degree of randomness in CSS. Let’s take a look!

Randomization from other languages

There are ways to get some “dynamic randomization” using CSS variables as Robin Rendle explains in an article on CSS-Tricks. But these solutions are not 100% CSS, as they require JavaScript to update the CSS variable with the new random value.

We can use preprocessors such as Sass or Less to generate random values, but once the CSS code is compiled and exported, the values are fixed and the randomness is lost. As Jake Albaugh explains:

random in sass is like randomly choosing the name of a main character in a story. it’s only random when written. it doesn’t change.

— jake albaugh (@jake_albaugh) December 29, 2016

Why do I care about random values in CSS?

In the past, I’ve developed simple CSS-only apps such as a trivia game, a Simon game, and a magic trick. But I wanted to do something a little bit more complicated. I’ll leave a discussion about the validity, utility, or practicality of creating these CSS-only snippets for a later time.

Based on the premise that some board games could be represented as Finite State Machines (FSM), they could be represented using HTML and CSS. So I started developing a game of Snakes and Ladders (aka Chutes and Ladders). It is a simple game. The goal is to advance a pawn from the beginning to the end of the board by avoiding the snakes and trying to go up the ladders.

The project seemed feasible, but there was something that I was missing: rolling dice!

The roll of dice (along with the flip of a coin) are universally recognized for randomization. You roll the dice or flip the coin, and you get an unknown value each time.

Simulating a random dice roll

I was going to superimpose layers with labels, and use CSS animations to “rotate” and exchange which layer was on top. Something like this:

Simulation of how the layers animate on a browser

The code to mimic this randomization is not excessively complicated and can be achieved with an animation and different animation delays:

/* The highest z-index is the numbers of sides in the dice */ 
@keyframes changeOrder {
  from { z-index: 6; } 
  to { z-index: 1; } 
} 

/* All the labels overlap by using absolute positioning */ 
label { 
  animation: changeOrder 3s infinite linear;
  background: #ddd;
  cursor: pointer;
  display: block;
  left: 1rem;
  padding: 1rem;
  position: absolute;
  top: 1rem; 
  user-select: none;
} 
    
/* Negative delay so all parts of the animation are in motion */ 
label:nth-of-type(1) { animation-delay: -0.0s; } 
label:nth-of-type(2) { animation-delay: -0.5s; } 
label:nth-of-type(3) { animation-delay: -1.0s; } 
label:nth-of-type(4) { animation-delay: -1.5s; } 
label:nth-of-type(5) { animation-delay: -2.0s; } 
label:nth-of-type(6) { animation-delay: -2.5s; }

The animation has been slowed down to allow easier interaction (but still fast enough to see the roadblock explained below). The pseudo-randomness is clearer, too.

See the Pen


Demo of pseudo-randomly generated number with CSS
by Alvaro Montoro (@alvaromontoro)


on CodePen.

But then I hit a roadblock: I was getting random numbers, but sometimes, even when I was clicking on my “dice,” it was not returning any value.

I tried increasing the times in the animation, and that seemed to help a bit, but I was still having some unexpected values.

That’s when I did what most developers do when they find a roadblock they cannot resolve just by searching online: I asked other developers for help in the form of a StackOverflow question.

Luckily for me, the always resourceful Temani Afif came up with an explanation and a solution.

To simplify a little, the problem was that the browser only triggers the click/press event when the element that is active on mouse down is the same element that is active on mouse up.

Because of the rotating animation, the top label on mouse down was not the top label on mouse up, unless I did it fast or slow enough for the animation to circle around. That’s why increasing the animation times hid these issues.

The solution was to apply a position of “static” to break the stacking context, and use a pseudo-element like ::before or ::after with a higher z-index to occupy its place. This way, the active label would always be on top when the mouse went up.

/* The active tag will be static and moved out of the window */ 
label:active {
  margin-left: 200%;
  position: static;
}

/* A pseudo-element of the label occupies all the space with a higher z-index */
label:active::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  z-index: 10;
}

Here is the code with the solution with a faster animation time:

See the Pen


Demo of pseudo-randomly generated number with CSS
by Alvaro Montoro (@alvaromontoro)


on CodePen.

After making this change, the one thing left was to create a small interface to draw a fake dice to click, and the CSS Snakes and Ladders was completed.

This technique has some obvious inconveniences

  • It requires user input: a label must be clicked to trigger the “random number generation.”
  • It doesn’t scale well: it works great with small sets of values, but it is a pain for large ranges.
  • It’s not really random, but pseudo-random: a computer could easily detect which value would be generated in each moment.

But on the other hand, it is 100% CSS (no need for preprocessors or other external helpers) and, for a human user, it can look 100% random.

And talking about hands… This method can be used not only for random numbers but for random anything. In this case, we used it to “randomly” pick the computer choice in a Rock-Paper-Scissors game:

See the Pen


CSS Rock-Paper-Scissors
by Alvaro Montoro (@alvaromontoro)


on CodePen.

predictably-random

The concept of random is pretty interesting when you think about it from a computer’s point of view, because without some external input, random is impossible.

Computers (hardware and software) are deterministic. Meaning you give some input and get an output, and if you repeat the input, you’ll get the same output. Random numbers are, from a philosophical stand point, indeterministic.

So random functions in software, like Math.random() starts with a seed value and roughly speaking, the result of the random function call is used as the next seed, and the next and so on. This means that computers can generate a random sequence of values but to start it needs a seed. A good candidate for a seed is the current time in millisecond for instance.

In “most” cases, these pseudo random values are fine, though if you want to do some in depth reading on random number generation in V8 there’s a great article on Medium entitled TIFU by using Math.random() (archived copy).

Random for games

Lately I’ve been playing with my own Tetris clone and added an (undocumented) two player option. When the game plays, the tetrominos are selected “at random”. However in a two player game both players should get the same “random” sequence of blocks.

Using Math.random() won’t fly because I can’t control the seed value. In this instance, I have to write my own (or copy tried and tested) random function. This way I am to set the seed value when both players join.

Additionally to this, I’ve created a game that creates a random board layout and if the player decides to publish their score, the software can take their starting seed value and their sequence of moves to validate their score is legitimate.

Let’s write our own random function

RFC 1149.5 specifies 4 as the standard IEEE-vetted random number via xkcd

A bit of searching on the web will turn up a number of different algorithms for a random number. My problem is that I’m not entirely sure I a) understand the given code and b) trust the code actually does what it says it does.

For example, here’s three functions I found:

let seed = Date.now();

function randomA() {
  seed = (5 * seed   3) % 16;
  return seed / 16;
}

function randomB() {
  seed = (seed * 7919   1) & 0xffff;
  
  return seed / 0xffff;
}

let ctr = 0;
function randomC(n = 7) {
  ctr  ;
  let value = ((((seed >> 9) & 1) ^ ((seed >> 1) & 1)) << 15) | (seed >> 1);

  seed = value;
  value >>= 8; 
  value  = ctr;
  value %= n;

  return value / n;
}

I’d generally gravitate towards the more complicated looking function, but that’s based entirely on looks. Reading these functions I can’t quite imagine what they’re doing and running them in a browser won’t really confirm whether their numbers are random (enough) even if I ran the functions over and over and over.

Quite honestly these functions look like they’re mooshing together numbers in the way you might moosh together Opal Fruits and end up with some colour that doesn’t resemble anything.

What’s useful here is a visualisation of noise distribution the functions make. You might have seen these before:

random noise

What I’m looking for in these images is any visual patterns, and patterns are bad. This is an example of a “bad” random number generator – since there’s a pattern, the sequence can be predicted:

bad random noise

Originally I found some python code that generates these kinds of images, but I figured with HTML5-like tech, it should be viable to run in the browser and even generate the random function code on the fly to get a good impression if my algorithm works for my purposes.

So sketched out some code and came up with this mini tool: https://random.isthe.link

This way I can instantly see the effects of the three functions:

I’m sure there’s better functions out there, but the randomC is “good enough” for my particular needs. Though interestingly it works well for the 7 values I need, but when the range of values goes up, to 7777 or even