Random numbers for Wasm

Is there a recommended way to generate random numbers when compiling to Wasm. I tried a couple of approaches now, using std.rand.Xoroshiro128 and std.crypto.random, but had problems with both approaches. I ended up pulling them from javascript :frowning:

With cypto option I get this:

os.zig:1088:58: error: use of undeclared identifier 'mode_t'
pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t {
                                                         ^
os.zig:186:20: note: referenced here
    const fd = try openZ("/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
                   ^
os.zig:181:48: error: expected type 'std.os.OpenError!void', found '@typeInfo(@typeInfo(@TypeOf(std.os.getRandomBytesDevURandom)).Fn.return_type.?).ErrorUnion.error_set!void'
        else => return getRandomBytesDevURandom(buffer),
                                               ^
os.zig:181:48: note: error set '@typeInfo(@typeInfo(@TypeOf(std.os.getRandomBytesDevURandom)).Fn.return_type.?).ErrorUnion.error_set' cannot cast into error set 'std.os.OpenError'
        else => return getRandomBytesDevURandom(buffer),

and for completeness the error I get with ‘rand’ approach:

std/time.zig:98:15: error: container 'std.os' has no member called 'timespec'
    var ts: os.timespec = undefined;
              ^
std/time.zig:73:49: note: called from here
    return @intCast(i64, @divFloor(nanoTimestamp(), ns_per_ms));
                                                ^
std/time.zig:64:36: note: called from here
    return @divFloor(milliTimestamp(), ms_per_s);
                                   ^
stars.zig:14:75: note: called from here
var prng: std.rand.Xoroshiro128 = rand.DefaultPrng.init(std.time.timestamp());
                                                                          ^
stars.zig:14:56: note: called from here
var prng: std.rand.Xoroshiro128 = rand.DefaultPrng.init(std.time.timestamp());
                                                       ^
std/time.zig:73:49: note: referenced here
    return @intCast(i64, @divFloor(nanoTimestamp(), ns_per_ms));
                                                ^
std/time.zig:64:36: note: referenced here
    return @divFloor(milliTimestamp(), ms_per_s);
                                   ^
stars.zig:14:75: note: referenced here
var prng: std.rand.Xoroshiro128 = rand.DefaultPrng.init(std.time.timestamp());
                                                                          ^
stars.zig:39:24: note: referenced here
        points[i].x = (prng.random.float(f32) * tile.w);
                       ^
std/time.zig:98:15: error: container 'std.os' has no member called 'timespec'
    var ts: os.timespec = undefined;

What you’re seeing is basically that std.crypto.random relies on OS APIs that you don’t have in wasm. If you’re targeting WASI I’ve been told there is a dedicated implementation, but for “vanilla” wasm that runs in the browser you just don’t have an OS layer that you can depend on.

When it comes to Xoroshiro, that’s a prng implementation that works perfectly in wasm, you just need to find a proprer way of seeding it. The error that you got is because you tried to seed it with std.time.timestamp(), which tries to access an API not available in wasm. All you have to do in this case (assuming that’s good enough for your usecase) is to have JS provide provide a function that returns the current timestamp and just call that instead.

Thanks, all that makes sense, and pulling a single seed is much better than pulling every random number over the js interface :slight_smile:

However I realise I can’t do that in a ‘const’ way, since it involves a call to javascript at runtime.

const prng: std.rand.Xoroshiro128 = rand.DefaultPrng.init(js.getSeed());

And obviously I only want to do it once. But I am struggling to understand how to have an undefined struct? I was expecting something like this:

var prng: std.rand.Xoroshiro128 = undefined;

fn runtimeDisplayFrame() {
  if (prng == undefined) {
        prng = rand.DefaultPrng.init(js.getSeed());
        js.logU32("rnd", js.getSeed());
    }
}

But Xoroshiro128 is a file based struct? (and I am still getting my head around that concept/terminology) :slight_smile:

Unless there are runtime static intialisers that I missed in the documentation?