Recently, I created a , but for now, we’ll have to create our own (kind of).
But let’s begin at the beginning:
Who am I?
I’m Miki Stanger, a freelance front-end developer and architect with 15 years experience in the industry.
I like creating code that’s easy to maintain, and to work on both UX and the underlying stuff that makes the whole project work (so, for example, both perfecting the edge cases of a table behavior, and maybe implementing the underlying table logic myself).
You can learn more about me and what I do in my function, which returns a number at the range of [0, 1) (that’s a number between 0 and 1, including 0 and excluding 1).
Using such a number, we can get a random number of any range, by applying some math.
For example, given a minimum number m, a maximum number n, and a random number r in the range of [0, 1), you could get to a number of the range of [m, n) with the following formula:
m + r * (n - m)
So, to get a number between 1 and 6 (excluding 6), we’ll do:
1 + Math.random() * (6 - 1)
In order to get an integer, we’ll simply round down the result, thus removing the fraction part from the number. We’ll also add 1 to the multiplier of r, to include all numbers that are in the range of [m, m+1), which will be rounded down to m:
floor(m + r * (n - m + 1))
So in order to do a standard 1d6 (that’s a six-sided dice, or [1, 6] with integers only), we’ll do:
Math.floor(1 + Math.random() * (6 - 1 + 1))
(or simply Math.floor(1 + Math.random() * 6))
Ma, They Took My Random Number Generator
Back to CSS and it’s lack of a random number generator.
Because we don’t have that in CSS, we’ll have to create one of our own.
And because there’s no way I know to do that, we’ll have to create a pseudo-random number generator.
A pseudo-random number generator is a deterministic function that produces a sequence of numbers that seem random. Optimally, this number is within a specific range, so we could work it into any range we want.
CSS comes with some CSS function returns the sign of the number. You get -1 for a negative number, +1 for a positive number, 0 for zero (and -0 for a negative zero, which is .
What About the Non-Linear Distribution?
While squaring --index might help here, and while this might be visible mainly with a large number of random numbers with the same noise, I don’t have a true solution for this. However, for everything in my game - 80 stars, 12 buildings with 15 windows each, all affected by randomness - this was, again, good enough.
A New Experience Every Time
So far, we managed to generate random numbers. But these will remain the same random numbers every time you refresh, as --seed is a constant for the whole project, --index is a constant per element and the noise is a constant for every random number formula.
If we could change the --seed for every session, though, we’ll have a different experience every time - all of the random numbers will change.
In order to do that, I piggybacked on a very important part of the game - the “Start Game” button.
The HTML part looks something like this:
<body>
<div class="screen screen-main">
<!-- Title screen stuff -->
<div class="start-game-button-wrapper">
<input class="start-game-button" type="checkbox"/>
<input class="start-game-button" type="checkbox"/>
<input class="start-game-button" type="checkbox"/>
<input class="start-game-button" type="checkbox"/>
<input class="start-game-button" type="checkbox"/>
...
</div>
</div>
<div class="screen screen-game">
<!-- Game stuff -->
</div>
</body>
Then, in CSS, we do the following:
body {
.screen-game {
display: none;
}
/* If there's any checked start-game-button, start the game */
&:has(.start-game-button:checked) {
.screen-main {
display: none;
}
.screen-game {
display: block;
}
}
/* The start game button wrapper is a grid containing all of the checkboxes, the "Start Game" label etc. */
.start-game-button-wrapper {
display: grid;
grid-template-columns: repeat(1fr, 20);
height: 80px;
width: 200px;
&:after {
align-items: center;
content: "Start";
display: flex;
height: 80px;
justify-content: center;
pointer-events: none;
position: absolute;
width: 300px;
}
}
/* The checkbox is now a tiny square. Those squares fill the start game button wrapper. */
.start-game-button {
appearance: none; /* Hides the system checkbox UI */
background-color: #3355ff;
display: block;
height: 10px;
margin: 0;
width: 15px;
}
/* Checking a checkbox selects two base seed numbers according to the position of the element within the parent */
/* That's the first base seed: */
&:has(.start-game-button:checked:nth-child(20n + 1) {
--seed1: 4306301;
}
&:has(.start-game-button:checked:nth-child(20n + 2) {
--seed1: 5612987;
}
...
&:has(.start-game-button:checked:nth-child(20n + 19) {
--seed1: 1834393;
}
&:has(.start-game-button:checked:nth-child(20n) {
--seed1: 9229177;
}
/* That's the second base seed: */
&:has(.start-game-button:checked:nth-child(17n + 1) {
--seed2: 4306301;
}
&:has(.start-game-button:checked:nth-child(17n + 2) {
--seed2: 5612987;
}
...
&:has(.start-game-button:checked:nth-child(17n + 16) {
--seed2: 1834393;
}
&:has(.start-game-button:checked:nth-child(17n) {
--seed2: 9229177;
}
/* By having a seed generated from two base seeds like this, we add much more options by adding a relatively small amount of rules and numbers. */
--seed: calc(var(--seed1) + var(--seed2));
}
There's some stuff going on here, so let’s explain it:
The start button is made of many tiny checkboxes (designed to look like the button’s background). When you check one of those checkboxes, in addition to hiding the main screen and showing the game screen, the CSS variables --seed1 and --seed2 are set to two different numbers, which are added together to create -seed.
This means seed has 20 * 17 = 340 different options for a seed, which means that, depending on where the player clicked on the start button, they’ll get one of 340 different sets of random numbers.
To Sum It Up
Using a pseudo-random number generator - even with a single seed - opens up interesting options. You could randomize positions, colors - anything that can accept that calc function.
I hope you found this article as interesting as it was to me to write and implement it.
If you have any question, comments, ideas or ways to improve upon these ideas - please let me know in the comments.
SOCIAL SHARE CARD GENERATOR