Randomizer
Homemade Random Number Generator
Code lives here.
To create a random number generator, I was immediately drawn to the randomness that exists on the computer itself as a source: much in the way that natural phenomena like the weather can serve as a source of randomness, it seemed to me that the inner workings of a computer (CPU, GPU, folder structures, etc.) could be a great source of randomness.
As it turned out, I was not exactly right about that.
Getting The Info
I decided to try working with CPU usage statistics, since I already had a target project in mind to use my RNG on that was heavily related to the CPU. Getting the statistics were easy enough: I used the npm os module and copied the raw code from this gist, which basically gets the average CPU usage by measuring idle and tick times on the CPU cores. Getting this to be random, however, proved to be a bigger chore than I’d imagined. Using the actual average CPU usage wouldn’t have served as a great RNG: it’d be 1–2 digits and follow a pretty standard sinewave.
Instead, I initially decided to take the measured idle time of the CPU and perform a middle square randomization method on it as a seed. In getting this to work with the p5 sketch, though, I ended up making a mistake that would plague me throughout this process, which you can see in the code in the github repo. I set up a node server to serve my randomizer code to the p5 sketch, and in doing so forgot to pay attention to how the initial CPU average code actually worked.
By leaving my “start measure” outside of the module.exports
, I effectively ensured that totalDifference
and idleDifference
would continually increment as long as the server was still running. Although from some preliminary logging it appears that as long as the server is running, even if I run the cpuAverage() function independently the idle and total measures continue to increment, so maybe I’m not fully understanding what’s going on here.
At any rate, I barged ahead for quite some time without understanding what was going on here.
(Update : I’m dumb. Of course it will increment over time because OS is measuring the lifetime idle and active time of the core.)
I started by taking the CPU idle time and using a classic middle square method (taking the middle 5 digits and squaring them), but as you can see above that didn’t result in a great variety of numbers.
I swapped that for the last 2–3 digits and got more interesting output, although it was still incrementing.
I decided it was time to start testing with Allison’s visualization sketch, and began to have a very annoying time.
I quickly discovered that getting this to behave anything like an actual random number generator would require fairly significant intervals (roughly 1 draw
per second as opposed to the ideal 10 or greater) and a low step count on my pixel generator (1 at a time as opposed to the 4 in the initial sketch). Even once I set this up, it followed pretty obvious patterns (which I guess should have been clear to me, since it was constantly incrementing).
I did a lot of toying with slicing numbers, squaring them, and playing with timing until I got a method that seemed at least pseudorandom: running the function once a second and squaring the last three numbers of the CPU’s idle time. By limiting the measurement to once per second, I was able to simulate randomness because the measurement would increment from 0–999 greater over the course of that second.
This still wasn’t super easy to test since it ran so slowly, though. I increased the step count and hoped that would still communicate the idea visually
This seemed random enough, but it was so slow that it frustrated me. So I went with a different method: using the idleDifference
and the totalDifference
.
My initial testing with these numbers, by simply multiplying the last 3 digits of each number by one another, gave similarly predictable outputs. I cut it down to the last 2 digits, and then concatted them, and this finally gave me the type of output I was looking for. I was able to up the framerate to 5 and still get a seemingly random distribution.
I tested at higher framerates and got more uniform/patterned distributions.
It still doesn’t look totally random, but it was pseudorandom enough for my purposes, and I was running out of time, so I moved on to integrating it to my old tarot project.
Implementation
This was easy enough: I simply added the randomizer function as a required package to my tarot script, using it to randomly select a process to either start up or kill dependent on the sentiment analysis of the tarot. Auspiciously enough, the first time it ran successfully it decided to kill my browser. I think there’s something fairly beautiful about using the CPU’s usage statistics to help select which process (which takes up space on the CPU) to start up or kill — it closes the loop of this script in a way that seems poetic to me.
Reflections
This was a fun little project that helped me understand both randomness and the CPU a good bit better. Mostly, it became apparent how hard true randomness is to achieve: I had to use a bunch of workarounds and hacks to get something even approaching it, and I’m sure it would be easy enough to predict if you documented exactly what was running on my computer at any time, although I think for the average user that tends to pretty random. Were I to do this again, I’d probably use a different measure than idle/active time, since both are always going to increment no matter what.