Skip to content

feat(highways): per-splitscreen-panel Camera Director cameras - #823

Merged
topkoa merged 7 commits into
mainfrom
feat/highway-per-panel-camera
Jul 9, 2026
Merged

topkoa merged 7 commits into
mainfrom
feat/highway-per-panel-camera

Conversation

@topkoa

@topkoa topkoa commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

Phase 1 of making Camera Director splitscreen-capable. The three 3D highways now read the Camera Director camera bridge per panel, so each splitscreen panel renders its own camera (independent orbit / height / zoom / tilt / pan) instead of every panel sharing the focused one. This is the renderer-side foundation; camera_director already publishes the per-panel map (window.__h3dCamCtlPanels), so tiled splitscreen works against it today.

How

A shared _freeCamFor(canvas) resolver in each highway:

prefer window.__h3dCamCtlPanels[panelIndexFor(canvas)]  // this panel
  else window.__h3dCamCtl                                // global / focused
  else null                                              // stock (Camera Director absent)

Defensive on the splitscreen global name (feedBackSplitscreen || slopsmithSplitscreen, rename in flight), NaN-safe, allocation-free — mirrors the existing _bgPanelKey per-panel pattern.

  • highway_3d (guitar): already read the global bridge; now sourced from the resolver (per-panel).
  • keys_highway_3d: adopts the bridge for the first time — layers dolly/height/orbit + pan/pitch onto its pan/zoom follow rig.
  • drum_highway_3d: adopts the bridge — a new per-frame applyCamera() folds static base pose + kick-pulse dip + free-cam offsets (its camera was previously static).
  • Pop-out (follower window) needs no highway change: one panel per window, so the resolver yields whatever camera the plugin set in that window.

Safety / compatibility

  • Camera Director absent → resolver returns null → renderers behave byte-for-byte as before. The keys "default look unchanged" unit test confirms the stock path.
  • Offsets are Number.isFinite-coerced so a malformed bridge can never feed NaN into position/lookAt.

Testing

  • Resolver logic unit-checked (per-panel select, stock fallback, throw-safety, slopsmith-alias) in plugins/{keys,drum}_highway_3d/tests/camera_bridge.test.js.
  • node --test per file: drum 21/21, keys 56/56 pass (+6 each: the new camera_bridge suite).
  • node --check clean on all three.
  • JS-only plugin edits — no native or Tailwind rebuild.

Part of the larger "named, shareable camera profiles for splitscreen" effort (camera_director + splitscreen companion PRs to follow).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added optional Camera Director free-cam support to the 3D highway views, including per-panel split-screen overrides.
  • Bug Fixes
    • Improved defensive splitscreen panel resolution and Camera Director selection, with safe fallbacks when data is missing or invalid.
    • Updated camera updates to apply position/look-at consistently (including hit “dip” behavior) rather than mutating only the Y position.
  • Tests
    • Added Node.js automated coverage for the Camera Director camera-bridge resolver in both drum and keys 3D plugins.
  • Chores
    • Bumped plugin manifest versions for affected 3D highway plugins.

Make the three 3D highways read the Camera Director bridge per panel so each
splitscreen panel renders its own camera (independent orbit/height/zoom/tilt/pan),
instead of all panels sharing the focused camera.

- Add a shared `_freeCamFor(canvas)` resolver to each highway: prefer this panel's
  entry in `window.__h3dCamCtlPanels[panelIndexFor(canvas)]`, fall back to the
  global `window.__h3dCamCtl`, else null (100% stock). Defensive on the splitscreen
  global name (feedBackSplitscreen || slopsmithSplitscreen), NaN-safe, allocation-free.
- highway_3d (guitar): source `_freeCam` from the resolver (was global-only).
- keys_highway_3d: adopt the bridge for the first time — layer dolly/height/orbit +
  pan/pitch offsets onto the pan/zoom follow rig at the camera write.
- drum_highway_3d: adopt the bridge — new per-frame `applyCamera()` folds the static
  base pose + kick-pulse dip + free-cam offsets.
- In a follower (popped-out) window there is one panel, so the resolver yields
  whatever camera the plugin set in that window; no highway change needed for pop-out.

Camera Director absent → resolver returns null → renderers behave exactly as before.
Bump each plugin patch version. Existing plugin tests pass (drum 15, keys 30); the
keys "default look unchanged" test confirms the stock path is byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Three highway plugins now resolve Camera Director bridges per panel, apply the resolved camera control data during rendering, add bridge-resolution tests, and bump their manifest versions.

Changes

Camera Director free-cam integration

Layer / File(s) Summary
Drum bridge resolver
plugins/drum_highway_3d/screen.js
Introduces shared bridge resolver helpers and exports them through the drum plugin test surface.
Drum camera application
plugins/drum_highway_3d/screen.js, plugins/drum_highway_3d/plugin.json
Stores the base camera pose as nullable state, composes the per-frame camera transform, updates kick decay handling, and bumps the manifest version.
Drum bridge tests
plugins/drum_highway_3d/tests/camera_bridge.test.js
Adds Node-based coverage for per-panel resolution, fallback behavior, error handling, and splitscreen API selection.
Highway camera bridge wiring
plugins/highway_3d/screen.js, plugins/highway_3d/plugin.json
Reworks splitscreen panel lookup, adds per-panel free-cam resolution, uses the resolved bridge during camera activation, and updates the manifest version.
Keys bridge and camera override
plugins/keys_highway_3d/screen.js, plugins/keys_highway_3d/plugin.json
Adds bridge resolvers, applies Camera Director offsets in updateScene(), exports the test hooks, and bumps the manifest version.
Keys bridge tests
plugins/keys_highway_3d/tests/camera_bridge.test.js
Adds Node-based coverage for the bridge resolver helpers and splitscreen API selection.

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

Possibly related issues

Possibly related PRs

  • got-feedBack/feedBack#794: Both PRs modify plugins/keys_highway_3d/screen.js’s camera transform path, and this PR extends that path with Camera Director overrides.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: per-panel Camera Director support across the highway screens.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/highway-per-panel-camera

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

Copilot AI 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.

Pull request overview

Adds per-splitscreen-panel Camera Director support to the 3D highway renderers so each panel can render with its own independent camera (with safe fallback to the prior “stock” camera when Camera Director is absent/disabled).

Changes:

  • Introduces a per-plugin _freeCamFor(canvas) resolver that prefers window.__h3dCamCtlPanels[panelIndex] and falls back to window.__h3dCamCtl.
  • Updates keys_highway_3d and drum_highway_3d to apply Camera Director free-cam offsets on top of their existing camera rigs.
  • Updates highway_3d to resolve the Camera Director bridge per panel (including for the “wide pane” gating logic), and bumps plugin versions.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
plugins/keys_highway_3d/screen.js Adds per-panel bridge resolver and layers free-cam offsets onto the existing pan/zoom follow rig.
plugins/keys_highway_3d/plugin.json Bumps plugin version to 0.2.1.
plugins/highway_3d/screen.js Adds per-panel resolver and uses it to drive Camera Director enablement + transforms.
plugins/highway_3d/plugin.json Bumps plugin version to 3.31.4.
plugins/drum_highway_3d/screen.js Adds per-panel resolver and a per-frame applyCamera() that combines base pose + kick dip + free-cam offsets.
plugins/drum_highway_3d/plugin.json Bumps plugin version to 0.3.2.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/highway_3d/screen.js Outdated
Address a review note on the free-camera block: the comments described the
bridge as "per-panel-aware" without naming the actual globals. Spell out that
_freeCam comes from _freeCamFor(highwayCanvas) — window.__h3dCamCtlPanels[
panelIndexFor(canvas)] when split, else the global window.__h3dCamCtl, else
null — and update the nearby comment that mentioned only __h3dCamCtl. Comment-
only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Comment thread plugins/highway_3d/screen.js Outdated
Comment thread plugins/drum_highway_3d/screen.js
Comment thread plugins/keys_highway_3d/screen.js Outdated
…r tests

Three review findings on the per-panel camera work:

- highway_3d: _bgPanelKey() resolved splitscreen via window.feedBackSplitscreen
  only, while _freeCamFor() uses the feedBackSplitscreen||slopsmithSplitscreen
  alias it claims to "mirror". If the rename lands, per-panel background settings
  would silently stop being per-panel while the camera stayed per-panel. Resolve
  the alias the same way in _bgPanelKey.
- drum_highway_3d: applyCamera()'s "before first positionCamera()" guard tested
  `_camBaseH == null`, but _camBaseH/_camBaseD were initialized to 0, so the guard
  never fired (and could apply a base-0 pose for a frame). Initialize to null.
- keys + drum: the PR claimed the Camera Director resolver was unit-checked, but
  nothing exercised it. Extract the resolver into pure, exported helpers
  (_resolveFreeCam + _ssApi), delegate the per-instance _freeCamFor to them, and
  add tests/camera_bridge.test.js covering per-panel select, global fallback,
  null-when-absent, throw-safety, and the slopsmith-alias resolution. Drum 15→21,
  keys 50→56, all pass; behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread plugins/highway_3d/screen.js Outdated
Follow-up to the _bgPanelKey alias fix: _freeCamFor already treats
panelIndexFor as potentially throwy and catches to keep framing stable, but
_bgPanelKey called it bare. A throwing splitscreen build would take down
background-settings resolution (and the render path) even though the camera
path falls back safely. Wrap the call in try/catch, falling back to 'main'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
@topkoa
topkoa requested a review from Copilot July 9, 2026 20:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
plugins/drum_highway_3d/tests/camera_bridge.test.js (1)

