[p5.js 2.0+ Bug Report]: getURLParams() mis-parses valueless parameters and never percent-decodes values
Maintainers usually reply within 2 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript
- Domain
- frontend
Research direction
Start at getURLParams in src/core/environment.js:1058 and run the four standalone cases from the issue (the inline regex snippet) to see the mis-parse before touching anything. The suggested replacement is Object.fromEntries(new URLSearchParams(location.search)), which handles valueless keys, percent-decoding and + as space; drop the lastIndex workaround with it. Add regression tests for ?debug&x=1, ?a=1&b, ?name=Hello%20World and ?q=a+b under the environment unit tests, and check the getURLParams reference docs for the double-decode note.
Written by the indexing model from the issue text.
Description
Most appropriate sub-area of p5.js?
- Core/Environment/Rendering
p5.js version
2.3.2 (verified on main @ 94fb07d)
Web browser and version
All — the parser is a hand-rolled regex, not a browser API.
Operating system
All
Steps to reproduce this
Steps:
- Open any sketch with
?debug&x=1appended to the URL. - Log
getURLParams(). - It returns
{ debug: "x" }— a pair that does not exist in the query string — andx=1is gone entirely. - Try
?name=Hello%20World: the value comes back still percent-encoded.
Snippet:
function setup() {
print(getURLParams());
}
The parser can be run standalone to see all the cases:
const re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim;
function current(search) {
let m; const v = {};
while ((m = re.exec(search)) != null) {
if (m.index === re.lastIndex) re.lastIndex++;
v[m[1]] = m[2];
}
return v;
}
current('?debug&x=1'); // { debug: "x" } expected { debug: "", x: "1" }
current('?a=1&b'); // { a: "1" } expected { a: "1", b: "" }
current('?name=Hello%20World'); // { name: "Hello%20World" } expected { name: "Hello World" }
current('?q=a+b'); // { q: "a+b" } expected { q: "a b" }
Cause
// src/core/environment.js:1058
fn.getURLParams = function () {
const re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim;
let m;
const v = {};
while ((m = re.exec(location.search)) != null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
v[m[1]] = m[2];
}
return v;
};
Three problems:
- The separator group accepts
&as well as=.(?:[&=])means a parameter with no value matches&debug&xas keydebug, separator&, valuex— it absorbs the next parameter's name as its own value, and consumes the realx=1pair along with it. A valueless parameter does not just get skipped; it corrupts its neighbour. - Nothing is decoded. No
decodeURIComponent, and+is not translated to a space, so any value containing a space,&,/or a non-ASCII character arrives in its encoded form. - A repeated key keeps only the last occurrence, and the returned object is a
{}literal, so it inheritstoString,constructor,hasOwnPropertyand friends. Code that testsif (params.constructor)gets a surprising truthy value.
Expected behaviour
Query strings should be parsed per the URL standard: values decoded, + treated as a space, and a parameter with no value reported as an empty string rather than absorbing the parameter after it.
Suggested fix
The platform already does all of this correctly, and URLSearchParams has been available in every browser p5 2.x supports for years:
fn.getURLParams = function () {
return Object.fromEntries(new URLSearchParams(location.search));
};
That also removes the lastIndex workaround (needed only because of the zero-length-match hazard in the hand-rolled loop) and the prototype-inherited keys.
Two notes on compatibility, since this is a behaviour change and not purely a fix:
- Valueless parameters start appearing as
''instead of being mis-parsed. Any sketch relying on the current output for?flagwas getting wrong data, but it is still a change worth a line in the release notes. - Values arrive decoded. A sketch that was calling
decodeURIComponent()on the result itself would now double-decode. Worth mentioning in the reference docs forgetURLParams().
If you'd prefer to preserve repeated keys as arrays while you're in there, that is easy to add on top — but it is a separate decision, so I've left it out of the suggestion.
I'd be glad to open a PR with the replacement and tests covering all four cases above.
- Dominant language
- JavaScript
- Stars
- 24.1k
- Forks
- 3.9k
- Avg merge
- 3d 19h
- Merged PRs (30d)
- 33
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from processing/p5.js
-
[p5.js 2.0+ Bug Report]: SVG importer does not respect preserveAspectRatio="none" for <symbol>/<use>Possibly taken @Danyccsf claimed this today. OpenArea:Core p5.js 2.0+
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
processing/p5.js#9257 · 2 comments · 1 assignee ·
Maintainers usually reply within 2 days
-
Add unit tests for noiseDetail()Possibly taken @Pcmhacker-piro claimed this 2 days ago. OpenArea:Math Enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
processing/p5.js#9253 ·
Maintainers usually reply within 2 days
-
[p5.js 2.0+ Bug Report]: storeItem() validation branches are missing `return`, so bad input throws a TypeError or is stored anywayPossibly taken @iUjjwalRaj claimed this 2 days ago. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
processing/p5.js#9242 ·
Maintainers usually reply within 2 days
-
[p5.js 2.0+ Bug Report]: Typo in Spanish reference documentation for ellipseMode()Possibly taken @cgutierrezval claimed this 5 days ago. OpenInternationalization p5.js 2.0+
Difficulty 1/5 Under an hour Newbie friendliness 95/100
processing/p5.js#9231 · 3 comments ·
Maintainers usually reply within 2 days
-
[p5.js 2.0+ Bug Report]: ReferenceError: p5 is not defined when calling loadPixels/get/copy/mask on p5.MediaElement in ESMPossibly taken @Pcmhacker-piro claimed this 10 days ago. OpenArea:Core Area:DOM
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
processing/p5.js#9189 · 1 comment ·
Maintainers usually reply within 2 days
All issues in processing/p5.js
Similar issues
-
ci-install-db-tools stall-case tests flake: stalled apt-get can be killed before it logs its callOpeneffort:low model:light plan planner:opus-5-5 tests
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
Bug 🐞
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
mozilla-mobile/firefox-ios#35986 ·
Maintainers usually reply within 1 day
-
component:sight
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
agentic-os-org/ANOLISA#6738 · 2 comments ·
Maintainers usually reply within 1 day
-
bug Durable Agents Observability (AI Telemetry) status: needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
mastra-ai/mastra#26470 · 1 comment ·
Maintainers usually reply within 1 day
-
feature/cohorts feature/feature-flags team/feature-flags
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day