
JavaScript's Math.random()
Back in 2014, I discussed how we can access random elements in an array through JavaScript, by making use of the Math.random() function. This function returns a random number between 0 and 1, and we can use that as the basis (or 'seed') to access a random index in our arrays (you can read this post here).
I've recently been implementing a lot of randomisation into one of my projects, which got me thinking: just how random is Math.random()? How does it work?
How Random is Random?
As you are probably well aware, when it comes to computing, we are ultimately talking about maths. On the absolute most rudimentary level, computers take an input, math it up, and then return the result. So, how does that affect our concept of "random", then? Given that it's all just an input with some logic applied, how random can it really be?
If we want to be really specific about it; because computers use an algorithm that in turn uses mathematical formulas to generate 'random' numbers, we aren't talking about a true Random Number Generator (RNG) at all, but more actually: a Pseudo‑Random Number Generator (PRNG) instead.
There are a few services out there that you can use if you want to generate truly random values rather than using PRNGs. One such service is random.org which uses atmospheric noise instead of PRNGs to produce random numbers. The results of using an RNG are much more randomised, are not periodic (this is a measure of how many times you would have to run an RNG or PRNG before they inevitably start repeating themselves and showing patterns), and are much better suited to things like lotteries, gambling or encryption algorithms (due to their unpredictable nature).
However, the biggest downside of RNGs is that they are noticeably less efficient. Whilst normal PRNGs employed by computers take fractions of fractions of a second to generate a randomised result, TRNGs (True Random Number Generators) like those found on random.org take time. This makes them much less useful for things like simulations, or for use in the kind of work most developers and specifically web developers will be running into on a day‑to‑day basis.
The Mystery of JavaScript's Prng
So, we know that computers use algorithms in PRNGs to create the illusion of randomness. Different languages use different algorithms and PRNGs. So what does JavaScript use? This is where it gets really interesting; JavaScript doesn't have a PRNG built‑in.
What? How is that possible? How can Math.random() work without JavaScript having any way to generate those random numbers?
Math.random() uses the PRNG in your browser to generate its answers, rather than anything within the language itself. It is totally up to your browser to determine the on‑the‑ground details of how this function should work, meaning (at least before 2015, which we'll get into in just a second) that the same Math.random() function could easily return more‑ or less‑random results depending on how those browsers were built, or which browser was being used to access your site at the time!
2015: Unifying the Browsers
The reason that I mentioned 2015, in particular, earlier is because of a fairly significant change that happened across all major browsers during that year. Before 2015, Firefox was using a "homebrew" solution, WebKit was using "GameRand", Chrome was using its own thing built using V8, and nobody seems to really have consensus on what IE was using, although that's not really a big surprise...
However, between 2015 and 2016, all of these big‑name browsers and engines ‑ as well as smaller ones like Opera ‑ switched over to the same PRNG; xorshift128+.
Xorshift128+
The mass switchover to xorshift128+ was a good move. Xorshift is a faster, more efficient PRNG than all of the previous ones that were in use by the mainstream browsers (although it should still definitely not be used for cryptography or security if you can help it).
To understand generally how it works, we're going to have to get uncomfortably close to bits and binary; something that ‑ as web developers ‑ we don't tend to have to think about all that often.
According to the V8 dev blog, the algorithm looks a little like this:
uint64_t state0 = 1;uint64_t state1 = 2;uint64_t xorshift128plus() { uint64_t s1 = state0; uint64_t s0 = state1; state0 = s0; s1 ^= s1 << 23; s1 ^= s1 >> 17; s1 ^= s0; s1 ^= s0 >> 26; state1 = s1; return state0 + state1;}So, as a developer this shouldn't look totally alien to you; I'm sure you have seen variable assignments, functions and return statements before, and you're probably also familiar with binary logical operators like the double ampersand. However, the operators that look like binary logical operators here (the >> and <<) are actually serving an entirely different purpose.
Bitwise Operators
The << and >> operators here are called bitwise operators. Specifically, they are the "left‑shift" and "right‑shift" operators (hopefully it's pretty obvious which one is which!).
Basically, they are taking a number, changing it into binary, and shifting the bits in the direction the arrow is pointing by however many places they are told to. For example, 17 << 9 tells the program to take the binary representation of 17, and shift each bit to the left by 9 places. This results in a drastically different number than the original. In fact, running 17 << 9 results in that 17 becoming 8704!
However, there is another step at play here ‑ the ^= operator. The ^= operator is called the exclusive or assignment operator (also known as the XOR assignment). To illustrate how this works, we'll drop the equals sign and just use it as the Xor operator on its own. Here's what's going on under the hood: 24 ^ 11 tells the program to take the binary representations of both 24 and 11, and compare the positions of the bits. Where the bits match, Xor outputs a 0. Where they don't, it outputs a 1. This results in an entirely new number. For instance, with the code 24 ^ 11, we would end up with the number 19.
Now that we know a little more about what's going on, let's break down a single line. The line, s1 ^= s1 >> 17; takes the value of s1 and right‑shifts it by 17 places, then Xors it against the previous value of s1, and then updates s1 with that new value. Perhaps this helps to shed some line on why it's called Xorshift?
Taking in the wider function, we can see that ‑ very basically ‑ what's going on here is that two numbers ("seed values") go into the method, get shifted and Xor'd, and then get added together. This result is then returned.
This is extremely efficient and results in a relatively random generated number too.
The Wrap‑Up
Fundamentally, any 'random' number that is generated programmatically actually is not random at all; any method used to generate these random numbers will eventually reveal repetition or patterns in the values they return.
At the end of the day, however, programming languages (and especially those for the web) need to be optimised for speed and performance above absolute randomisation. None of our web languages ‑ at least for right now ‑ use TRNGs and are truly random. However, our newer PRNGs are pretty good at what they do, and they're pretty quick too. A comfortable middle‑ground.
Related Articles

UseRef in React. 
Enhancing Web Typography with text‑wrap: balance. Enhancing Web Typography with
text‑wrap: balance
Advanced Techniques for Responsive Web Design. Advanced Techniques for Responsive Web Design

Understanding JavaScript's sort() Method. Understanding JavaScript's
sort()Method
Optimising Next.js Performance with Incremental Static Regeneration (ISR). Optimising Next.js Performance with Incremental Static Regeneration (ISR)

Can I Learn Front‑End Development in 2 Months? Can I Learn Front‑End Development in 2 Months?

Using the CSS :has Pseudo‑Class. Using the CSS
:hasPseudo‑Class
Understanding and Solving Regular Expression Matching. Understanding and Solving Regular Expression Matching
Why is Time to First Byte (TTFB) Important? Why is Time to First Byte (TTFB) Important?

Using Vue's Suspense for Asynchronous Components. Using Vue's Suspense for Asynchronous Components

DOMContentLoaded vs. load in JavaScript. DOMContentLoadedvs.loadin JavaScript
A Beginner's Guide to Web Hosting. A Beginner's Guide to Web Hosting