Skip to content

Add jit function #7698

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
quinton-ashley opened this issue Apr 4, 2025 · 9 comments
Open
1 of 17 tasks

Add jit function #7698

quinton-ashley opened this issue Apr 4, 2025 · 9 comments

Comments

@quinton-ashley
Copy link
Contributor

quinton-ashley commented Apr 4, 2025

Increasing access

Improve writability and readability for generating random positions

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

Proposal to add a function to p5.js that'd make it a bit easier for users to generate random positions.

In P2D mode, generating random positions anywhere within the bounds of the canvas, when no transformations are applied, is pretty simple.

circle(random(width), random(height), 10);

But generating symmetric random values around a point, such as the transform origin (initially at the center of the canvas in WEBGL mode), requires subtraction (ex. random(10) - 5) or defining lower and upper bound params in random (ex. random(-5, 5)). With small numbers this is a non-issue, but when working with variables, you can see that the length of the equation or function call increases linearly.

circle(random(width) - width / 2, random(height) - height / 2, 10);

The length of the line could be cut down by defining variables like this:

let hw = width / 2;
let hh = height / 2;
circle(random(-hw, hw), random(-hh, hh), 10);

If the user wanted to add a margin of 100 pixels from the canvas edges, where random points should not be placed, they could do so like this:

let hw = width / 2 - 100;
let hh = height / 2 - 100;
circle(random(-hw, hw), random(-hh, hh), 10);

Here's another approach using the existing p5.Vector API:

let pos = p5.Vector.random2D();
circle(pos.x * width / 2, pos.y * height / 2, 50);

But I'd prefer if this effect was achievable inside a function like circle as a clean one liner.

Previously I'd proposed randomX and randomY functions, with separate implementations for P2D and WEBGL mode to attain the same visual result of making a random point within the bounds of the canvas, when no transformations are applied.

circle(randomX(), randomY(), 10);

Margin of 100 pixels:

circle(randomX(-100), randomY(-100), 10);

But to summarize the discussion in issue #7671, numerous problems with the proposal were found:

  • the functions could not be described in simple enough terms
  • zero param use is very limited in its applicability, considering it doesn't account for transformations
  • when a single param (margin) is given, it could be mistaken for an upper bound like with random
  • the effect of margin signage (whether it causes the range to expand or contract) was another point of confusion given that CSS margins kinda work the opposite way

Now I also think it's strange in principle for math functions to have different implementations and return different values depending on which renderer is being used. So perhaps interoperability shouldn't be a goal here and seemingly can't be if greater flexibility is a priority.

I've thought up another function that simply generates symmetric random values. Yet, the name randomSymmetric is too long, it'd defeat the purpose. The short form randSym is also awful due to its similarity to the words "rancide" and "ransom".

jit(amount)

What about the name jit?

The full name jitter would be a fine too but ideally the shorter the better for this. No other words in English start with "jit" besides "jitney". For the seasoned programmers among us the acronym JIT Just-In-Time will come to mind but it'll be clear from how the function is used that that meaning doesn't apply.

Jitter is uncontrolled deviation from a signal. I think it's a fitting metaphor for random variation around 0 or whatever the result of jit is being added to.

EDIT: Jitter is also common vernacular: "First day jitters", "do the jitterbug", "the camera is jittering", etc.

Implementation

function jit(v) {
  return random(-v, v);
}

Examples

https://q5js.org/learn/#jit

Example of adding circles within a jitter range around the mouse position.

// using `random`

function setup() {
  createCanvas(200, 200);
}

function draw() {
  circle(mouseX + random(-3, 3), mouseY + random(-3, 3), 5);
}
// using `jit`

function setup() {
  createCanvas(200, 200);
}

function draw() {
  circle(mouseX + jit(3), mouseY + jit(3), 5);
}

Creating a random x position in p5's WEBGL mode.

// using `random`

function setup() {
  createCanvas(200, 100, WEBGL);
}

function draw() {
  circle(random(width) - width / 2, 0, random(50));
}
// using `jit`

function setup() {
  createCanvas(200, 100, WEBGL);
}

function draw() {
  circle(jit(width / 2), 0, random(50));
}

Summary

Though not as common as using random to generate values within a range with defined lower and upper bounds, jit would be a very small but useful addition to p5.js that provides a more convenient way to fulfill a common need with broad applications. Though jit is certainly something users could easily implement manually as a helper function in their own code, my guess is they probably wouldn't consider doing so (I never have either). If jit was a p5 library function that becomes familiar to the community, using it could become a no-brainer though.

