Random Distribution in Javascript

Fri May 02 2025 15:06:07 GMT+0100 (British Summer Time) randomjavascripthtmlarchived

Math.random distribution

How random is random? This simple example gives a way to vizualize how random the Javascript Math.random() method distribution is. As Javascript doesn't take a random seed, the output should not repeat between browser instances.

Random Distribution in Javascript visualized

I won't go through the whole code here, just the bits that matter. First of all, our Map variable keeps track of the tiles that make up the demonstration. It also provides a generate method, that creates a new map grid of tiles. Each tile keeps track of how many times it has been hit, starting at 0, and adding 10 every time the tile is hit by the random generator. This is used to determine how light the tile is, with 0 being black and 255 being white:


var Map = {
    tiles    : [],
    width    : 60,
    height    : 40,
    
    generate : function(w, h)
    {
        this.tiles.splice(0, this.tiles.length);
        
        tileW = canvasW / w;
        tileH = canvasH / h;
        
        this.width = w;
        this.height = h;
        
        for(var i = 0; i < w*h; i++) { this.tiles.push(0); }
    }
};

Our tiles are updated with the updateCells method. This generates a random number x times, where x = [total tiles] / 10 (ie; 10% of the number of tiles). The random number generated is between 0 and last tile index, and the corresponding tile in the Map.tiles list has 10 added to its value.


function updateCells()
{
    // 10% random update
    for(var i = 0; i < (Map.width * Map.height) / 10; i++)
    {
        var idx = Math.floor(Math.random() * Map.tiles.length);
        Map.tiles[idx]+= 10;
        if(Map.tiles[idx] > 255) { Map.tiles[idx] = 255; }
    }
}

Give it a try to see the hit rate of numbers within the range 0 to [Map width] x [Map height]. If you have a better computer than mine, try changing the Map.generate(60, 40); call in the onload method to different values to see random hits over different ranges.

Full source code

core.js


var ctx = null;

var tileW, tileH;
var canvasW, canvasH;

var currentSecond = 0, frameCount = 0, framesLastSecond = 0;
var gameTime = 0, lastFrame = 0, lastUpdate = 0, updateDelay = 2000;

var paused        = false;
var stepFrame    = false;
var reset        = false;

var Map = {
    tiles    : [],
    width    : 60,
    height    : 40,
    
    generate : function(w, h)
    {
        this.tiles.splice(0, this.tiles.length);
        
        tileW = canvasW / w;
        tileH = canvasH / h;
        
        this.width = w;
        this.height = h;
        
        for(var i = 0; i < w*h; i++) { this.tiles.push(0); }
    }
};

window.onload = function()
{
    ctx = document.getElementById("display").getContext('2d');
    
    canvasW    = document.getElementById("display").width;
    canvasH    = document.getElementById("display").height;
    
    Map.generate(60, 40);
    
    document.addEventListener('keypress', function(e) {
        if(String.fromCharCode(e.which).toLowerCase()=='p') { paused = !paused; }
        else if(String.fromCharCode(e.which).toLowerCase()=='s' && paused) { stepFrame = true; }
        else if(String.fromCharCode(e.which).toLowerCase()=='r') { reset = true; }
        else if(String.fromCharCode(e.which).toLowerCase()=='d')
        {
            updateDelay+= 200;
            if(updateDelay>=2000) { updateDelay = 200; }
        }
    });
    
    requestAnimationFrame(updateDisplay);
};

function updateDisplay()
{
    if(ctx==null) { return; }
    
    ctx.fillStyle = "#000000";
    ctx.fillRect(0, 0, canvasW, canvasH);
    
    // Framerate & game time calculations
    var sec = Math.floor(Date.now()/1000);
    if(sec!=currentSecond)
    {
        currentSecond = sec;
        framesLastSecond = frameCount;
        frameCount = 1;
    }
    else { frameCount++; }
    
    var now = Date.now();
    gameTime+= (now-lastFrame);
    lastFrame = now;
    
    if(reset)
    {
        reset = false;
        
        Map.generate(Map.width, Map.height);
    }
    if((paused && stepFrame) || (!paused && updateDelay!=0 && (gameTime-lastUpdate)>=updateDelay))
    {
        updateCells();
        lastUpdate = gameTime;
        stepFrame = false;
    }
    
    for(var y = 0; y < Map.height; y++)
    {
        var ySum = y * Map.width;
        
        for(var x = 0; x < Map.width; x++)
        {
            var hex = Map.tiles[(ySum + x)].toString(16);
            hex = (hex.length==1 ? "0" + hex : hex);
            
            ctx.fillStyle = "#" + hex + hex + hex;
            
            ctx.fillRect(x*tileW, y*tileH, tileW, tileH);
        }
    }
    
    ctx.fillStyle = "#ffffff";
    ctx.textAlign = "end";
    ctx.fillText("Framerate: " + framesLastSecond +
        (paused ? " (Paused)" : ""), canvasW-50, 20);
    
    ctx.textAlign = "start";
    ctx.fillText("s to Step when paused, r to reset", 10, canvasH - 30);
    ctx.fillText("Auto step time: " + updateDelay + "ms (d to change)", 10, canvasH - 15);
    
    requestAnimationFrame(updateDisplay);
}

function updateCells()
{
    // 10% random update
    for(var i = 0; i < (Map.width * Map.height) / 10; i++)
    {
        var idx = Math.floor(Math.random() * Map.tiles.length);
        Map.tiles[idx]+= 10;
        if(Map.tiles[idx] > 255) { Map.tiles[idx] = 255; }
    }
}

Index.htm


<!DOCTYPE html>
<html>
<head>

</head>
<body>

    <canvas id="display" width="400" height="300"></canvas>
    
    <script type="text/javascript" src="core.js"></script>

</body>
</html>