How random is Math.random()?

Rohit Mondal
6 min readJul 7, 2021

--

Photo by Andrew Coop on Unsplash

I am an avid football fan. During the lockdown, when we couldn’t go out to play, I turned my attention towards playing football games- one of them being PES. There, we can unlock players in a lottery system. We won’t get them as we wish. It’s random. While trying to unlock one such pack, it hit my mind!!

What algorithm did the game developers use for their random player selection? Is it really a game of luck?

That’s when I decided to dive into the depths of understanding randomization. I was already familiar with using Math.random() in Javascript. I quickly realised:-

Math.random() is not really random.

Photo by krakenimages on Unsplash

Shocked? Let me break it down for you.

First things first:-

What is Math.random()?

The Math.random() function returns a floating-point, pseudo-random number in the range 0 to less than 1 (inclusive of 0, but not 1) with approximately uniform distribution over that range — which you can then scale to your desired range

However ECMAScript has some other expectations for randomness:-

It should return a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy.

Math.random() returns pseudo-random numbers only. Not random!

Let’s dive deep into the differences.

Random vs Pseudo random

Random:- A number selected from a range with equal chance of all numbers in the range being selected via an unpredictable method.

Pseudo random:- An approximation of a random number generated by software created by a biased or deterministic process. In layman terms, not really random.

The difference between true random number generators(TRNGs) and pseudo-random number generators(PRNGs) is that TRNGs use an unpredictable physical means to generate numbers (like atmospheric noise), and PRNGs use mathematical algorithms (completely computer-generated).

Implementation-dependent algorithm

This is the defining phrase that differentiates the two. There is a deterministic process that is followed and can be backtracked to find the source of the generation. An user-defined algorithm comes into play.

How is a pseudo-random number generated?

Photo by Mick Haupt on Unsplash

Pseudo-random number generators output a long, non-recurring sequence of numbers. The exact value of that sequence is determined by an initial starting value referred to as the “seed”. For any given seed the sequence is apparently random but known and recoverable so long as we know the position and the seed. Each implementation of Math.random() requires a seed and most seed from the current time or a source of OS randomness.

The quality of a PRNG also depends on something called its “period” — the number of iterations a PRNG goes through before it starts repeating itself. Not only does a PRNG with a long period seem more random to us humans but it’s also harder (i.e. more resource intensive) for a computer to crack / predict. However its not entirely uncrackable and that is why our favourite

Math.random() should not be used for security purposes like token generation.

because the answer to the question what PRNG does JavaScript use is none.

That is because JavaScript doesn’t decide how Math.random() gets implemented, your browser does.Math.random() does not accept a seed value from the user and even if it could, the actual generator varies across browsers. Chrome, Firefox, Safari each use different random number generators in accordance with the ECMAScript definition of randomness.

What is seed in Javascript?

Random seed is a method of initializing random number generators using an initial seed value. Random generators with the same seed will output the same pseudo-random results.

Most pseudo-random number generators (PRNGs) are build on algorithms involving some kind of recursive method starting from a base value that is determined by an input called the “seed”. The default PRNG in most statistical software is the Mersenne Twister algorithm MT19937, which is set out in Matsumoto and Nishimura (1998).

The purpose of the seed is to allow the user to “lock” the pseudo-random number generator, to allow replicable analysis.

Replicable analysis:- The goal of replication analysis is to assess how replication results relate to the original results and, possibly, other previous findings.

Some analysts like to set the seed using a true random-number generator (TRNG) which uses hardware inputs to generate an initial seed number, and then report this as a locked number. If the seed is set and reported by the original user then an auditor can repeat the analysis and obtain the same sequence of pseudo-random numbers as the original user. If the seed is not set then the algorithm will usually use some kind of default seed (e.g., from the system clock), and it will generally not be possible to replicate the randomisation.

The Math.random() function is not cryptographically secure

Why is Math.random() not designed to be cryptographically secure?

The JavaScript Math.random() function is designed to return a single IEEE floating point value n such that 0 ≤ n < 1. It is (or at least should be) widely known that the output is not cryptographically secure. Most modern implementations use the XorShift128+ algorithm which can be easily broken. As it is not at all uncommon for people to mistakenly use it when they need better randomness, why do browsers not replace it with a CSPRNG? It might be that XorShift128+ is faster than a CSPRNG, but on modern (and even not so modern) computers, it would be trivial to output hundreds of megabytes per second using ChaCha8 or AES-CTR. These are often fast enough that a well-optimized implementation may be bottlenecked only by the system's memory speed. Even an unoptimized implementation of ChaCha20 is extremely fast on all architectures, and ChaCha8 is more than twice as fast.

Several major browsers have had bug reports suggesting to replace this function with a cryptographically-secure alternative, but none of the suggested secure changes landed:

For the newbie developers, generating cryptographically secure numbers in javascript can be either in accordance with MDN that rightly points out:-

Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method.

Or, you can for instance use mouse movement as seed for random numbers, read out time and mouse position whenever the onmousemove event happens, feed that data to a whitening function and you will have some first class random at hand. Though do make sure that user has moved the mouse sufficiently before you use the data.

There are several Pseudo random number generators present on the Web. For starters, it would be reasonable to go through those codes and make certain tweaks in them for experimentation. There’s a long way to go and we are just starting out.

So, the answer to the question:-

How random is Math.random()?

Random enough for daily frontend tasks as long as they don’t pertain to security. When security comes into play, that’s a whole different ball game.

FAQs

  • Does Math.random() have a default seed of its own?

Ans:- NO.

There’s no seed because the underlying algorithm is up to the browser — if Math.random() did have a seed, the seeds would not be guaranteed to give the same results in different browsers.

  • Can we add a seed to Math.random()?

Ans:- Also, NO.

Thanks for reading!!

These are my opinions. You are always welcome to have your own and cross question me. After all, that’s the essence of learning!

<HappyLearning/>

--

--