Skip to content

feat(highway_3d): fret wires flash on a confirmed hit - #969

Merged
topkoa merged 12 commits into
mainfrom
feat/highway-3d-fret-wire-hit-flash
Jul 19, 2026
Merged

feat(highway_3d): fret wires flash on a confirmed hit#969
topkoa merged 12 commits into
mainfrom
feat/highway-3d-fret-wire-hit-flash

Conversation

@topkoa

@topkoa topkoa commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

The 3D highway's fret wires were static scenery — gold inside the anchor lane, grey outside, and nothing tied them to what the player was doing. This gives them a job:

  1. Widened the lane/neck contrast, so the wires around the active lane read as a focus cue and the rest of the neck recedes.
  2. Made the wires slightly thicker (and bumped the tube's radial segments, since a fatter tube shows its hexagonal facets).
  3. Flash the wires bracketing a note when a scorer confirms a hit, so a hit registers on the board itself. Here is a good example of it in action for reference: https://www.youtube.com/watch?v=gIpDvz4PNaQ

Which wires flash depends on the note:

Wires lit
Fretted single note on fret f f-1 and f — the wire behind it, and the wire it's pressed against
Chord Only the outermost wires of the shape (behind the lowest fret, at the highest). Frets 3/5/5 → wires 2 and 5, not 2‑3‑4‑5, so it reads as one bracketed block instead of a picket fence
Open string The anchor lane's edge wires. An open string has no fret of its own, and its gem is drawn as a wide slab spanning the lane, so those are the wires it actually sits between

Verified on screen

Verified by the maintainer against a scripted note-state provider (a console-driven "perfect player" that confirms every note as it crosses the hit zone — the dev machine has no guitar attached). The additions below came out of that on-screen iteration.

Additions since the first draft

  • At most two wires are ever lit — overlapping decay tails (fast passages) collapse to the outermost pair of the lit span: one bracket, never a picket fence.
  • Chord flashes frame the lane, not the shape — the lit lane strip can run a fret past the chord's outermost fret, and a bracket one wire inside the lit lane read as misaligned. Chords now light the anchor lane's edge wires (the same pair open strings use); the shape's own outer pair survives as the fallback on anchor-less charts.
  • Gem rims flash too — on a confirmed hit the gem's outline lights in the string's own colour with the same intensity treatment as the wires, fading with the scorer's alpha. Just the rims: face fill and sustain trails unchanged.
  • A pulse-envelope ("lightning strike") variant of the wire flash was tried and reverted in-branch; the shipped behaviour is the decaying glow described above.

The levers — all named constants, all in one block

All in plugins/highway_3d/screen.js, in the fret-wire constants block next to FRET_METALNESS / FRET_BOW_DZ. Nothing here is derived from anything else, so each can be moved independently.

Hit flash

Constant Now What it does
FRET_WIRE_HIT_INTENSITY 4.2 The main brightness knob. Multiplies emissiveIntensity at full flash (baseline 1). This is what makes a wire read as a light source rather than a brightly-lit object. Went 3.0 → 6.0 → 4.2; last feedback on 6.0 was "slightly too bright", so 4.2 is an untested guess between.
FRET_WIRE_HIT_DECAY 0.32 The linger knob. Seconds for a flash to fall to ~1/e once the provider stops reporting. Deliberately independent of brightness — a long tail can read as "too bright" even when the peak is right, so if it feels hot, try trimming this to ~0.22 before dropping the intensity.
FRET_WIRE_HIT_EMISSIVE 0xFFE9B0 Glow colour at full flash (hot warm-white).
FRET_WIRE_HIT_HEX 0xFFFFFF Albedo at full flash. Has less effect than you'd expect — see the note below.
FRET_WIRE_HIT_OP 1.0 Opacity at full flash.

Why emissive and not colour: these are MeshStandardMaterial in a scene with no envMap, so raising albedo alone barely brightens them — it saturates toward white and stops. emissiveIntensity is the lever with real range. Corollary: there's no bloom/post-processing pass in this scene, so past ~8 you're just painting white pixels for longer and can't get glow bleed into the surrounding area. If it needs to be brighter than the material can go, the next step is an additive halo mesh around the flashing wire — a bigger change, not a constant.

Base look (the two idle tiers)

Constant Now Was
FRET_WIRE_ACTIVE_HEX / _OP 0xD8A636 / 0.9 0xD8A636 / 0.8
FRET_WIRE_IDLE_HEX / _OP 0x4A4A60 / 0.28 0x666688 / 0.4
FRET_TUBE_RADIUS STR_THICK * 0.75 * 0.55
FRET_TUBE_RADIAL 8 6

Things to actually look at when picking this up

  • Fast passages. With a 0.32 s tail, consecutive notes on nearby frets overlap their flashes, so neighbouring wires may stay lit more or less continuously. That could look great (a glowing band tracking your hand) or muddy (nothing ever goes dark). This is the single most likely thing to need tuning.
  • Held sustains. The provider returns alpha tracking live input level for a held note, which jitters frame to frame. The decay tail is what smooths it — check it's actually smooth and not pulsing.
  • High frets under fog. The idle tier is dimmer now (0.28 vs 0.4); confirm distant wires haven't been swallowed entirely.
  • Charts with no anchors. anchorLaneBoundsAt returns null, so open-string flashes silently do nothing (fretted notes are unaffected). Rare — GP imports synthesise anchors — but it's the one case that fails quietly rather than visibly.

Implementation notes for a reviewer

  • Gated on the provider verdict (_ndGood), never the proximity hit heuristic. The latter only means "near the strike line" — using it would flash on every passing note whether or not it was played. So with no scorer attached, the neck behaves exactly as it does today.
  • Two passes, deliberately. The base tier loop (gold/grey) runs near the top of update(), long before any note is drawn. The flash is applied in a second pass after the note and chord draw loops, so it sees this frame's verdicts rather than the previous frame's. emissive / emissiveIntensity are re-seeded to baseline in the tier loop each frame — without that, a flash would never fade back out.
  • Chords accumulate. drawNote() is the chord loop's per-note call and can't see the chord's span, so chord hits collect into a small Map (keyed by chord id, falling back to chord time — ch.id can be absent, a documented pitfall) and the flash pass resolves min/max fret into the outer wire pair. This avoided restructuring the chord loop with begin/end hooks.
  • The flash decays in chart time, so it respects playback speed, and a backward seek clears it rather than leaving a stale flash on a wire you jumped away from.
  • drawNote() is a sibling of update(), not nested in it, so the anchors are snapshotted into a closure var (_drawAnchors) — following the pattern already established there for _drawChordTemplates.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Expanded the Unreleased changelog with updates covering career and passport gameplay, gigs, Gold tier progression, stems support, genre enrichment, and 3D highway improvements.
    • Added notes on navigation, song-grid performance, library filtering, playlists, resume and exit flows, settings, themes, and working tuning.
    • Documented plugin-system enhancements, architectural improvements, performance updates, and security fixes.
  • Chores

    • Updated the 3D highway plugin version to 3.32.0.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@topkoa, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a883a957-ca4e-45c5-bae2-f00550816129

📥 Commits

Reviewing files that changed from the base of the PR and between 2b28cd0 and 8ea4217.

📒 Files selected for processing (2)
  • plugins/highway_3d/screen.js
  • tests/js/highway_3d_render_order.test.js
📝 Walkthrough

Walkthrough

The changelog’s unreleased section is substantially expanded with feature, refactor, UI, fix, and security notes. The classic v2 shell removal is documented, historical notes remain, and the 3D highway plugin version is updated to 3.32.0.

Changes

Release documentation and plugin metadata

Layer / File(s) Summary
Unreleased feature and architecture notes
CHANGELOG.md
Documents career passport features, stems behavior, enrichment fallbacks, CI conformance, backend refactors, plugin-system changes, performance work, and gameplay additions.
UI, runtime, and security release notes
CHANGELOG.md
Records v3 UI changes, rendering and startup fixes, path-traversal hardening, and retained historical release notes.
3D highway plugin version metadata
plugins/highway_3d/plugin.json
Updates the plugin manifest version from 3.31.5 to 3.32.0.

Estimated code review effort: 1 (Trivial) | ~4 minutes

Possibly related PRs

Suggested reviewers: byrongamatos, chrisbewithyou

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly matches the main change: fret wires now flash on confirmed hits in highway_3d.
Description check ✅ Passed The What section is detailed and on-topic, but the feedpak and checklist sections from the template are missing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/highway-3d-fret-wire-hit-flash

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

The fret wires were static scenery: gold inside the anchor lane, grey
outside, and nothing tied them to what the player was actually doing.

Give them a job. Widen the lane/neck contrast so the wires around the
active lane read as a focus cue, and flash the wires bracketing a note
when a scorer confirms it. A fretted note lights the wire behind it and
the wire it is pressed against; a chord lights only the outermost wires
of its shape, so it reads as one bracketed block rather than a picket
fence; an open string has no fret of its own and its gem is drawn as a
slab spanning the lane, so it lights the lane's edge wires instead.

Gated on the provider verdict, never the proximity heuristic -- the
latter only means "near the strike line", so it would flash on every
passing note whether or not it was played. With no scorer attached the
neck behaves exactly as before.

Emissive (and emissiveIntensity) carry the flash, not albedo: these are
MeshStandard materials in a scene with no envMap, so raising albedo
alone barely brightens them.

Every value is a named constant -- see FRET_WIRE_* -- because the look
is a taste call that wants tuning by eye, not a derivation.

Signed-off-by: Kris Anderson <topkoa@gmail.com>
@topkoa
topkoa force-pushed the feat/highway-3d-fret-wire-hit-flash branch from f55c021 to 981b779 Compare July 14, 2026 21:50
topkoa added 7 commits July 16, 2026 19:40
Bring the branch up to date with main (includes #994, which also touched
highway_3d/screen.js — the lane hit-line fix — in a different region).
Fast passages overlap their decay tails: consecutive notes on nearby
frets left three, four, five wires glowing at once — the picket fence
the chord rule was written to avoid, arriving through time instead of
through a shape.

The apply pass now decays every wire's glow state as before, but flashes
only the outermost pair of the lit span (or the single wire when only
one is above threshold). Interior wires keep decaying invisibly — the
base tier loop re-seeds their materials each frame — so the bracket
tightens naturally as the outer tails expire, and a hit inside the
current span widens nothing.

Net effect: at most two wires are ever lit, and everything currently
glowing reads as one bracket, exactly like a chord.

Signed-off-by: topkoa <topkoa@gmail.com>
The lit lane strip spans the anchor's width (minimum ~4 frets), which
can run a fret past the chord's outermost fret. The chord flash
bracketed the shape (wire behind its lowest fret, wire at its highest),
so on those anchors the bracket sat one wire INSIDE the lit lane —
reading as misaligned rather than as a frame around what's lit.

Chord hits now light the anchor lane's edge wires: the exact wires the
lane strip itself spans, and the same pair open strings already use, so
every hit shape inside a lane produces the same bracket. The shape's
own outer pair survives only as the fallback for charts with no
anchors. Fretted and open intensities merge into one entry (they light
the same two wires now), and an all-open chord on an anchor-less chart
still degrades to no flash rather than a bad index.

Signed-off-by: topkoa <topkoa@gmail.com>
On a confirmed hit the gem's outline now flashes in the STRING'S OWN
colour with the same intensity treatment as the fret wires — the
FRET_WIRE_HIT_INTENSITY emissive ramp, faded by the provider's alpha —
instead of the fixed spring-green mHitBright rim. Just the rims: the
lateral face fill keeps its existing green, and the sustain trail is
untouched.

Mechanics mirror the wires' pattern. mRimFlash[s] is one material per
string (created with the other per-string materials, palette-retint
aware, fog-exempt, disposed in teardown); drawNote() assigns it as the
outline on a good verdict and records the verdict alpha into a
per-frame per-string max (_rimFlashIn); the flash pass applies the
intensity ramp once per string. Shared-per-string is the same
compromise mGlow already makes — two same-string gems flashing in
different phases share the brighter alpha.

No decay tail of our own, deliberately: the material is only assigned
while the provider confirms the note, and the provider's alpha already
fades. When it goes silent the outline reverts, so idle intensity never
shows.

Signed-off-by: topkoa <topkoa@gmail.com>
The flash was instant-on with a 0.32 s exponential tail, and a held
sustain kept re-feeding it — wires stayed lit for the whole note. The
requested feel is a shock: light hits the frets, they jolt, it's over.

The flash is now a one-shot pulse triggered on the input's rising edge:
a near-instant crack up (RISE 25 ms), a fast fall (FALL 160 ms) shaped
(1-u)^2 so it drops hard then eases out, with a 26 Hz flicker biting
into the fall (the electric shudder — the crack itself stays clean),
then hard zero. A held 'active' verdict keeps the input high
continuously, which by construction triggers nothing new: one strike
per hit, and the wires go dark while the note rings on. A re-strike
after the provider goes silent re-triggers cleanly.

Seeking backward or a long stall clears all pulse state, and a pulse
whose strike time lands ahead of the playhead after a seek is
discarded. The outer-pair bracket rule is unchanged — it now selects
across pulses instead of decay tails.

Knobs: FRET_WIRE_HIT_RISE / _FALL / _FLICKER_HZ / _FLICKER_DEPTH
(replacing FRET_WIRE_HIT_DECAY).

Signed-off-by: topkoa <topkoa@gmail.com>
The strike trigger was a rising edge on each WIRE's input, which merged
distinct hits: two consecutive correct notes on the same fret kept that
wire's input continuously high, so the second note produced no strike at
all. The wires must respond to what the player did — one strike per
judged hit-zone event.

The trigger is now per event identity, using the same seen-map pattern
as _sparkSeen: the first frame a note gets a good verdict its key
(string|fret|time — or the chord key for a strum, which strikes once as
a unit) lands in _fwStruck and requests a strike on its wires; the
event never fires again however long its verdict stays live. Because
every producer is gated, any nonzero input in the apply pass IS a fresh
strike, so it restarts a pulse already in flight — a rapid re-hit on
the same wire re-cracks instead of being swallowed.

Seeks clear the map (replayed notes strike again); it is size-bounded
like _sparkSeen. Envelope, flicker, and the outer-pair rule unchanged.

Signed-off-by: topkoa <topkoa@gmail.com>
Reverts d003532 and e05d90e. The wire flash returns to its original behaviour: instant-on at the provider's alpha with a smooth exponential fade (FRET_WIRE_HIT_DECAY 0.32 s), held sustains keep their wires lit while the note rings, and no flicker. The outer-pair bracket, lane-framed chords, and string-coloured gem rims are untouched.

Signed-off-by: topkoa <topkoa@gmail.com>
@topkoa
topkoa marked this pull request as ready for review July 19, 2026 00:40
@topkoa
topkoa requested a review from Copilot July 19, 2026 00:46

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

Updates the bundled highway_3d visualization to make fret wires visually respond to confirmed note hits, improving gameplay feedback and readability on the 3D highway.

Changes:

  • Add a hit-confirmed “flash” effect to the fret wires bracketing the played note (and appropriate behavior for chords/open strings).
  • Adjust base fret-wire visuals (lane/neck contrast and wire thickness/geometry) to better emphasize the active anchor lane.
  • Bump the highway_3d plugin version to 3.32.0.

Reviewed changes

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

File Description
plugins/highway_3d/plugin.json Bumps plugin version to reflect the new rendering/feedback behavior.
plugins/highway_3d/screen.js Implements fret-wire hit flash + related visual tuning and gem rim flash integration.

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

Comment thread plugins/highway_3d/screen.js

@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 (3)
CHANGELOG.md (1)

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

Consolidate repeated changelog headings.

These repeated Added, Changed, Fixed, and Removed headings trigger the supplied MD024 warnings. Merge each category into one section under [Unreleased], or explicitly configure the linter if segmented sections are intentional.

Also applies to: 260-260, 295-295, 319-319, 340-340, 344-344, 352-352, 355-355, 365-365, 418-418, 446-446, 454-454, 457-457, 503-503, 513-513

🤖 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 `@CHANGELOG.md` at line 117, Consolidate the repeated Added, Changed, Fixed,
and Removed headings in the [Unreleased] section of CHANGELOG.md into one
heading per category, preserving all existing entries and their order; only
configure the linter instead if the segmented headings are intentionally
required.

Source: Linters/SAST tools

plugins/highway_3d/screen.js (2)

12734-12746: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Fret-wire and gem-rim hit-flash intensity ignores the glowMul slider.

_m.emissiveIntensity = 1 + (FRET_WIRE_HIT_INTENSITY - 1) * _g; (wires) and the matching gem-rim loop just below both apply FRET_WIRE_HIT_INTENSITY unscaled. Every other emissive material in this file (mHitBright, mStrHitOutline, mAccentOutline, mTapChevron, mBarre, mSusOutline, …) is driven through _applyGlow() or multiplied inline by glowMul so the "Glow" setting controls overall brightness. This new flash is the one place that stays full-intensity regardless of the user's glow preference.

💡 Proposed fix
-                    _m.emissiveIntensity = 1 + (FRET_WIRE_HIT_INTENSITY - 1) * _g;
+                    _m.emissiveIntensity = 1 + (FRET_WIRE_HIT_INTENSITY - 1) * _g * glowMul;
...
-                    if (_m) _m.emissiveIntensity = 1 + (FRET_WIRE_HIT_INTENSITY - 1) * _rimFlashIn[_s];
+                    if (_m) _m.emissiveIntensity = 1 + (FRET_WIRE_HIT_INTENSITY - 1) * _rimFlashIn[_s] * glowMul;
🤖 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 `@plugins/highway_3d/screen.js` around lines 12734 - 12746, Update the
fret-wire hit-flash calculation and the gem-rim loop using _m.emissiveIntensity
so FRET_WIRE_HIT_INTENSITY is scaled by the existing glowMul setting, consistent
with the other emissive materials and preserving the current intensity ramp
behavior.

13970-13985: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

_fwChordAcc key collision on repeated same-template chords within one frame.

The accumulator is keyed by chordId (falling back to `t${n.t}` only when nullish). ch.id is the chord-template id and is reused across every occurrence of the same shape (repeats/gallops, which this file explicitly handles elsewhere, e.g. the Frantic gallop comments). If two occurrences of the same chord are both confirmed-hit in the same frame, they'd collide into one entry, and _fwE.t (fixed at first-touch, never updated) could resolve the wrong anchor-lane wires for the later occurrence.

🔧 Proposed fix
-                    const _fwK = (chordId !== undefined && chordId !== null) ? chordId : `t${n.t}`;
+                    const _fwK = (chordId !== undefined && chordId !== null) ? `${chordId}|${n.t}` : `t${n.t}`;
🤖 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 `@plugins/highway_3d/screen.js` around lines 13970 - 13985, Make the
_fwChordAcc key uniquely identify each chord occurrence within the frame, not
just the reusable chord-template chordId. Incorporate the occurrence’s
timing/anchor identity into the key while preserving the existing fallback for
missing chord IDs, and ensure _fwE.t remains associated with that specific
occurrence so repeated same-template chords cannot share accumulator entries.
🤖 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.

Nitpick comments:
In `@CHANGELOG.md`:
- Line 117: Consolidate the repeated Added, Changed, Fixed, and Removed headings
in the [Unreleased] section of CHANGELOG.md into one heading per category,
preserving all existing entries and their order; only configure the linter
instead if the segmented headings are intentionally required.

In `@plugins/highway_3d/screen.js`:
- Around line 12734-12746: Update the fret-wire hit-flash calculation and the
gem-rim loop using _m.emissiveIntensity so FRET_WIRE_HIT_INTENSITY is scaled by
the existing glowMul setting, consistent with the other emissive materials and
preserving the current intensity ramp behavior.
- Around line 13970-13985: Make the _fwChordAcc key uniquely identify each chord
occurrence within the frame, not just the reusable chord-template chordId.
Incorporate the occurrence’s timing/anchor identity into the key while
preserving the existing fallback for missing chord IDs, and ensure _fwE.t
remains associated with that specific occurrence so repeated same-template
chords cannot share accumulator entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c1d49ac1-5125-4563-bd8d-78a747edf7e8

📥 Commits

Reviewing files that changed from the base of the PR and between 1c077c9 and 2b28cd0.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • plugins/highway_3d/plugin.json
  • plugins/highway_3d/screen.js

topkoa added 2 commits July 18, 2026 20:52
The two render-order tests pinned the old literal hexes (idle 0x666688). The tiers moved to named constants with a retuned idle (FRET_WIRE_IDLE_HEX 0x4A4A60); the tests now assert the code uses the constants AND pin the constants' values, so a future retune is a deliberate two-line change here rather than a silent one.

Signed-off-by: topkoa <topkoa@gmail.com>
The wire-flash path clamps the note-state provider's alpha to 0..1; the rim-flash accumulation used it raw, so a provider returning >1 would over-drive emissiveIntensity. Clamped to match.

Signed-off-by: topkoa <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 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread tests/js/highway_3d_render_order.test.js
…view)

Signed-off-by: topkoa <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 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread tests/js/highway_3d_render_order.test.js Outdated
Comment thread tests/js/highway_3d_render_order.test.js Outdated
Two review findings on the render-order test file, both correct:

The header claimed ALL 3D-highway materials use depthTest:false, making
renderOrder "the only" draw-order control — but the accent halo
materials set depthTest:true. Now says "nearly all", names the
exception, and calls renderOrder the primary control. A header someone
trusts mid-debug must not overclaim.

The fret-wire depthTest/depthWrite assertions matched anywhere in
screen.js, which is full of other depthTest:false materials — the test
would keep passing if the wire material dropped the flags. Both are now
anchored to the wire material literal via FRET_WIRE_IDLE_HEX (unique to
it), as two separate anchored matches so property order inside the
literal still isn't pinned.

Signed-off-by: topkoa <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 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

tests/js/highway_3d_render_order.test.js:216

  • The depth-flag assertions for the fret-wire material are still vulnerable to false positives because they aren’t anchored to the new T.MeshStandardMaterial({ ... }) literal itself, and they also implicitly require color: to appear before the depth* properties. You can make this both order-independent and scoped to the fret-wire material by matching the MeshStandardMaterial literal and using lookaheads within a single {...} block.
    // Both depth flags anchored to the fret-wire material literal (via its
    // FRET_WIRE_IDLE_HEX color, unique to it) — an unscoped match would pass
    // off any other depthTest:false material in the file. Asserted as two
    // separate anchored matches so property order inside the literal still
    // isn't pinned.
    assert.match(
        s,
        /color\s*:\s*FRET_WIRE_IDLE_HEX[\s\S]{0,400}?depthTest\s*:\s*false/,
        'the fret wire material itself must set depthTest: false',
    );
    assert.match(
        s,
        /color\s*:\s*FRET_WIRE_IDLE_HEX[\s\S]{0,400}?depthWrite\s*:\s*false/,
        'the fret wire material itself must set depthWrite: false (no z-buffer pollution)',

@topkoa
topkoa merged commit 2413991 into main Jul 19, 2026
7 checks passed
@topkoa
topkoa deleted the feat/highway-3d-fret-wire-hit-flash branch July 19, 2026 01:11
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