jit is one dimensional for simple use when specifying input params for other functions. EDIT: (mistake removed)

However, functions for generating a random point within a unit circle or unit sphere could be worth adding to p5 as well.

https://docs.unity3d.com/6000.0/Documentation/Manual/class-random.html

Thoughts on this?

@davepagurek @ksen0 @GregStanton @VANSH3104

@GregStanton
Copy link
Collaborator

GregStanton commented Apr 5, 2025

Interesting!

Thanks @quinton-ashley for this thoughtful proposal. It raises a number of interesting issues.

jit is one dimensional for simple use when specifying input params for other functions. p5.Vector.random2D and p5.Vector.random3D already exist for generating random points within a unit square and unit cube respectively. However, functions for generating a random point within a unit circle or unit sphere could be worth adding to p5 as well.

This comment points to a problem with the current p5 API that I hadn't noticed before, and I think it points the way to some possible improvements too. I'll try to share some quick thoughts. I'm pretty curious to know what you think @ksen0, but I'll happily wait until we're on the other side of the 2.0 release :)

Problems with the current p5.js API make it hard to extend

Although adding a function like jit would be simple by itself, I think it's helpful to pursue the more general line of thinking from the quote above. This should make it clearer whether adding such a function yields a net benefit. It also leads us to some apparent inconsistencies with random(), p5.Vector.random2D(), and p5.Vector.random3D() that affect both predictability and extensibility.

Predictability

Based on the current behavior of random() with no arguments, we may expect random2D() and random3D() to generate random points inside cubes, as below.

  • random(): the 1D unit cube (i.e. the segment $[0, 1]$)
  • random2D(): the 2D unit cube (i.e. the square $[0, 1] \times [0, 1]$)
  • random3D(): the 3D unit cube (i.e. the cube $[0, 1] \times [0, 1] \times [0, 1]$)

This is exactly what you indicated in your write-up. Unfortunately, this is not the case! Although this description of random() is accurate, the latter two functions actually generate points on (not inside) the unit sphere, in 2D space and 3D space respectively. Here's a demo showing how random2D() generates points on a circle and how random3D() generates points on the surface of a sphere.

Syntax is an issue as well. Whereas random() takes either no argument, a single max argument, or a min and a max argument, the 2D and 3D versions don’t take any arguments. Extending them to take arguments wouldn’t make sense due to how they’re defined.

Extensibility

It seems hard to add functions that generate points inside 2D and 3D cubes, say, since the natural names for those functions are already taken. If such features were to exist in p5, surely they'd be called random2D() and random3D(), by analogy with random().

Possible solution (involves breaking changes)

When it comes to breaking changes, it's always good to proceed with caution. But, we might consider deprecating random2D() / random3D() after 2.0 is released, aliasing them as randomUnit2D()/ randomUnit3D() say, and potentially removing the original names for these features in a 3.0 release. Further investigation is needed, but that might provide a more consistent, predictable, and extensible foundation in case p5 or add-ons want to add more features later.

For example, p5 itself or a community-contributed add-on could then provide a more complete set of functions, if desired:

  • random()/ random2D()/ random3D() for points inside cubes, all with the same overloads as p5's current random() function (To start, we could rename the min and max parameters to something like start and end. In fact, random() already works this way: the first parameter can be greater than second parameter, so these names would be more accurate. These names also make more sense in the 2D and 3D cases, where these two parameters could be vectors. They'd specify a cuboid, i.e. a rectangular box, by indicating two of its corners. This would be similar to how we specify rectangles in CORNERS mode, although for right now at least, rect() doesn't accept p5.Vector arguments. We could potentially even have random2D() and random3D() take 2D or 3D array arguments and return a randomly selected element, in the same way that random() returns a random element of a 1D array, if that'd be useful.)
  • randomUnit()/randomUnit2D()/randomUnit3D() for points on unit spheres (The first of these would return -1 and +1 with equal probability; it could potentially be omitted but improves consistency and may be convenient.)
  • randomMove()/randomMove2D()/randomMove3D() for points inside spheres, with the parameters minDistance and maxDistance replacing random()'s min and max (The working name “randomMove” uses physical movement as a metaphor, since these functions can be thought of as returning a random displacement.)

Note: This Unity tutorial, which is aimed at beginners, notes a lack of a convenient function for generating points in cubes (or cuboids). The tutorial also shows how a workaround can be explained to beginners. This suggests that built-in random-point-in-cuboid generators might be useful, but also that these aren't essential (except for the 1D case).

