Skip to content
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

Add randomX and randomY functions #7671

Closed
17 tasks
quinton-ashley opened this issue Mar 25, 2025 · 26 comments
Closed
17 tasks

Add randomX and randomY functions #7671

quinton-ashley opened this issue Mar 25, 2025 · 26 comments

Comments

@quinton-ashley
Copy link
Contributor

quinton-ashley commented Mar 25, 2025

Increasing access

This feature could increase beginner-friendliness by making it a bit easier to generate such values as well increase interoperability between P2D and WEBGL mode.

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

I'd like to make a suggestion to add two new functions to p5: randomX and randomY, which would get random values relative to the dimensions of the canvas and the coordinate system origin (which differs between P2D and WEBGL mode).

A margin could be passed as an optional parameter, the distance to extend (positive) or contract (negative) the range from the edges of the canvas.

In WEBGL mode it'd reduce the amount of code needed to do so from this random(-width / 2, width / 2) to just randomX() which in a large file where this is done many times would make user's code smaller and easier to read imo since the function is intuitively named.

@ksen0
Copy link
Member

ksen0 commented Mar 26, 2025

Thanks for the idea @quinton-ashley ! I've edited the description slightly to make it more precise, hope that's alright: that the aspect of access being addressed in beginner-friendliness. Other aspects of access include web accessibility and internationalization. I'm not sure if beginner-friendliness for new contributors and for new users should be separately stated, but I think this idea affects both!

Status: Discussion is open and input is welcome!

For all those reading and new to this topic: Please read the below and chime in. Please do not make PRs before there is a decision to implement this and before an assignment (read more in the contribution guidelines)

Potential feature for next minor release of p5.js 2.0

Right now, we are close to releasing p5.js 2.0 (see timeline), and no new features can be considered for that initial release.

After this, new features will be considered for p5.js 2.1 (or later minor releases, like 2.2 and 2.3).

Except bugfixes (or similar exceptions), new features will not be considered for p5.js 1.x (even though it will continue to be supported and available until summer 2026).

What potential benefits and drawbacks do you see?

The prioritization of a feature is not based on popularity directly, but if you want to see this implemented, please do comment! Besides popularity: it matters what the benefits/drawbacks of adding the feature, and the benefits/drawbacks of not adding it.

For example, for this one, my thoughts are as follows:

🌸 It sounds like the biggest benefit is that it would make picking a random spot in the sketch the same in WEBGL and in Canvas mode, which is a big plus for beginner-friendliness.

🌸 If we agree to include it, it would also be a Good First Issue for new contributors. especially if it includes adding tests and so on.

🐛 As far as I can tell, the only drawback is that in general adding new features creates more chances for bugs in the future, and more maintenance load. This particular feature is super small, and in canvas mode randomX() would be equivalent to random(width)

🌸 The drawback of not adding this feature would be that switching between WEBGL and canvas mode changes random canvas point behavior. Here's a sketch

Do add yours below!

@VANSH3104
Copy link

@ksen0 @quinton-ashley
We also need randomZ() in WEBGL mode to make it compatible with 3D rendering. Additionally, we could allow specifying a range like randomX(min, max), since randomX() currently returns a value across the entire canvas width. This change would provide more control and flexibility to users. Is it right

@ksen0
Copy link
Member

ksen0 commented Mar 27, 2025

@VANSH3104 Good point about randomZ()! What do you envision the usage of randomX(min, max) to be? What would the min and max values be, in this case?

@VANSH3104
Copy link

VANSH3104 commented Mar 27, 2025

@ksen0 For randomX(min, max), the idea is to allow users to specify a custom range within the canvas instead of always using the full width. it become easy if we want to positioning elements within a specific section of the sketch.

@ksen0
Copy link
Member

ksen0 commented Mar 27, 2025

@VANSH3104 with min and max, what are the values, what's the range? If values are in pixels, then the Canvas/WEBGL modes will have different interpretations, because (0,0) means different things there. If values are in proportion (from 0 to 1) then that might itself be a confusion. How do you see the sections of the sketch being reflected in the numbers? Examples would be really helpful here!

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 27, 2025

instead of always using the full width

@VANSH3104 Did you see in the feature request details section that the functions could accept a margin?

See example use of randomY(-60) here:

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

Are you suggesting randomX(leftMargin, rightMargin)?

@VANSH3104
Copy link

@quinton-ashley @ksen0 Thanks for the clarification! I see your point about how Canvas and WEBGL interpret coordinates differently.
@quinton-ashley, yes, I was taking something like randomX(leftMargin, rightMargin). This would allow users to specify a range within the canvas, making it easier to position elements dynamically without needing to manually adjust for different coordinate systems in Canvas and WEBGL modes.

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 27, 2025

So an alternate use when two number parameters are provided?

randomX(-50, 80)

Making leftMargin -50 would expand the range from the left edge to the left by 50 and making rightMargin 80 would expand from the right edge to the right.

EDIT: The problem is that users might confuse it with min and max bounds given the similarity to random.

@GregStanton
Copy link
Collaborator

It's great to see new ideas, and all this discussion! Since this proposal would extend the API, I think this is a good test case for the six API criteria I proposed previously:

  1. Consistency: Provide a similar interface for similar features and different interfaces for different features.
  2. Readability: Design features that are easy to read and understand.
  3. Writability: Make features easy to use and hard to misuse.
  4. Predictability: Respect the principle of least surprise, so that users can guess how features work.
  5. Extensibility: Hide implementation details and lay a foundation for add-on libraries.
  6. Economy: Limit redundant features, and consider add-on libraries for new features.

The main benefit of the proposed features, to me, is writability (here I'm quoting the proposal):

In WEBGL mode it'd reduce the amount of code... from this random(-width / 2, width / 2) to just randomX().

However, my concern is that this benefit is outweighed by a number of significant costs, which are clearer when we look at the other criteria; @quinton-ashley I'm sorry that this is our first major interaction... I bet you have a ton of other ideas that I'd love! I'm just hesitant about this particular idea. I'll explain why below.

Consistency

Single-argument version

In random(50), the argument is an upper bound, whereas in randomX(50), the argument is a margin that's two sided and represents neither a lower nor an upper bound.

Two-argument version

The issue with random(lowerBound, upperBound) vs. randomX(leftMargin, rightMargin) is similar. I think there'd be a strong tendency to interpret randomX(10, 20) as a random number between 10 and 20. That interpretation is actually the first idea that @VANSH3104 proposed, and users may interpret it the same way, especially since that's how random() works.

Readability, Predictability

Although more concise function calls may seem more readable to those who already understand the functions involved, I think there's an overall loss of readability, since the margin concept is a bit confusing:

  1. A negative margin as described on the q5 site seems to correspond to positive padding in CSS. This issue could possibly be addressed by having positive values narrow the range, but the next point suggests it's simply easy to get confused between the two options.
  2. According to the main proposal here and the q5 site, a negative margin will "contract" the range. But in the comment above, @quinton-ashley wrote that "Making leftMargin -50 would expand the range." I'm guessing that was an accident, but accidents like this tend to indicate complexity of some kind; if the feature's designer makes this mistake, I think we can expect a significant percentage of users to make incorrect predictions about how this feature works.
  3. As proposed, randomX(-50) would generate a number from an interval centered at zero in WebGL mode, but in P2D mode, it'd generate a number from an interval that doesn't contain zero at all. We might be able to overcome that source of confusion, if not for the next issue, relating to extensibility.

Extensibility

There is no canvas boundary in the Z direction, so the concept of margin doesn't seem to apply here. Specifying one of three coordinates in an entirely different way would introduce additional complexity.

Economy

There are always costs whenever we add to the API, so we need to consider whether the benefits outweigh those costs. In addition to technical costs like bug fixing and maintenance, there's a conceptual cost to users.

Imagine a software library with only 10 functions. You might think, I can learn this library in a day! Now imagine a library with 10,000 functions. You might quickly be overwhelmed, assuming it will take years to learn the basics. The cost I'm describing is extra conceptual weight.

To be clear, a tiny feature count isn't necessarily good. That might mean that a lot of functionality is packed into a small number of features, making those features hard to learn. What we want is a good power-to-weight ratio, meaning roughly that we want to empower users to do a lot without requiring them to learn a lot.

Actually, p5's random() is a really helpful example. In principle, it's possible to achieve the same effects with the native Math.random(), so you might say p5's random() doesn't add power... But I'd argue that it significantly empowers users, since beginners may not be able to figure out how to generate a random whole number using Math.random(), or how to pick a random element of an array.

On the other hand, I think that if users can understand a coordinate system, they'd be able to figure out how to get the same effect as randomX() and randomY() using only random(). In that sense, we don't seem to gain any power, but we'd be adding three new functions randomX(), randomY(), and randomZ() and a significant amount of conceptual weight.

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 28, 2025

@GregStanton I agree with your assessment of the two argument use, which I didn't propose and I don't think it should be included. I was struggling to articulate that point more clearly and thankfully you've laid it out nicely.

I didn't make a mistake in my explanation though, because range extension from the left margin would require subtraction from the left margin position. Whether -50 or positive 50 would cause expansion from the left margin would be a point of confusion with how it works in CSS. I've since edited the comment to make it clearer that I was merely trying to flesh out that idea, you seem to be under the assumption that I wanted to add that onto the proposal which I don't. You're making a strawman argument.

You also didn't sufficiently address 0 argument use, which I'm sure you'd agree is very readable and intuitive: randomX().

Also with single arg use, such as randomX(50), at first glance it's reasonable to assume that the 50 represents something other than just an upper bound extension (not surprising). The margin concept of expanding or contracting from the edges is conceptually straightforward.

There's significantly less conceptual weight to what I'm proposing compared to most of the existing math related functions in p5. There are also plenty of functions in p5 that don't offer much additional functionality, but rather are simple shortcuts for common tasks. Though adding functions also adds more for beginners to learn, I think that if we keep the randomX and randomY API simple, the benefits outweigh the cons in this case.

Respectfully, I wouldn't have added these functions to q5.js if they didn't meet the criteria for good API.

@VANSH3104
Copy link

@GregStanton I want to clarify that the two-argument version randomX(leftMargin, rightMargin) was my idea. I thought it would help generate random numbers within dynamic margins rather than fixed bounds, making positioning easier in some cases.I agree that it is confusing
I support @quinton-ashley randomX() proposal because:

Simplicity – randomX() is easy to use and adapts to WebGL and canvas modes.
Consistency – It aligns with p5.js functions that simplify common tasks.
Practicality – randomX(margin) allows quick placement without extra calculations.
Minimal Complexity – It’s a small addition with big usability benefits.

I understand @GregStanton concerns about consistency and predictability, and he makes a great point about avoiding confusion. However, I believe that randomX() remains intuitive and useful if kept simple, without the two-argument version.

@ksen0
Copy link
Member

ksen0 commented Mar 28, 2025

I'll try to summarize the current proposed idea, thanks to @VANSH3104 and @quinton-ashley for clarifications, please correct me if I missed something.

  • Add 3 functions: randomX(), randomY() for generating a random location. (Not randomZ(), because of the complications @davepagurek mentions below.)
  • Each has one optional parameter that is a margin in pixels, and perhaps should be constrained to positive values; but not 2 parameters because it's too confusing with random() usage. It's possible even this could cause a lot of confusion because it still has a totally different behavior than random() with one parameter.

Respectfully, I wouldn't have added these functions to q5.js if they didn't meet the criteria for good API.

@quinton-ashley I think it's really important to be mindful that "criteria for good API" is context specific. @GregStanton's criteria make a lot of sense for p5.js more generally, even if for q5.js a different prioritization makes sense. I really agree with Greg's suggestion to carefully consider adding any functions, even small ones, because it makes the whole library more challenging to maintain with a wide and varied contributor base. This is not to say that the function is not useful - I can see myself using this all the time! - but in the case of p5.js, I think the requirements of economy (in terms of API design) is relatively higher priority than it might be in another situation.

Specifically: "It’s a small addition with big usability benefits" - also with additional implementation/maintenance costs of adding unit tests, and updating FES, tutorials, reference potentially with extra explanations to disambiguate with usage of random depending on whether the optional parameter is confusing. I don't think those are massive costs, but they are also not zero.

I think that if users can understand a coordinate system, they'd be able to figure out how to get the same effect as randomX() and randomY() using only random()

@GregStanton I feel like potentially there is a benefit to being able to more easily switch WEBGL vs Canvas, but maybe that means we should rather think about how other parts of the API could make that switch more straightforward? Or whether this is a common challenge?

Summary

This seems like a useful feature, and it would be relatively straightforward to add (code + tests + FES updates + associated documentation). However, I do think that the potential for confusion about parameters (even with a single margin parameter) is not dismissable. I think it would be useful to get some more voices in this thread on whether the single-parameter use is useful enough to justify risk of confusion.

For those reading: to make this a bit more concrete, here is a sketch that does random placement using random and allows smooth switching between the different renderers for WEGBL and Canvas mode.

@davepagurek
Copy link
Contributor

@ksen0 @quinton-ashley
We also need randomZ() in WEBGL mode to make it compatible with 3D rendering. Additionally, we could allow specifying a range like randomX(min, max), since randomX() currently returns a value across the entire canvas width. This change would provide more control and flexibility to users. Is it right

Good point about z! randomZ is possibly a little different, since there's a clear minimum (has to be in front of the camera!) but the maximum can be quite far away. Is a uniform distribution correct for that? I'm not sure what the answer is there, might take some thought.

One other observation is that if you start adjusting depth, the x/y range that is visible changes (less range closer to the camera, more farther.)

My thought is that if there's not a clear answer, it's ok to just do x and y at first, since the "correct" range for z might vary per sketch, so we can ask users to do their own range manually at first with the usual random().

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 28, 2025

@ksen0 I agree that any addition to the library should be considered carefully and also appreciate the discussion of additional ideas from @VANSH3104

I understand that whether this proposal is something worth implementing in p5 depends on other factors too.

I'd consider these functions as useful shortcuts rather than solving a common "challenge" like random does though.

@GregStanton I've seen firsthand how random is empowering for users. I used to teach a lesson to first day or second day beginners that required using Math.random() to generate a range of random integers from 1-100 for a guess the number game. It was actually challenging for some of them and could take several minutes. Their final answers were almost always slightly wrong too. My students were so happy to use random instead of Math.random in later lessons lol. Admittedly the functions I'm suggesting produce values that beginners can quite easily compute with random.

I think what's important to consider then, is whether these are shortcuts that users would remember and prefer to use for this task.

For example, users could fill a background with fill and rect quite easily, but background is still a useful shortcut.

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 28, 2025

@davepagurek I hadn't considered that these functions could take rotation into account.

In case it isn't clear why they shouldn't, for example in WebGL mode users could rotate the y axis such that the canvas contains an x axis that stretches out to infinity, like the z axis does at the default camera position.

My description of the margin parameter "expanding or contracting the range from the canvas edges" is incorrect though if scaling or rotation is applied. Hmm I'll need to think of how to better phrase that.

@davepagurek
Copy link
Contributor

@quinton-ashley good point! in a lot of my own sketches where one might apply rotation, I end up using a depth values based on width/height (maybe a max or min.) In that scenario, we already have x/y versions of this function, so someone could maybe just use those.

@GregStanton
Copy link
Collaborator

@ksen0:

I feel like potentially there is a benefit to being able to more easily switch WEBGL vs Canvas, but maybe that means we should rather think about how other parts of the API could make that switch more straightforward? Or whether this is a common challenge?

This is a great point to consider. I think the sketch you shared clearly shows that, when using the proposed features to generate random coordinates within the bounds of the canvas, it's easier to switch between Canvas and WebGL. I'm not sure how common it is to switch a sketch between renderers like that. Do you have any thoughts on this @davepagurek?

If we want to facilitate that kind of switch, I think that considering other parts of the API is a terrific idea, since it opens up many possibilities. As an initial example, the p5.Renderer class might have leftEdgeX and rightEdgeX properties that'd be inherited by the different renderers. Those could be accessed on the renderer instance that's returned by createCanvas(). That might not be a good idea; my intention is just to illustrate the size of the solution space.

Although I'm not sure whether switching a sketch between P2D and WebGL renderers is common, I do think that conceptual compatibility between Canvas (i.e. the P2D renderer) and WebGL features is a common challenge. It seems unavoidable, given the important aim of making both 2D and 3D graphics accessible. This comment has some examples of such issues in the context of 2D primitives. There are also tensions between the shape and geometry interfaces, as well as p5.Graphics and p5.Framebuffer (these pairs of similar features have different interfaces that serve subtly different purposes; the challenge is to clearly communicate or possibly reduce these differences).

So far, @davepagurek and I have discussed conceptual compatibility on a case-by-case basis, and I took a similar approach in the API audit proposal. For now, I set a reminder to myself to consider incorporating a sub-issue or top-level agenda item aimed at identifying and resolving Canvas vs. WebGL tensions more generally (to the extent possible).

@GregStanton
Copy link
Collaborator

@VANSH3104 Thanks so much for sharing your thoughts based on the API criteria for p5 that I shared. It's very helpful to see how different people interpret them! I think this shows how they work best as a framework for discussion.

@GregStanton
Copy link
Collaborator

GregStanton commented Mar 29, 2025

@quinton-ashley:

"You're making a strawman argument."

I think it might be helpful to do a "friendly vibe check" (a helpful phrase I learned from @nickmcintyre a while back). A straw man, by definition, is based on bad intent. I'll assume you didn't mean to imply that, but I'm hoping we can avoid terms like that. I do think I described the proposed ideas accurately, and I did attribute the two-argument idea to @VANSH3104. If I inadvertently implied that you were advocating for the idea, rather than just brainstorming, that's my mistake! Similarly, I hope I didn't imply anything about the API decisions made for q5, as that project exists in a different context.

It's always hard to disagree in writing, and in a public forum no less! In general, I think the best we can do is to interpret intentions as generously as possible. Of course, that's easier said than done, especially when our brainchildren are involved 😅 I hope you understand where I'm coming from, and I do look forward to future discussions! With those preliminaries out of the way, I'll try to address each of your points.

Margin confusion?

I didn't make a mistake in my explanation though, because range extension from the left margin would require subtraction from the left margin position.

I think this is part of the confusion. In the examples on the q5 site, it looks like a single negative argument causes the range on both sides to be restricted. I think it's reasonable to interpret this to mean that -50 is being applied to the left and to the right. That would imply that applying a negative value to the left edge results in a contraction, rather than an expansion. On the other hand, if we interpret -50 as being added to the left bound, the result would be an expansion.

(There's also the possible confusion with CSS padding that we discussed.)

Zero-argument case

You also didn't sufficiently address 0 argument use, which I'm sure you'd agree is very readable and intuitive: randomX().

That's a good point. I do think randomX() and randomY() with no arguments are readable and intuitive in P2D mode; the corresponding random() with no arguments also generates a random number within a range, so it seems consistent with that use case as well.

I'm not sure yet about WebGL mode. In 3D sketches, rotations are an additional complication. Also, randomZ() (however it's defined) is less intuitive, which is an extensibility issue. If randomZ() were omitted, this would somewhat go against predictability and the principle of least surprise, since users may expect a WebGL coordinate-based feature to account for $x$, $y$, and $z$. But it might be okay to just include randomX() and randomY(), as @davepagurek mentioned.

In any case, random() is also pretty readable, so I think the main benefit of adding randomX() and randomY() is writability: these functions are easier to write, for users who are prepared to add them to their repertoire.

Useful, yes! Also, costs to consider.

I agree with @ksen0's assessment completely: "This is not to say that the function is not useful - I can see myself using this all the time! - but in the case of p5.js, I think the requirements of economy (in terms of API design) is relatively higher priority than it might be in another situation." On that note, I'll share a few thoughts about shortcuts in the context of p5.

Using existing p5 shortcuts as design guides: benefits and pitfalls

There are also plenty of functions in p5 that don't offer much additional functionality, but rather are simple shortcuts for common tasks.

Helpful examples

I think your example of background() is a good one to consider with respect to economy. Although users could use a fill() and rect(), I think that may require users to overcome functional fixedness: instead of thinking of rectangles as shapes that are drawn in a canvas, they need to realize they could be used to effectively change the color of the canvas itself. Changing the color of a canvas is also an extremely common use case; if my functional fixedness hypothesis is correct for even a small percentage of users, then it will affect a lot of people.

I don't think the functional fixedness issue applies to randomX() and randomY(). The first example in the reference illustrates the use of random() for generating numbers in a range. The proposed randomX() and randomY() also generate random numbers in a range, so I don't think they remove a conceptual hurdle in the same way.

Pitfalls

Although I think background() is an example of a useful shortcut, I don't want to imply that we should use the existing p5 API as a design guide: there are currently quite a few problematic aspects of the current API, and that's the reason for my API audit proposal. To be clear, my favorite thing about p5 is that it makes a serious effort to be accessible to a wide range of groups, including beginners. The net result is an amazing library and community. But, it's a big project with a lot of moving parts, and I think there are parts of the API that have room for improvement.

General costs of shortcuts for beginners

My students were so happy to use random instead of Math.random in later lessons lol. Admittedly the functions I'm suggesting produce values that beginners can quite easily compute with random... I think what's important to consider then, is whether these are shortcuts that users would remember and prefer to use for this task.

I appreciate you being candid about this! I think that whether users would remember and prefer these shortcuts is a great question. My current thinking is that these convenience features might not be a net gain for beginners. This is partly because novices and experts think differently. An expert may see a problem, effortlessly select the most convenient approach from several possible strategies, and execute the approach without mistakes. A beginner may see a problem and struggle to remember one approach without sufficient practice. Or, they might have no strategies to begin with, in which case presenting them with multiple approaches may create more work for them, rather than saving work. I'll expand on each of these points briefly.

Cognitive interference

Introducing additional approaches may produce cognitive interference effects. For example, in retroactive interference, new memories make it hard to retrieve old memories. Here, learning about randomX() and randomY() after learning about random() might actually cause users to forget how random() works. It's easy to imagine this happening to a user who starts using randomX() and randomY() in multiple sketches, and then needs to return to the more general random() function for their next sketch. They'd be deprived of regular practice with random(), so they may forget how it works. That's less than ideal, since random() is the more flexible feature.

Extra work

A user who peruses the reference looking for a way to handle randomness will have multiple reference entries to consider; they'll need to assess which is best for their use case and make a decision. This cuts both ways. Although a user looking precisely for the functionality provided by randomX() and randomY() might benefit from those features, a user whose requirements call for random() might need to do extra work to determine that this is the case.

Here's an example. Suppose a user wants to generate random points inside a circle and is looking for a way to do this in p5. The most convenient function for this, in general, would be random(). However, since the user will need to generate random values for $x$ and $y$, it's plausible from the names "randomX and "randomY" that these could be what the user wants. The user may then decide to read the documentation for those features, and eventually realize that these probably are not the right features for them, before turning to random(). That's more work for them, not less.

Summary

I think randomX() and randomY(), at least the zero-argument version, would represent a usability gain for some users. That may include beginners in certain circumstances! My feeling, though, is that additions to the API require a clear net gain with respect to p5's design goals. In this case, it's not clear to me whether there is a net gain, and I think there's a decent chance that inclusion would incur a net loss. In cases like these, I tend to rely on the maxim "When in doubt, leave it out." I do look forward to further discussion.

@VANSH3104
Copy link

VANSH3104 commented Mar 29, 2025

@davepagurek I see your point, and I agree with you. My idea of randomX() can technically be achieved using random() with some additional calculations. While it provides a shortcut, it might not be necessary to extend the API if the same result can be obtained with existing methods. But we should think about it too.
@quinton-ashley Thanks for your appreciation.I completely agree with your point that randomX() and randomY() serve more as convenient shortcuts rather than solving a fundamental challenge like random(). While users can already achieve the same results using random(), having dedicated functions for random X and Y coordinates could make the code cleaner and more intuitive, especially for beginners.

Your example about teaching Math.random() vs. random() is a great illustration of how simplifying functions can improve usability. Even if users can figure out the logic with random(), providing a direct and readable function like randomX() could enhance their workflow.
My original idea can still be achieved using random(), but these functions were meant as a convenience. Now, I see that the margin behavior I described doesn’t hold up when transformations like scaling or rotation are applied. I’ll think more about how to refine the concept so it aligns better with WebGL’s flexibility.

@ksen0 whether switching between Canvas and WebGL is a common challenge, I believe that conceptual compatibility between the two is an ongoing issue. Your suggestion to address Canvas vs. WebGL inconsistencies at a broader level is a great idea, as it would help ensure a smoother experience for developers working across both modes.
We considered modifying p5.Renderer to include properties like leftEdgeX and rightEdgeX, but in WebGL, the visible area depends on the camera position and projection matrix. Unlike P2D (where the edges are fixed at 0 and width/height), WebGL coordinates are transformed by the view matrix, making "left" and "right" dynamic.

Instead of modifying p5.Renderer, we can provide a helper function to handle this

function getLeftEdge() {
  return isWebGL ? -width / 2 : 0;
}

function getRightEdge() {
 //
}

function randomX() {
  return random(getLeftEdge(), getRightEdge());
}
This keeps the API simple while allowing smooth switching between Canvas (P2D) and WebGL modes.

@VANSH3104
Copy link

@GregStanton Thanks for the appreciation.
I appreciate the thoughtful discussion on randomX() and randomY()—especially the emphasis on clarity, usability, and the cognitive load of additional API functions. I'll try to address key points concisely:

Margin Confusion & Interpretation

You bring up a valid point about the ambiguity in margin handling. If a negative argument both reduces the left bound and contracts the range on both sides, that could be confusing.
It is like confusion between margin vs. padding In CSS
If we treat negative values in randomX() like negative CSS margin, we'd expect expansion of the area.If we treat them like negative padding, we'd expect contraction instead.Since different users might expect different behaviors, this needs to be clearly defined in the documentation and examples.

Zero-Argument Case & WebGL Considerations

agree that randomX() and randomY() are intuitive in P2D mode, aligning with random(). However, WebGL introduces additional complexity with dynamic coordinate systems, especially when transformations are involved.
Many users, particularly those working in 2D, may find randomX() and randomY() useful for conciseness, even if they could technically achieve the same with random(width) or random(-width/2, width/2).

Balancing Convience and Api

I align with the concern that while shortcuts can improve usability, they also come with trade-offs:

  • Beginners Like me might find randomX() and randomY() before random(), leading to conceptual gaps.
  • More functions require users to decide between alternatives, increasing the learning curve.
  • Adding many convenience functions can dilute the core API's simplicity.
    randomX() and randomY() don’t necessarily remove a major learning hurdle. Instead, they offer a slight convenience for those already familiar with random().

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Mar 29, 2025

@GregStanton I didn't use the term "strawman argument" to imply ill intent.

Although I didn't expect to have such an in-depth discussion on this, I do appreciate it!

On the point of adding extra work, that could be said of any part of the API. Users looking to have an image set as the background may be disappointed to find that background stretches the image, though that is the more common use.

On the point of cognitive interference, would you agree that generating random values with random for other purposes is more common? Hence the risk would be that users forget to use randomX and randomY, not the other way around right?

My question was whether users would remember and prefer to use randomX and randomY over random when possible; if not they shouldn't be added to p5. You're essentially arguing they're too memorable and could be used too often. 😅

@quinton-ashley
Copy link
Contributor Author

We considered modifying p5.Renderer to include properties like leftEdgeX and rightEdgeX, but in WebGL, the visible area depends on the camera position and projection matrix. Unlike P2D (where the edges are fixed at 0 and width/height), WebGL coordinates are transformed by the view matrix, making "left" and "right" dynamic. Instead of modifying p5.Renderer, we can provide a helper function to handle this

@VANSH3104 That still doesn't make sense. The edges are not fixed in P2D mode either since scaling and rotation can be applied. When the canvas is rotated 90 degrees the x axis doesn't go left to right. What you're suggesting is also longer to write than the current way.

random(getLeftEdge(), getRightEdge());
random(-width / 2, width / 2);

I'd like if we could keep the conversation here focussed on the proposed randomX and randomY functions. 🙏🏻

@quinton-ashley
Copy link
Contributor Author

I'm closing this and will make a new proposal based on the feedback you've all given.

@quinton-ashley
Copy link
Contributor Author

quinton-ashley commented Apr 3, 2025

@GregStanton After reflecting more, I think you're right that randomX and randomY isn't good API.

Even if I avoided false generalization in the function description by adding something like "when no transformations are applied", perhaps not taking transformations into account makes these functions too limited, since then we're back to using random anyway.

I had thought of a new idea that the single input param could be an absolute distance from the initial center of the canvas, but same issue with transformations, interoperability between P2D and WEBGL would go out the window.

if (usingP2D) {
	$.randomX = (v = $.halfWidth) => $.random(-v, v) + $.halfWidth;
	$.randomY = (v = $.halfHeight) => $.random(-v, v) + $.halfHeight;
} else {
	$.randomX = (v = $.halfWidth) => $.random(-v, v);
	$.randomY = (v = $.halfHeight) => $.random(-v, v);
}

Maybe what I was really after was a more convenient way to generate symmetric random values.

@ksen0
Copy link
Member

ksen0 commented Apr 3, 2025

Just catching up on this @quinton-ashley and for what it's worth, the parameter-free version of this I could still imagine being a really nice "Good First Issue" that could help someone add a helpful utility while getting started with contribution (writing tests etc). I feel that beginner-friendliness for contributors as well as users is important, and that includes both creating approachable implementation tasks, as well as inviting folks into the discussion of language/API decisions.

So if anyone on this thread wants to open the discussion to a wider conversation and/or if someone thinks something else that could create some sense of interoperability/parity between P2D and WEBGL, please feel free to open a new issue! This was a very interesting discussion, recapping it in any new issue I'm sure could also be a really interesting read.

@quinton-ashley quinton-ashley mentioned this issue Apr 4, 2025
17 tasks
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