Skip to content
This repository was archived by the owner on Aug 2, 2026. It is now read-only.

feat(demos): add four demos for the seeded random system - #154

Merged
vancura merged 3 commits into
mainfrom
bt-291-add-random-demos-and-migrate-existing-demos-off-mathrandom
Jul 31, 2026
Merged

vancura merged 3 commits into
mainfrom
bt-291-add-random-demos-and-migrate-existing-demos-off-mathrandom

Conversation

@vancura

@vancura vancura commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

Implements BT-291.

The migration half of BT-291 already landed under BT-389, so this PR adds only the new demos. That branch also
determined the scope: it reached for int, float, pick, next, intInclusive, bool, angle, insideRect,
and pointInRange, leaving everything else in the random surface with no demo coverage. These four cover exactly
that gap, one per chapter of the engine's guide-random.md.

Demos

Demo Covers
random-basics shuffle / shuffleInPlace, weighted, gaussian, sign, direction4 / direction8
seeded-worlds BT.randomSeed, seedValue, clone(), fork()
coordinate-patterns hash1i, hash2i, hash3i
noise ValueNoise, PerlinNoise, SimplexNoise, noise2D/3D, fbm2D/3D

Each demo is one file, uses the shared UI kit for all on-screen chrome, works on touch as well as keyboard, and
carries the beginner comment style this repo requires.

Notable decisions

fork() before clone(). fork() draws one number from the parent to seed the child, so cloning first would
have shown a clone that did not match its original - the opposite of the lesson. seeded-worlds forks first.

Noise block sizes stop at 4px. 2px blocks mean 19,200 drawRectFill calls per frame and drop the demo to about
1 FPS. This was measured, not assumed: it is the call count, not the noise sampling and not Rect2i allocation,
both of which I ruled out by measurement. 4px holds 60 FPS in every reachable state.

coordinate-patterns layer slider. Terrain comes from hash2i so it ignores the layer, while decorations come
from hash3i so they follow it. Moving the slider shows the 2D/3D difference directly.

Test plan

This repo has no automated tests by design, so everything below was checked by hand in the dev server.

  • pnpm run preflight passes (format, lint, spellcheck, knip, docs:links, demo registry, build)
  • All four demos load with zero console errors
  • random-basics: all five scenes render; weighted tally reached 73/23/9/0 against the requested 70/20/9/1
  • seeded-worlds: "Copy left seed" makes both halves pixel-identical; clone() matches the original while
    fork() diverges and reports seedValue as unknown
  • coordinate-patterns: jumped 4,000 tiles away and back, compared screenshots with cmp - byte-identical,
    while the far location genuinely differs
  • noise: measured 60+ FPS in every reachable configuration (both block sizes, all three flavors, drift on,
    maximum octaves)
  • Reviewer: check the beginner comments read clearly to someone who has not written code

Notes

  • No file overlap with the BT-389 branch, so both merge cleanly.
  • README demo count was already stale at 40; corrected to 45.
  • Separately: pnpm run dev is broken from any git worktree because vite.config.js:105 hardcodes
    ../blit386/dist/blit386.js. Not fixed here to keep this branch to BT-291's scope; worth its own ticket.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added four interactive demos covering randomization techniques, repeatable seeded worlds, coordinate-based patterns, and procedural noise landscapes.
    • Added controls for scene navigation, camera movement, reseeding, noise style, resolution, zoom, color modes, animation, and display settings.
    • Added demonstrations of shuffling, weighted choices, Gaussian scattering, directional movement, terrain generation, coordinate hashing, and noise visualization.
    • Added side-by-side seed comparisons and demonstrations of cloned and forked random streams.
  • Documentation
    • Expanded the README to cover 45 demos, seeded randomness, embedding, source panels, shell behavior, and centered direct demo URLs.

BT-291. The migration half of that ticket landed under BT-389, so this
adds only the new demos, covering the parts of the random surface the
migration never reached.

- random-basics: shuffle vs shuffleInPlace, weighted, gaussian vs flat
  float, sign, and direction4 vs direction8, as five switchable scenes
- seeded-worlds: two worlds side by side with seeds read back from
  BT.random.seedValue; copying a seed across makes the halves
  identical, plus a clone() vs fork() stream comparison
- coordinate-patterns: an endless world from hash1i/hash2i/hash3i that
  stores nothing; the layer slider shows terrain ignoring the third
  coordinate while decorations follow it
- noise: ValueNoise, PerlinNoise, and SimplexNoise at matched settings,
  with octaves switching noise2D for fbm2D, and a drift toggle driving
  the 3D variants