Note: Here, I've proposed randomMove() as a possible name for what you've called jit. I'm not sure about it, but I like that randomMove() is descriptive and that it avoids abbreviations. I think jit is creative, and the use of metaphor feels helpful, but ideally it'd be easy for users to guess a function's purpose from its name (edit: to clarify, it's good if similar features have similar interfaces, which is the intention behind random() and randomMove()). I don't think jit will be meaningful to most users without extra context. Also, people who are familiar with jitter from the context of signals may expect it to be Gaussian.

More discussion is required to determine whether the particular nine functions above would be good for p5. I'm mainly proposing that, as a more immediate priority, we discuss how (or whether) to address the existing inconsistencies.

Next steps?

The last feature request I looked at faced similar issues. In that case, implementing a new feature would propagate bugs caused by problems with the existing p5 API. When it comes to variations on random(), extending the API in a coherent way is difficult because the existing API is inconsistent (we could add jit/randomMove(), but extension in general is tricky, and it may be nice to clean up existing features first). It'd help to look at other feature requests, but I suspect these kinds of difficulties will keep arising until the problems with the existing API are addressed (to the extent possible).

After the release of 2.0, I wonder if it might make sense to open a discussion about what 3.0 might look like. Whereas 2.0 modernized p5.js and brought exciting new features, maybe 3.0 could focus on cleaning up problems that have accumulated in the interface. For example, that might involve resolving larger tensions between 2D and 3D rendering features (setting detail for 2D vs. 3D primitives, shapes vs. geometries, p5.Graphics vs. p5.Framebuffer, etc.).

Focusing on existing features wouldn't necessarily preclude the addition of new features; it might just be a way to narrow the scope to features that make the existing API more consistent and intuitive. Examples might include random-point generation inside cubes in 2D and 3D, a modified curveDetail() feature that can set detail levels on 2D primitives (which might fix bugs based on the current API), an arcVertex() feature that resolves an inconsistency while increasing the expressiveness of custom shapes, etc.

I think the last point is worth repeating, since it seems to come up sometimes in discussions: although it's counterintuitive, adding functions can actually reduce complexity, making an API more economical from a user standpoint. If we were to provide a consistent feature set, such as the nine random generators outlined above (including a function such as jit/randomMove()), that might improve conceptual economy. (But some additions might hurt it.)

To be extra clear, let's say we have a table of related features, and some of the features are missing. In that case, it'll tend to be harder to remember which features are available. Also, since patterns will be obfuscated, it might even make the individual features harder to remember. This is especially clear if we borrow an idea from information theory (specifically Kolmolgorov complexity): it's harder to remember the number $0.11101001$ than it is to remember $0.11111111$ or even $0.1111111111111111111\ldots$. Maybe this example could even be a rough metaphor for p5.js 3.0.

Edits:

  1. Added specification of overloads for the reimagined random2D() and random3D() that generate random points in a cuboid (a rectangular box).
  2. Added clarification about how adding functions can sometimes improve economy from a user perspective.
  3. Added link to Unity tutorial about points in cubes (or cuboids).
  4. Added clarification: the nine functions I proposed are just meant to illustrate that it may be helpful to address existing inconsistencies first, before we consider adding convenience features.
  5. Formatting.

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Apr 6, 2025

@GregStanton Thanks for the correction, clearly I never use Vector.random2D and Vector.random3D, so my misperception was just due to a lack of documentation, which is an issue given the vague naming.

Although, that's precisely why I suggested adding API similar to Unity's, that includes the name of the shape and where the random points will be generated.

Random.insideUnitCircle, Random.insideUnitSphere,
Random.onUnitCircle, and Random.onUnitSphere

Your suggested naming scheme doesn't make sense to me. The priority for naming these functions shouldn't be consistency or brevity, it should be descriptiveness. Unity already nailed it imo.

A function like randomUnit shouldn't be added to p5 because it isn't useful enough to justify its existence other than to fill out your API table, which perhaps could be taken as an indication that it's not a good approach.

random and jit should exist independently of the circle and sphere random functions.

Also, jitter is common vernacular right? "First day jitters", "do the jitterbug", "the camera is jittering", etc. I chose jit (jitter) because it's descriptive of the type of movement: uncontrolled displacement.

randomMove is vague and too long. randomMove(3) is the same length as random(-3, 3), so there'd be no point in using it. Shortcuts need to be short. randomMove also sounds too much like something that would perform an action, not output a number. Using the full word, jitter would avoid abbreviation and would be four characters shorter.

@GregStanton
Copy link
Collaborator

GregStanton commented Apr 6, 2025