24-57: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: add coverage for ss.panelIndexFor not a function.

Current tests cover missing panel map, missing panel entry, absent globals, and throw-safety, but not the case where ss is truthy while panelIndexFor isn't a function — this should also fall through to the global camera per the typeof ss.panelIndexFor === 'function' guard.

➕ Suggested additional test case
test('_resolveFreeCam: falls back to global when panelIndexFor is missing/not a function', () => {
    const { __test } = load();
    const g = { id: 'global' };
    assert.equal(__test._resolveFreeCam({}, {}, { 0: {} }, g), g);
});

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9ba2dda-3d51-4b29-be37-ed7d9448711b

📥 Commits

Reviewing files that changed from the base of the PR and between ff8a638 and 0d4d822.

📒 Files selected for processing (5)
  • plugins/drum_highway_3d/screen.js
  • plugins/drum_highway_3d/tests/camera_bridge.test.js
  • plugins/highway_3d/screen.js
  • plugins/keys_highway_3d/screen.js
  • plugins/keys_highway_3d/tests/camera_bridge.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/highway_3d/screen.js
  • plugins/drum_highway_3d/screen.js

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread plugins/highway_3d/screen.js Outdated
topkoa and others added 2 commits July 9, 2026 16:27
…ridge fns

- _bgPanelKey() treated any non-null panelIndexFor() return as a valid panel id,
  so a NaN/non-finite index minted a bogus "panelNaN" localStorage key instead
  of falling back to "main". Gate on Number.isInteger(idx) && idx >= 0. (The
  camera path is already NaN-safe — panelsMap[NaN] misses and falls through.)
- Add a NaN/negative-index case to the resolver tests (drum 22, keys 57, pass).
- Convert the camera-bridge helpers' comments to JSDoc (_bgPanelKey, _freeCamFor,
  _resolveFreeCam, _ssApi across the three plugins) to lift docstring coverage on
  the changed surface. Comment/robustness only; no behavior change beyond the
  NaN guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
Finish the docstring pass for the CamDir bridge functions changed in this PR:
convert the two per-panel _freeCamFor delegating wrappers to JSDoc, matching the
pure _resolveFreeCam / _ssApi helpers. Comment-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread plugins/keys_highway_3d/screen.js Outdated
Comment thread plugins/drum_highway_3d/screen.js Outdated
Comment thread plugins/highway_3d/screen.js Outdated
_resolveFreeCam() (keys/drum) and _freeCamFor() (highway_3d) guarded the panel
map lookup with only `i != null`, so a non-integer / negative / string index
from panelIndexFor() could resolve an unintended or inherited property (e.g.
map['toString']) instead of cleanly falling back to the global camera. Gate the
index on `Number.isInteger(i) && i >= 0` before `map[i]`, matching the hardening
already applied in _bgPanelKey(). Extend the resolver tests with float/string
(prototype-key) cases. Behavior change only for malformed indices.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@topkoa
topkoa merged commit aaf593b into main Jul 9, 2026
6 checks passed
@topkoa
topkoa deleted the feat/highway-per-panel-camera branch July 9, 2026 20:38
byrongamatos added a commit that referenced this pull request Jul 10, 2026
Rebased onto main; #823 already shipped 3.31.4 (per-panel camera), so this
Butterchurn buffer-sizing fix advances to 3.31.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
byrongamatos added a commit that referenced this pull request Jul 10, 2026
…ghway (#820)

* fix(highway_3d): size Butterchurn output canvas buffer to fill the highway

The 3D-highway Butterchurn background set only the output canvas CSS size
and called setRendererSize(), but never sized the canvas DRAWING BUFFER
(canvas.width/height). Butterchurn does not size the output canvas itself
(renderToScreen viewports to the reported size into the default
framebuffer), so the buffer stayed at the browser default 300x150 while the
viewport was the full highway. Only the bottom-left ~300x150 of the pattern
was drawn, then CSS-stretched across the whole highway -- zoomed, soft, and
aspect-wrong, worse the larger the panel.

Add _bcApplySize(cssW, cssH): set the drawing buffer to the device-pixel
render size (round(css * min(DPR, 1.5))), confine every layer (canvas,
backdrop, scrim, tint) to the highway rect, and report the same device px
to setRendererSize so buffer == on-screen viewport. Seed the buffer at
create and switch createVisualizer to pixelRatio:1, textureRatio:1 (DPR is
now folded into the reported size, so buffer == viewport == internal
texsize, no double-counting). render() and resize() both route through it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>

* chore(highway_3d): bump to 3.31.5 (3.31.4 taken by #823 on main)

Rebased onto main; #823 already shipped 3.31.4 (per-panel camera), so this
Butterchurn buffer-sizing fix advances to 3.31.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Byron Gamatos <xasiklas@gmail.com>
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.

2 participants