Block sizes in the noise demo stop at 4px. 2px means 19,200
drawRectFill calls per frame and drops the demo to about 1 FPS, while
4px holds 60 FPS in every reachable state, drift and maximum octaves
included.

Registers all four in DEMO_ORDER and README, and corrects the README
demo count, which was already stale at 40.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Vaclav Vancura <commit@vancura.dev>
@vancura vancura added the cr Needs code review label Jul 31, 2026
@linear-code

linear-code Bot commented Jul 31, 2026

Copy link
Copy Markdown

BT-291

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d35b4037-0c95-4439-b512-faf74b0c419d

📥 Commits

Reviewing files that changed from the base of the PR and between cd9dd81 and 4f95e35.

📒 Files selected for processing (1)
  • src/random-basics.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • blit386/blit386 (auto-detected)
  • blit386/create-blit386 (auto-detected)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/random-basics.js

📝 Walkthrough

Walkthrough

The pull request adds four interactive randomness demos: random operations, seeded worlds, coordinate patterns, and noise landscapes. It registers the demos in navigation and documents them in the README.

Changes

Randomness demos

Layer / File(s) Summary
Demo catalog and documentation
plugins/demo-order.js, README.md
The catalog includes four new demos. The README documents 45 demos and adds a Randomness category.
Interactive random operation scenes
src/random-basics.js
The demo shows shuffling, weighted selection, Gaussian scattering, random signs, and 4-/8-way directions with scene controls and explanatory panels.
Deterministic seeded worlds
src/seeded-worlds.js
The demo generates repeatable pixel worlds from numeric seeds. It supports reseeding, seed copying, and clone-versus-fork stream comparisons.
Stateless coordinate patterns
src/coordinate-patterns.js
The demo renders hashed strips, terrain, and layer-dependent decorations without storing tiles. It supports camera movement and distant travel.
Configurable noise landscape
src/noise.js
The demo renders cached Value, Perlin, and Simplex noise fields with controls for resolution, zoom, colors, octaves, animation drift, and block size.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NoiseDemo
  participant NoiseGenerators
  participant CachedField
  participant Renderer
  NoiseDemo->>NoiseGenerators: Sample configured Value, Perlin, or Simplex noise
  NoiseGenerators->>CachedField: Rebuild cached field
  CachedField->>Renderer: Provide palette-cell values
  Renderer->>NoiseDemo: Render terrain or grayscale blocks
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the addition of four demos for the seeded random system, which matches the pull request’s primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bt-291-add-random-demos-and-migrate-existing-demos-off-mathrandom

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/noise.js (1)

60-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the buffer size from BLOCK_SIZES.

SMALLEST_BLOCK repeats the smallest entry of BLOCK_SIZES. If a smaller block size is added later, MAX_CELLS stays too small and this.cells[...] writes past the end of the Uint8Array, which JavaScript discards silently. Compute the smallest block from the array.

Proposed refactor
-// The smallest block we ever draw decides how big the sample buffer has to be.
-const SMALLEST_BLOCK = 4;
-const MAX_CELLS = (DISPLAY_W / SMALLEST_BLOCK) * (DISPLAY_H / SMALLEST_BLOCK);
+// The smallest block we ever draw decides how big the sample buffer has to be. Reading it
+// straight out of BLOCK_SIZES means the buffer keeps fitting if a size is added later.
+// Math.min(...BLOCK_SIZES) spreads the list out into separate arguments for Math.min.
+const SMALLEST_BLOCK = Math.min(...BLOCK_SIZES);
+const MAX_CELLS = Math.ceil(DISPLAY_W / SMALLEST_BLOCK) * Math.ceil(DISPLAY_H / SMALLEST_BLOCK);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/noise.js` around lines 60 - 65, Update the `SMALLEST_BLOCK` constant in
`src/noise.js` to derive its value from `BLOCK_SIZES` rather than duplicating
the current minimum. Keep `MAX_CELLS` based on this derived minimum so it
automatically accommodates any smaller block size added to `BLOCK_SIZES`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/coordinate-patterns.js`:
- Around line 266-293: Update the tile-rendering loop in the grid drawing method
so the final row’s rectangle height is clipped to the remaining space within
GRID_H, rather than always using TILE. Apply the same clipped height to the tile
and decoration rendering as appropriate, while preserving full TILE height for
all earlier rows and keeping the frame drawn last.

In `@src/noise.js`:
- Around line 262-264: In the sample method’s animation comment, replace the
British spelling “travelled” with the American spelling “traveled”; leave the
surrounding behavior and code unchanged.
- Around line 385-387: Update the drift checkbox handling in the render/UI flow
to detect when the returned value differs from the previous this.animate value,
and set needsRebuild for that change so cached 2D/3D noise is regenerated.
Remove the stale comment claiming render() adjusts blockIndex when drift is
enabled; leave unrelated checkbox behavior unchanged.

In `@src/random-basics.js`:
- Around line 481-486: Update the clampInt upper bounds in the bug position
update to use area.x + area.width - 1 and area.y + area.height - 1, keeping the
lower bounds unchanged so the bug remains within the final pixel of its Rect2i
area.

In `@src/seeded-worlds.js`:
- Around line 360-364: Update rollSeed() to use a dedicated private random
generator for seed selection instead of the shared BT.random stream reseeded by
generateWorld(). Initialize or reuse that generator independently, while
preserving the inclusive SEED_MIN-to-SEED_MAX range.

---

Nitpick comments:
In `@src/noise.js`:
- Around line 60-65: Update the `SMALLEST_BLOCK` constant in `src/noise.js` to
derive its value from `BLOCK_SIZES` rather than duplicating the current minimum.
Keep `MAX_CELLS` based on this derived minimum so it automatically accommodates
any smaller block size added to `BLOCK_SIZES`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a80b8ce8-4fbc-408f-80bd-5d1394819d63

📥 Commits

Reviewing files that changed from the base of the PR and between 60e7565 and 2c99f69.

📒 Files selected for processing (6)
  • README.md
  • plugins/demo-order.js
  • src/coordinate-patterns.js
  • src/noise.js
  • src/random-basics.js
  • src/seeded-worlds.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • blit386/blit386 (auto-detected)
  • blit386/create-blit386 (auto-detected)

Comment thread src/coordinate-patterns.js
Comment thread src/noise.js
Comment thread src/noise.js Outdated
Comment thread src/random-basics.js
Comment thread src/seeded-worlds.js
Review follow-ups on the four new random demos.

coordinate-patterns clipped its tile rows to the grid window. The grid
is a cutout in the middle of the screen, not the screen itself, so the
first and last rows spilled over the frame and covered the caption
above and the panels below. Verified across all 64 scroll offsets that
every drawn row and decoration now stays inside the window.

seeded-worlds picks new seeds from a private generator. rollSeed() drew
from BT.random, which generateWorld() had just reseeded, making each
new seed a fixed consequence of the previous one: "Copy left seed"
followed by "New right" handed back the identical seed every time, so
the button looked broken. Confirmed against the engine's Random - four
identical seeds before, four distinct after.

noise rebuilds the field when drift is switched off, not just on.
Drifting samples the 3D generators and standing still samples the 2D
ones, so without this the picture kept showing the last 3D frame while
the panel said otherwise. Also drops a stale comment about render()
adjusting the block size, which stopped being true when the 2px option
was removed, derives SMALLEST_BLOCK from BLOCK_SIZES so a finer size
cannot outgrow the buffer, and corrects "travelled" to "traveled".

random-basics keeps its bugs inside their squares. The clamp used
area.x + area.width, one past the last pixel a Rect2i covers.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Vaclav Vancura <commit@vancura.dev>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/random-basics.js`:
- Around line 481-486: Update stepBug() so bug.pos is clamped to bounds inset by
the half-size of the 5x5 marker drawn by renderBug(), keeping the complete
marker inside the Rect2i while preserving the existing three-pixel movement and
area-boundary behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ac05a25-3d1c-4b91-be92-81e956263e32

📥 Commits

Reviewing files that changed from the base of the PR and between 2c99f69 and cd9dd81.

📒 Files selected for processing (4)
  • src/coordinate-patterns.js
  • src/noise.js
  • src/random-basics.js
  • src/seeded-worlds.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • blit386/blit386 (auto-detected)
  • blit386/create-blit386 (auto-detected)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/seeded-worlds.js
  • src/noise.js
  • src/coordinate-patterns.js

Comment thread src/random-basics.js Outdated
Each bug is drawn as a 5x5 square centered on its position, but the
clamp bounded that center to the wander area itself. At any edge the
marker straddled the frame by two pixels: a bug at x = 24 in a square
starting at x = 24 drew from x = 22.

The bounds now come in by BUG_REACH at each end, and both the clamp and
the drawing read the same two constants so they cannot drift apart.
Step size and the wander areas are unchanged.

Checked by driving 3,200 positions into every edge and corner of both
squares: the whole marker stays inside the frame every time.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Vaclav Vancura <commit@vancura.dev>
@vancura
vancura merged commit 88abe9b into main Jul 31, 2026
6 checks passed
@vancura
vancura deleted the bt-291-add-random-demos-and-migrate-existing-demos-off-mathrandom branch July 31, 2026 05:16
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cr Needs code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant