Skip to content

Fix seeding bugs and correct release metadata for 0.3.0 - #5

Merged
jshaw merged 2 commits into
masterfrom
claude/code-review-arduino-release-cf6eb6
Aug 14, 2026
Merged

Fix seeding bugs and correct release metadata for 0.3.0#5
jshaw merged 2 commits into
masterfrom
claude/code-review-arduino-release-cf6eb6

Conversation

@jshaw

@jshaw jshaw commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Code review of the tagged 0.2.1 release. arduino-lint --compliance strict passed clean on 0.2.1 and all examples compiled, so the problems here are not spec violations — they are metadata that is valid but wrong in effect, plus four correctness bugs.

Ships as 0.3.0, not a patch: the PRNG change means a given seed produces different noise than it did in 0.2.x. Anyone who saved a seed to reproduce a pattern will need to re-pick it.

Correctness fixes

reseed() before the first noise() call was silently discarded. It regenerated the table but never set initialized, so the lazy init in noise() immediately overwrote it with init(millis()).

Seed 0 did not seed, and the automatic seed rarely varied. Seeding went through randomSeed(), whose AVR implementation is if (seed != 0) srandom(seed); — so init(0)/reseed(0) were no-ops. init() with no argument passes millis(), which is ~0 that early in a sketch, so the "automatic random seed" path frequently produced an identical permutation on every board and every boot. Replaced with a private xorshift32, which also makes a given seed reproducible across AVR/ESP32/Teensy (Arduino's PRNG is not the same on all three) and stops generatePerm() from clobbering the sketch's own random() state.

fbm() with octaves < 1 returned NaN. maxValue stayed at 0.0, and scaledFbm passed the NaN straight into servo.write(). Now clamped to at least 1.

generatePerm() also now shuffles in place within perm[0..255] instead of into a separate 256-byte stack buffer that was allocated lazily from inside noise() — on an Uno the servo example leaves 584 bytes for locals, so an unpredictable 256-byte frame was worth removing. This fell out of the rewrite rather than being separate work.

Packaging fixes

Dropped depends=Servo,ESP32Servo,PWMServo. library.properties has no per-architecture dependency mechanism, so this installed all three servo libraries on every board — for a library whose core noise API needs none of them. I confirmed the Teensy consequence locally: with Library Manager's PWMServo installed alongside the Teensy core's bundled copy, the resolver reports Alternatives for PWMServo.h: [PWMServo@2.1 PWMServo@2.1] and picks the sketchbook copy over the core's. The README now documents the per-board install as a table instead.

includes=SimplexNoise.h — previously also listed SimplexServo.h, so the IDE dragged a servo library into noise-only sketches.

License. The LICENSE file was GPL-3.0 while the README and both source headers said public domain. GPL-3 on an Arduino library is strong copyleft that propagates into users' sketches, which is almost certainly not the intent and contradicts Gustavson's public-domain original. Now MIT, with license=MIT in library.properties and the upstream public-domain attribution preserved.

⚠️ Reviewer note: relicensing is effectively one-way. There is one merged outside contribution (szekelyisz, a24ea8e), though that code was fully rewritten in v0.2. Worth a second look before merging.

keywords.txt no longer advertises mapNoise, fastFloor, dot, F2 and G2 — all private.

Documentation fixes

The "What's New in v0.2.1" list claimed "Memory optimization: Uses PROGMEM for constant data to save RAM". Checking the v0.1 tag, static const uint8_t p[256] PROGMEM and pgm_read_byte were already there — that bullet was never new. Also replaced the "True randomness" feature claim, added Library Manager install instructions, and pointed the release checklist at arduino/library-registry for the first-time submission.

CI

Stopped installing PWMServo from Library Manager. The Teensy core bundles it, and the LM copy shadowed the bundled one — CI was validating something Teensy users don't actually get. Verified Teensy still compiles without it.

Verification

  • arduino-lint --compliance strict --library-manager submit — clean, no errors or warnings
  • 3 examples × arduino:avr:uno, esp32:esp32:esp32, teensy:avr:teensy41 — 9/9 compile
  • Host harness with an Arduino shim: seed determinism, seed 0 honoured, distinct seeds diverge, fbm(0) and fbm(-3) finite, output ranges sane over 20k samples
  • Bug A confirmed in two fresh processes — reseed(555)→noise() and init(555)→noise() now both yield 0.39794308497523956
  • Shuffle validated across seeds 0, 1, 555, 12345, 0xFFFFFFFF: valid 256-permutation, upper half mirrored, permMod12 consistent

AVR cost: ~+142 bytes flash, +2 bytes RAM (xorshift is slightly larger than the random() calls it replaced).

Deliberately out of scope

  • permMod12[512] is 512 bytes of pure cache for perm[i] % 12; with perm[512] that's 1024 bytes, and the noise-only example still sits at 64% of an Uno's RAM. Biggest remaining win.
  • double throughout is free on AVR but software-emulated on ESP32 and Teensy 4.x. Switching to float would be a real speedup on two of the three CI targets but changes public signatures.
  • Source header comments still say "(2025)" while LICENSE now says 2026.

🤖 Generated with Claude Code

jshaw and others added 2 commits August 12, 2026 17:18
Code review of the 0.2.1 release turned up four correctness bugs and
several pieces of packaging metadata that were valid per arduino-lint
but wrong in effect.

Correctness:

- reseed() never set `initialized`, so a reseed() issued before the
  first noise() call was silently discarded by the lazy init in
  noise(), which reseeded from millis() instead.
- Seeding went through randomSeed()/random(). randomSeed() ignores a
  seed of 0, so init(0)/reseed(0) did not seed at all; init() with no
  argument passes millis(), which is ~0 that early in a sketch, so the
  "automatic" seed frequently produced an identical table every boot.
  Arduino's PRNG also differs across AVR/ESP32/Teensy, so a given seed
  was not portable, and generatePerm() clobbered the sketch's own
  random() state. Replaced with a private xorshift32.
- fbm() with octaves < 1 left maxValue at 0 and returned NaN, which
  callers feed straight into servo.write(). Now clamped to >= 1.

generatePerm() now shuffles in place within perm[0..255] rather than
into a 256-byte stack buffer that was allocated lazily from inside
noise().

Packaging:

- Dropped `depends=Servo,ESP32Servo,PWMServo`. library.properties has
  no per-architecture mechanism, so this installed all three servo
  libraries on every board for a library whose core API needs none of
  them. It also shadowed the Teensy core's bundled PWMServo.
- `includes` no longer pulls SimplexServo.h into noise-only sketches.
- Added `license=MIT` and replaced the GPL-3.0 LICENSE file, which
  contradicted the public-domain intent stated in the README and both
  source headers.
- keywords.txt no longer advertises private members.

Version is 0.3.0, not a patch: the PRNG change means a given seed
produces different output than in 0.2.x.

Verified: arduino-lint --compliance strict --library-manager submit is
clean; all 3 examples compile for arduino:avr:uno, esp32:esp32:esp32
and teensy:avr:teensy41; the shuffle produces valid permutations and
the seeding fixes were confirmed on the host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The esp32 matrix job failed on PR #5 with a network timeout fetching the
xtensa toolchain from GitHub releases, during "Install cores" and before
any library code was compiled. The same commit passed the esp32 job in a
sibling run, and re-running it went green, confirming it was transient.

Retry core installs up to 3 times with a short backoff. A persistent
failure still fails the job, so real breakage is not masked.

Also restrict the push trigger to master and tags. Previously every PR
commit ran the full matrix twice, once for push and once for
pull_request, which doubled CI cost and doubled the exposure to exactly
this kind of flake.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jshaw
jshaw merged commit 754ce79 into master Aug 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant