Random Position On The Surface Of A Sphere

Ok, so you want to randomly position lots of things on the surface of a sphere? Lets try a few things. I’ll be using THREE.js for these examples but the logic should be transferable.

Download source from Github

First try — Random vector with setLength().

function getCartesianPositions(howMany, radius) {
    // Create and array to store our vector3 point data
    var vectors = [];
    // Create new points using random x,y and z properties then setting vector length to radius
    for (var i = 0; i < howMany; i += 1) {
        var vec3 = new THREE.Vector3();
        vec3.x = THREE.Math.randFloatSpread(1);
        vec3.y = THREE.Math.randFloatSpread(1);
        vec3.z = THREE.Math.randFloatSpread(1);
        vec3.setLength(radius);
        vectors.push(vec3);
    }
    return vectors;
}

This method takes two parameters. howMany - the number of points you want and the radius of your sphere. In the loop we generate a new vectors and set their x,y, and z points to random points between -1 and 1. This effectively puts the points inside a cube. Then we use the setLength() method of the vector to push the point out to the surface of a sphere. So what we’ve done is make a tiny cube of points and blow it out to be a large sphere. This method actually works pretty well for smaller numbers of points but when you start to use a lot 100,000 or more you can start to see clustering where the edges of the cube were.

Random vector with setLength()

Lets try something else!

Spherical coordinates

function getSphericalPositions(howMany, radius) {
    // Create and array to store our vector3 point data
    var vectors = [];
    // Create a spherical object
    var spherical = new THREE.Spherical();
    // Set radius of spherical
    spherical.radius = radius;
    // Create new points using random phi and theta properties of the spherical object
    for (var i = 0; i < howMany; i += 1) {
        spherical.phi = THREE.Math.randFloat(0, Math.PI); // Phi is between 0 - PI
        spherical.theta = THREE.Math.randFloat(0, Math.PI * 2); // Phi is between 0 - 2 PI
        var vec3 = new THREE.Vector3().setFromSpherical(spherical);
        vectors.push(vec3);
    }
    return vectors;
}

This time we’ll use a random spherical coordinate and convert it to a cartesian coordinate. Spherical coordinates have three properties. radius, phi and theta. Basically a radius and two different angles — Phi between 0 and PI and Theta between 0 and PI*2. We’ll use our radius and generate two random angles.

Spherical

Hmm, we’ve got a lot of clustering at the poles. That’s not what we want. Lets try add a little bias into the distribution.

function getSphericalPositionsWithBias(howMany, radius, bias) {
    var vectors = [];
    var spherical = new THREE.Spherical();
    spherical.radius = radius;
    for (var i = 0; i < howMany; i += 1) {
        spherical.phi = getRndBias(0, Math.PI, Math.PI / 2, bias); // Phi is between 0 - PI
        spherical.theta = THREE.Math.randFloat(0, Math.PI * 2); // Theta is between 0 - 2 PI
        var vec3 = new THREE.Vector3().setFromSpherical(spherical);
        vectors.push(vec3);
    }
    return vectors;
}
function getRndBias(min, max, bias, influence) {
    const rnd = Math.random() * (max - min) + min; // random in range
    const mix = Math.random() * influence; // random mixer
    return rnd * (1 - mix) + bias * mix; // mix full range and bias
}

We’re going to try bias the distribution to favor the middle of the phi angle.

getSphericalPositionsWithBias(numberOfPoints,radiusOfSphere, 1);

Hmm, now we have too many particles along the equator of our sphere. 🤔

Spherical with bias 1

Ok, lets try tweak the bias… Using 0.5.

getSphericalPositionsWithBias(numberOfPoints,radiusOfSphere, 0.5);

Hurrah! This looks pretty good. No, or very little, clustering. Yes!

Spherical with bias 0.5

Bonus

Uniform distribution on a sphere.

function getUniformPositions(howMany, radius) {
    var vectors = [];
    var inc = Math.PI * (3 - Math.sqrt(5));
    var x = 0;
    var y = 0;
    var z = 0;
    var r = 0;
    var phi = 0;
    for (var k = 0; k < howMany; k++) {
        var off = 2 / howMany;
        var vec3 = new THREE.Vector3();
        y = k * off - 1 + off / 2;
        r = Math.sqrt(1 - y * y);
        phi = k * inc;
        x = Math.cos(phi) * r;
        z = (0, Math.sin(phi) * r);
        x *= radius;
        y *= radius;
        z *= radius;
        vec3.x = x;
        vec3.y = y;
        vec3.z = z;
        vectors.push(vec3);
    }
    return vectors;
}

I didn’t write this. I don’t fully understand it either but what is does do is give you a nice uniform distribution over the surface of a sphere. I can’t remember where I found this code. But you recognise it then please let me know and I’ll credit it.

Uniform distribution