For such a task, you may find useful Perlin Noise or even Fractal Brownian Motion noise. See this implementation in JavaScript:
class Utils {
static Lerp(a, b, t) {
return (1 - t) * a + t * b;
}
static Fade(t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
}
class Noise {
constructor() {
this.p = [];
this.permutationTable = [];
this.grad3 = [[1, 1, 0], [-1, 1, 0], [1, -1, 0],
[-1, -1, 0], [1, 0, 1], [-1, 0, 1],
[1, 0, -1], [-1, 0, -1], [0, 1, 1],
[0, -1, 1], [0, 1, -1], [0, -1, -1]];
for (let i = 0; i < 256; i++)
this.p[i] = Math.floor(Math.random() * 256);
for (let i = 0; i < 512; i++)
this.permutationTable[i] = this.p[i & 255];
}
PerlinDot(g, x, y, z) {
return g[0] * x + g[1] * y + g[2] * z;
}
PerlinNoise(x, y, z) {
let a = Math.floor(x);
let b = Math.floor(y);
let c = Math.floor(z);
x = x - a;
y = y - b;
z = z - c;
a &= 255;
b &= 255;
c &= 255;
let gi000 = this.permutationTable[a + this.permutationTable[b + this.permutationTable[c]]] % 12;
let gi001 = this.permutationTable[a + this.permutationTable[b + this.permutationTable[c + 1]]] % 12;
let gi010 = this.permutationTable[a + this.permutationTable[b + 1 + this.permutationTable[c]]] % 12;
let gi011 = this.permutationTable[a + this.permutationTable[b + 1 + this.permutationTable[c + 1]]] % 12;
let gi100 = this.permutationTable[a + 1 + this.permutationTable[b + this.permutationTable[c]]] % 12;
let gi101 = this.permutationTable[a + 1 + this.permutationTable[b + this.permutationTable[c + 1]]] % 12;
let gi110 = this.permutationTable[a + 1 + this.permutationTable[b + 1 + this.permutationTable[c]]] % 12;
let gi111 = this.permutationTable[a + 1 + this.permutationTable[b + 1 + this.permutationTable[c + 1]]] % 12;
let n000 = this.PerlinDot(this.grad3[gi000], x, y, z);
let n100 = this.PerlinDot(this.grad3[gi100], x - 1, y, z);
let n010 = this.PerlinDot(this.grad3[gi010], x, y - 1, z);
let n110 = this.PerlinDot(this.grad3[gi110], x - 1, y - 1, z);
let n001 = this.PerlinDot(this.grad3[gi001], x, y, z - 1);
let n101 = this.PerlinDot(this.grad3[gi101], x - 1, y, z - 1);
let n011 = this.PerlinDot(this.grad3[gi011], x, y - 1, z - 1);
let n111 = this.PerlinDot(this.grad3[gi111], x - 1, y - 1, z - 1);
let u = Utils.Fade(x);
let v = Utils.Fade(y);
let w = Utils.Fade(z);
let nx00 = Utils.Lerp(n000, n100, u);
let nx01 = Utils.Lerp(n001, n101, u);
let nx10 = Utils.Lerp(n010, n110, u);
let nx11 = Utils.Lerp(n011, n111, u);
let nxy0 = Utils.Lerp(nx00, nx10, v);
let nxy1 = Utils.Lerp(nx01, nx11, v);
return Utils.Lerp(nxy0, nxy1, w);
}
FractalBrownianMotion(x, y, z, octaves, persistence) {
let total = 0;
let frequency = 1;
let amplitude = 1;
let maxValue = 0;
for(let i = 0; i < octaves; i++) {
total = this.PerlinNoise(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total / maxValue;
}
}
With Fractal Brownian Motion can have huge control about the randomness of distribution. You can set the scale, initial offset and its increment for each axis, octaves, and persistence. You can generate as many positions you like by incrementing the offsets, like this:
const NUMBER_OF_POSITIONS = 1000;
const X_OFFSET = 0;
const Y_OFFSET = 0;
const Z_OFFSET = 0;
const X_SCALE = 0.01;
const Y_SCALE = 0.01;
const Z_SCALE = 0.01;
const OCTAVES = 8;
const PERSISTENCE = 2;
const T_INCREMENT = 0.1;
const U_INCREMENT = 0.01;
const V_INCREMENT = 1;
let noise = new Noise();
let positions = [];
let i = 0, t = 0, u = 0, v = 0;
while(i <= NUMBER_OF_POSITIONS) {
let position = {x:0, y:0, z:0};
position.x = noise.FractalBrownianMotion((X_OFFSET + t) * X_SCALE, (Y_OFFSET + t) * Y_SCALE, (Z_OFFSET + t) * Z_SCALE, OCTAVES, PERSISTENCE);
position.y = noise.FractalBrownianMotion((X_OFFSET + u) * X_SCALE, (Y_OFFSET + u) * Y_SCALE, (Z_OFFSET + u) * Z_SCALE, OCTAVES, PERSISTENCE);
position.z = noise.FractalBrownianMotion((X_OFFSET + v) * X_SCALE, (Y_OFFSET + v) * Y_SCALE, (Z_OFFSET + v) * Z_SCALE, OCTAVES, PERSISTENCE);
positions.push(position);
t += T_INCREMENT;
u += U_INCREMENT;
v += V_INCREMENT;
i++;
}
Positions you get with these options would look similar to these:
...
501: {x: 0.0037344935483775883, y: 0.1477509219864437, z: 0.2434570202517206}
502: {x: -0.008955635460317357, y: 0.14436114483299245, z: -0.20921147024725012}
503: {x: -0.06021806450587406, y: 0.14101769272762685, z: 0.17093922757597568}
504: {x: -0.05796055906294283, y: 0.13772732578136435, z: 0.0018755951606465138}
505: {x: 0.02243901814464688, y: 0.13448621540816477, z: 0.013341084536334057}
506: {x: 0.05074194554980439, y: 0.1312810723109357, z: 0.15821600463130164}
507: {x: 0.011075140752144507, y: 0.12809058766450473, z: 0.04006055269090941}
508: {x: -0.0000031848272303249632, y: 0.12488712875549206, z: -0.003957905411646261}
509: {x: -0.0029798194097060307, y: 0.12163862278870072, z: -0.1988934273517602}
510: {x: -0.008762098499026483, y: 0.11831055728747841, z: 0.02222898347134993}
511: {x: 0.01980289423585394, y: 0.11486802263767962, z: -0.0792283303765883}
512: {x: 0.0776034130079849, y: 0.11127772191732693, z: -0.14141576745502138}
513: {x: 0.08695806478169149, y: 0.10750987521108693, z: 0.049654228704645}
514: {x: 0.036915612100698, y: 0.10353995005320946, z: 0.00033977899920740567}
515: {x: 0.0025923223158845687, y: 0.09935015632822117, z: -0.00952549797548823}
516: {x: 0.0015456084571764527, y: 0.09493065267319889, z: 0.12609905321632175}
517: {x: 0.0582996941155056, y: 0.09028042189611517, z: -0.27532974820612816}
518: {x: 0.19186052966982514, y: 0.08540778482478142, z: -0.00035058098387404606}
519: {x: 0.27063961068049447, y: 0.08033053495775729, z: -0.07737309686568927}
520: {x: 0.20318957178662056, y: 0.07507568989311474, z: -0.14633819135757353}
...
Note: for efficiency, it's a good idea generate all positions only once into an array of positions like in this example, and then in some animation loop just assigning positions to your point from this array one by one.
Bonus: Here you can see how those values affect the distribution of multiple points by playing around with real-time response control panel:
https://marianpekar.github.io/fbm-space/
References:
https://en.wikipedia.org/wiki/Fractional_Brownian_motion
https://en.wikipedia.org/wiki/Perlin_noise