Skip to content

[2.x] Add a way to get the value of the current random seed #7711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 of 17 tasks
XenusYard opened this issue Apr 9, 2025 · 11 comments
Open
1 of 17 tasks

[2.x] Add a way to get the value of the current random seed #7711

XenusYard opened this issue Apr 9, 2025 · 11 comments

Comments

@XenusYard
Copy link

XenusYard commented Apr 9, 2025

Increasing access

I believe this feature would increase the consistency of the API (i.e. you can both set and get the value of the current frame rate, for instance, but you can only set and never get the value of the random seed) and make iterative development centered around randomness easier and friendlier.

Most appropriate sub-area of p5.js?

  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • Build process
  • Unit testing
  • Internationalization
  • Friendly errors
  • Other (specify if possible)

Feature request details

It is sometimes useful to know the value of the random seed that produced a specific result — be it for debugging or presentation purposes, or something else.

Currently the only way to know the seed value in advance is to generate it with random() first, save that value and pass it to randomSeed(). It is cumbersome and requires setting it up every time you want to keep track of your random seeds.

Without a similar setup reproducing a specific result may be a tedious and unreliable process — especially if the degree of accuracy required is very strict.

Thus, I propose adding a way of fetching the current value of the random seed, perhaps in a form of a variable (e.g. seed) or a function (e.g. getRandomSeed() or seed()). That way the value could be conveniently retrieved at any moment during runtime (for example, from the dev console in the browser) and referenced later.

Copy link

welcome bot commented Apr 9, 2025

Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, please make sure to fill out the inputs in the issue forms. Thank you!

@SableRaf
Copy link
Contributor

SableRaf commented Apr 9, 2025

For simplicity and consistency, how about using the same model as framerate(), where calling it without an argument returns the current value?

So calling randomSeed() without an argument would return the current random seed.

The same could be applied to noiseSeed().

@XenusYard
Copy link
Author

For simplicity and consistency, how about using the same model as framerate(), where calling it without an argument returns the current value?

That was my initial idea too. However, according to the source code, calling randomSeed() with no arguments randomizes the seed value:

// At src/math/random.js:81
p5.prototype.randomSeed = function(seed) {
  this._lcgSetSeed(randomStateProp, seed);
  <...>

// At src/math/random.js:31
p5.prototype._lcgSetSeed = function(stateProperty, val) {
  // pick a random seed if val is undefined or null
  // the >>> 0 casts the seed to an unsigned 32-bit integer
  this[stateProperty] = (val == null ? Math.random() * m : val) >>> 0;
  <...>

This is useful if you want to simply randomize the seed and don't care about setting it to a specific value.
A workaround might be to implement this behavior by calling randomSeed(-1) and implement returning the current seed by calling randomSeed() with no arguments.

But that whould, presumably, break backwards compatibility for projects that rely on the current behavior of randomSeed(), which is why I believe introducing a new entity would be a more stable solution — as much as it would be nice to have randomSeed() behave exactly like frameRate().

@SableRaf
Copy link
Contributor

SableRaf commented Apr 9, 2025

Ah good catch! Thanks for looking into this.

I don't think my consistency/simplicity argument supports breaking backwards compatibility, so anyone looking at this feel free to ignore it.

getRandomSeed() and getNoiseSeed() could be a worthy additions.

@ksen0
Copy link
Member

ksen0 commented Apr 10, 2025

Great point!

@SableRaf @XenusYard what do you think about randomSeed() (function) setter and randomSeed (variable) getter? This follows the pattern of naming many getter variables (mouseX, frameCount, etc) in p5.js. There are getXYZ() style getter functions in the API, but they are not typically for sketch-level information, and rather for getting attributes from Table, XML, and other objects. Downside of this is that

Another idea would be to have randomSeed() return the value it sets (right now it's undefined). Downside is that it changes the function signature, upside is that is doesn't expand the API.

Since 2.0 release is around the corner, I would suggest we keep this as a possible addition in 2.x post-release. This is because the proposed timeline is to not expand 1.x once 2.0 is released, even though 1.x is still available for a while (info: #7488). Do you mind adding that to issue body/title please?

Other relevant API precedent to consider (source: https://beta.p5js.org/reference/):

p5.js 2.0 typography uses 1 function as both getter and setter (though with randomSeed() that's not straightforward to accommodate - as written above):

Image

And frameRate() and getTargetFrameRate(), which follows this pattern because the target frame rate is what was set by frameRate, regardless of performance, and the intent was to make that clear in the name:

Image

@SableRaf
Copy link
Contributor

Thanks @ksen0

what do you think about randomSeed() (function) setter and randomSeed (variable) getter? This follows the pattern of naming many getter variables (mouseX, frameCount, etc) in p5.js.

I like this approach. Adding randomSeed and noiseSeed variables makes sense.

I don't know what the consequence would be on older sketches that used the same variable names but I believe this type of name collision is already handled by the FES.

@XenusYard
Copy link
Author

@ksen0

what do you think about randomSeed() (function) setter and randomSeed (variable) getter?

That would be ideal, but wouldn't that create a name conflict? How would you distinguish between the function and the variable? Or are you talking about literal ES6 getters and setters?

Another idea would be to have randomSeed() return the value it sets (right now it's undefined).

But what if you want to get the current seed value without changing it? If you set the seed first, then return the value — you've lost the previous seed value, the one you were trying to get in the first place. If you save the previous seed value before randomizing and return that instead — you can never get the new value of the seed before changing it first. I believe the first solution you proposed would be better.

Do you mind adding that to issue body/title please?

Yeah, sure! Should I just prepend the title with "[2.x]"?

@XenusYard XenusYard changed the title Add a way to get the value of the current random seed [2.x] Add a way to get the value of the current random seed Apr 10, 2025
@limzykenneth
Copy link
Member

Just to mentioned that this has been looked into before in #2606 and to rehash the point in that old issue, while this is possible to implement, I'm not 100% sure what the use case is. It is possible to save the current seed including a random one with this snippet:

var seed = random();
randomSeed(seed);

Although it feels cumbersome, as mentioned in the issue post, I don't really see another way around it, ie. at the minimum we still have to do randomSeed(random()) which saves one line, and with randomSeed() returns the seed value, we more or less delegate that one line saved to later:

randomSeed(random());

// Later
var seed = randomSeed();

So just based on this I still don't fully see the use case, unless there is something else I missed?

For current behavior of randomSeed() setting a random seed value is technically undocumented (I certainly didn't know or remember it does that until now) so if needed can be changed.

@XenusYard
Copy link
Author

@limzykenneth

Although it feels cumbersome, as mentioned in the issue post, I don't really see another way around it, ie. at the minimum we still have to do randomSeed(random()) which saves one line, and with randomSeed() returns the seed value, we more or less delegate that one line saved to later: [...]

That's not really true, since p5 randomizes the seed by default. You don't need to call randomSeed() explicitly if you want to have different random values each time. What you would be able to do, however, is access the seed value directly from the console, thus minimizing the friction when it comes to the development process.

There are two main aspects to my argument: convenience and consistency.

The original drive for this proposal comes from my own experience and aligns with the convenience aspect. There have been several times when I was working on a project that was centered around randomness and encountered a bug that was only apparent in a certain configuration of the system. And since the entire system is based around randomness it is very difficult in those kinds of situations to reproduce a specific state, which makes debugging rather unpleasant and inefficient. Now, is it my fault for being imprudent and not preparing for these situations in advance? Yes, absolutely. There is no reason for me not to save the random seed value ahead of time, other then it being, well, inconvenient. It feels like something that should be taken care of by the framework. It stores the seed somewhere in its state. Why not let the developer easily fetch it at any time, without having to worry about it beforehand? It makes developing and prototyping more cumbersome and verbose than it needs to be.

When it comes to consistency, it seems odd to me that random seed doesn't get the same treatment like other state variables that are part of p5: width and height of the canvas, frame rate, frame count etc. After all, we don't have to save the value of the target frame rate in a variable every time we want to have access to it — we simply call getTargetFrameRate(). Why not do the same for the random seed?

@limzykenneth
Copy link
Member

limzykenneth commented Apr 18, 2025

That's not really true, since p5 randomizes the seed by default. You don't need to call randomSeed() explicitly if you want to have different random values each time. What you would be able to do, however, is access the seed value directly from the console, thus minimizing the friction when it comes to the development process.

That is not possible, when randomSeed() is not explicitly called, random() delegates to Math.random() (ie. p5.js is not randomizing the seed by default at the start), and it is not possible to retrieve the seed value of Math.random() (not to mention they use different RNG implementation depending on the browser). Changing unseeded RNG to use our own RNG implementation presents performance tradeoff, ie. Math.random() is significantly faster than our seedable RNG implementation.

There is no reason for me not to save the random seed value ahead of time, other then it being, well, inconvenient.

Sorry to kinda repeat here as this is the part I don't really understand, you will have to set the random seed ahead of time, as per above, and so that value must have existed before randomSeed() is called and thus can be saved in a variable to be accessed later. Yes, it is possible to access it later if randomSeed() return the seed value but it still is relegating setting that variable value from before RNG is seeded to after it is seeded. I think it is probably worth highlighting here that I'm not against implementing this, I'm just not seeing a scenario where it would be considered different from saving the seed value beforehand.

The reason this is potentially treated differently is back to the first point above, the RNG when not seeded is using the more performant Math.random() and its seed cannot be retrieved.

@XenusYard
Copy link
Author

XenusYard commented Apr 18, 2025

That is not possible, when randomSeed() is not explicitly called, random() delegates to Math.random() (ie. p5.js is not randomizing the seed by default at the start), and it is not possible to retrieve the seed value of Math.random() (not to mention they use different RNG implementation depending on the browser).

Ah. See, I was not aware of that. Yeah, I will have to concede that kind of undermines the entire convenience point, unfortunately.

So, with that correction in mind, the discussion comes down to which of the following paradigms is the most sensible and whether the pros of a new feature outweigh the cons of introducing it:

(NOTE: the following applies equally to noise() and noiseSeed())

1) Leave as is:

// Initializing
let seed = random();
randomSeed(seed);

// Retrieving
print(seed);

Pros:

  • No changes made to the API

Cons:

  • Verbose

2) Introduce getter variables:

// Initializing
randomSeed();

// Retrieving
print(randomSeed);

Pros:

  • Less verbose
  • More consistent with the existing API

Cons:

  • Adds new entities to the framework with a small relative benefit
  • Still not sure how this is supposed to work without producing a naming conflict (randomSeed variable vs. randomSeed function)

3) Change the return value of randomSeed():

// Initializing
let seed = randomSeed();

// Retrieving
print(seed);

Pros:

  • Slightly less verbose
  • Doesn't introduce new entities to the framework

Cons:

  • Changes the existing function signature

Bottom line

I think what makes the most sense at this stage is to:

  1. Retain the behaviour of randomizing the seed value when randomSeed()/noiseSeed() is called with no arguments.
  2. Adapt randomSeed()/noiseSeed() to return the new seed value.
  3. Reflect both of those aspects in the documentation.

What do you guys think?
@SableRaf @ksen0 @limzykenneth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants
@SableRaf @ksen0 @limzykenneth @XenusYard and others