Math.random() Collision Visualizer

by Nicholas Berlette

HTML

<canvas id="canvas"></canvas>
<div id="stats" class="">
  <span id="info"></span><button id="controls">Start</button>
</div>

CSS

html, body {
  background: #fff;
  padding: 0;
  margin: 0; 
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
  text-align: center;
}

#canvas {
    position: fixed;
    background: #FFF
}
#stats {
    position: absolute;
    bottom: 10px;
    right: 10px;
    z-index: 10;
    width: 35rem;
    height: 2em;
    vertical-align: middle;
    line-height: 2em;
    margin: 0px 10px;
    font-family: 'Gotham Narrow ScreenSmart', 'Gotham Narrow', 'Gotham', helvetica, serif;
    font-size: 1rem;
    font-weight: 600;
    text-align: center;
    opacity: 0.8;
    color: white;
    background-color: #000a;
    border-radius: 0.25rem;
    box-shadow: 0 0.125rem 0.25rem 0.0625rem #0005;
}
#stats:hover {
    opacity: 1;
}
#controls {
    position: absolute;
    right: 10px;
    top: 5px;
    cursor: pointer;
}

JavaScript

/**
 * IMPORTANT: this variable controls the algorithm used to generate collisions.
 * Switch the value of this between `mt19937`, `lcg`, and `Math.random` to see
 * how each algorithm compares to one another.
 *
 * 1. MT19937: Mersenne-Twister (with a yuge period size of 2^19937 - 1). 
 *   Implemented here in 450 B of WebAssembly. Designed to be as close to the
 *   native implementation as possible in terms of performance and uniformity.
 *   This function seems promising, I'd expect halfway decent results from it.
 *   I specifically developed this for the @nick/math package's random module,
 *   to emulate the native Math.random() function as close as possible. See
 *   the JSR docs at https://jsr.io/@nick/math/doc/~/random for more info.
 *
 * 2. LCG: Linear Congruential Generator (Park-Miller). The simplest and worst
 *   performing of all three. Implemented here in 150 B of WebAssembly.
 *
 * 3. Math.random: Recent versions of v8 use an improved API that is based on
 *   Mersenne-Twister, so the results should be relatively similar to that
 *   of the MT19937 function implemented in WebAssembly here. If you're using
 *   Firefox, Safari, or some other browser, then the results will likely be
 *   disparate from the MT19937 algorithm, as those browsers use other APIs
 *   that I'm unfamiliar with at this time.
 */
var random = LCG;

var HAS_UINT32_ARRAY = !(typeof Uint32Array === "undefined")
// its SIGNIFICANTLY slower to use Uint32Array instead of Array.
// probably due to V8's special optimizations for plain Arrays...
var USE_UINT32_ARRAY = false;

var bits_per_word = 32,
  address_shift = 5 // log2(bits_per_word)

/**
 * Create a new bit set. If a size is provided an array will be allocated
 * upfront, which speeds up writes and test operations since bitwise operations
 * against undefined require a cast. Providing a size will fix the bitset to
 * exactly that size, whereas unsized bitsets grow automatically.
 *
 * The size of the BitSet is rounded up...