@quinton-ashley I was just making edits to my previous comment when you replied. I indicated what the edits were at the bottom, in case you want to take a look.

Regarding the "mistake" you made, my point was that it wasn't fundamentally your mistake. A seasoned programmer such as yourself will guess the most reasonable behavior based on a function name, and that's exactly what you did. The real mistake is in the current p5 API. Documentation doesn't fully resolve the issue if the natural interpretation conflicts with the actual behavior.

I think the Unity names are helpful and might be better if we can adapt them to the context of p5. It might take a bit more thought to make that scheme work well within the context of p5, though. I'd like to think more about it, but I'm kind of slammed with work now.

@GregStanton
Copy link
Collaborator

Another quick idea, before I forget (I haven't fully vetted it):

  • random numbers:
    • random([bound1], [bound2])
    • randomDeviation([size]) [optional]
  • random points in cuboids, in 2D and 3D:
    • randomPointInRect([corner1], [corner2]) [optional]
    • randomPointInBox([corner1], [corner2]) [optional]
  • random points on spheres, in 2D and 3D:
    • randomPointOnCircle([radius]) [alias]
    • randomPointOnSphere([radius]) [alias]
  • random points in spheres, in 2D and 3D:
    • randomPointInCircle([radius]) [optional]
    • randomPointInSphere([radius]) [optional]

Notes:

  • "Optional" is meant to indicate a feature that might be considered for an add-on library or for p5 itself. More discussion about the costs vs. benefits of adding these features would be good.
  • "Aliases" indicate features that could potentially replace existing p5 features after deprecation, if it wouldn't be too disruptive. Specifically, randomPointOnCircle(), randomPointOnSphere() are more flexible versions of random2D(), random3D(), with clearer names, and without the confusing inconsistencies with random().
  • An advantage of randomDeviation() over jitter() is that it uses the same random* API as the other features. A disadvantage is that it's longer. It’s hard to find an accurate term that's shorter than "deviation." In three.js, there’s randomFloatSpread(range), which in p5 could become randomSpread(), but that feels confusing because it doesn’t return a spread.
  • Features generating random points on cuboids are omitted, since I guess they may not be as useful.

It still seems to me that the first priority is to resolve the inconsistencies with random(), random2D(), and random3D(), possibly by means of the alias approach mentioned above. But I could create a separate issue for that.

@VANSH3104
Copy link
Contributor

Thanks for the thoughtful discussion both @GregStanton and @quinton-ashley made great points.
I agree with @GregStanton that random2D() and random3D() are a bit misleading if they return unit vectors. Renaming them to something like randomPointOnCircle() / randomPointOnSphere() could improve clarity, especially for beginners.
@quinton-ashley point about avoiding unnecessary symmetry is valid too — we should focus on clarity over consistency.
As for jit vs randomMove(), I like jit for its expressive, artistic feel. Maybe jitter() or randomDisplacement() could be good middle grounds?
Also, I’d love to see a p5.Vector add-on or extension that complements this especially for generating random vectors in different shapes and ranges. Could open up some really creative use cases!

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Apr 6, 2025

@GregStanton I like the new approach with the randomPoint* functions, makes it clear that the functions return a point.

I think perhaps it'd be more useful, and for consistency with the rect and box functions, to offer randomPointInEllipse and randomPointInSpheroid instead. Those functions would take a and b lengths, I think randomPointInRect should take a width and height, which is the default rectMode too, not corners.

Again, randomDeviation is too long. I think anything longer than random, 6 characters, would make it no longer a shortcut and then its main purpose is lost.

There's a precedent for jit with functions like dist. I guess I'd be fine with jitter as a verbose alias too.

@ksen0
Copy link
Member

ksen0 commented Apr 8, 2025

Hi folks, this thread’s getting a bit dense—would love if we could keep things a bit more concise and understandable for newcomers to the topic. Maybe one way forward is trying these ideas out in an add-on library? If it catches on, it could be a good candidate for core later. Plus, starting separately gives more room to experiment with the API details and see what works best: which additional functions provide a lot of value but still keep the additions as a whole focused.

@quinton-ashley
Copy link
Contributor Author

@ksen0 Yeah, I should've put my footnote about adding other random math functionality like Unity's in a separate issue to keep the conversation focussed on jit.

There's no need for an addon library for this, it's 3 lines of code.

function jit(v) {
  return random(-v, v);
}

I spent a long time writing this request in hopes that jit could be added to p5.js directly.

@quinton-ashley quinton-ashley changed the title Add jit function Add jit function Apr 14, 2025
@iologpav

This comment has been minimized.

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