Skip to content

fix(PatternGenerator): keep randomWalk within the value range - #1484

Open
Jaybhade wants to merge 1 commit into
Tonejs:devfrom
Jaybhade:fix/random-walk-single-value
Open

fix(PatternGenerator): keep randomWalk within the value range#1484
Jaybhade wants to merge 1 commit into
Tonejs:devfrom
Jaybhade:fix/random-walk-single-value

Conversation

@Jaybhade

@Jaybhade Jaybhade commented Aug 9, 2026

Copy link
Copy Markdown

The bug

randomWalk is the only pattern generator that doesn't bound its index, and with a single value it walks straight off the end of the array and never comes back:

const pattern = PatternGenerator(1, "randomWalk");
// yields 1, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, …
// 0 is the only valid index

Index 0 is both the bottom and the top of a one-value range. The bottom check comes first, so it wins and steps up to 1. From there neither boundary check can ever match again — index === 0 is false, and index === numValues - 1 is the same test — so every following step is an unguarded random ±1 with nothing above it.

Through the public API that means a one-value Tone.Pattern hands undefined to its callback on almost every tick, since _tick does this._values[index.value]:

const pattern = new Tone.Pattern(
	(time, note) => {
		// note is undefined
		synth.triggerAttackRelease(note, 0.1, time);
	},
	["C4"],
	"randomWalk"
).start(0);

A single-value array is legal — PatternGenerator only asserts numValues >= 1 — and it is easy to land on when values comes from data and is assigned at runtime.

The fix

Hold the index still when there is nowhere to step to. Every other generator already pins to 0 for a single value through clamp(index, 0, numValues - 1), so this just brings randomWalk in line with its neighbours. For two or more values the walk is unchanged.

Tests

Two tests added next to the existing randomWalk test:

  • never randomly walks outside of the range of values — asserts the invariant the bug broke, over sizes 1 through 4.
  • randomly walks in place when there is only one value — deterministic, expects ten 0s.

The existing randomWalk test can't catch this: it uses five values and only asserts that consecutive indices differ by 1, which stays true while the walk is drifting past the end of the array.

Both new tests fail on dev and pass with the change. Before the fix:

❌ PatternGenerator > Patterns > randomly walks in place when there is only one value
      AssertionError: expected [ 1, +0, 1, +0, 1, 2, 3, 2, 1, +0 ] to deeply equal [ Array(10) ]

Verification

npm test for the event group, which covers the changed file — 6/6 test files, 182 passing, 0 failing (180 before the two new tests). npm run lint and npm run spellcheck are both clean, and prettier reports no formatting changes. I couldn't get a single-browser run of all 145 test files to finish on this machine — it wedges partway through with unrelated Clock/TickSource timing failures that don't reproduce when that group runs on its own — so the remaining groups are covered by CI here rather than locally.

With a single value the walk has nowhere to step: index 0 is both the
bottom and the top, the bottom check wins and increments unconditionally,
and from index 1 onwards neither boundary check matches again, so the
walk drifts off the end of the array forever. Tone.Pattern then passes
undefined to its callback on every tick.

Hold the index still when there is only one value, which matches what
every other generator does for that case. The walk is unchanged for two
or more values.
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