Add per-key wear and highlight variation to the keyboard stage - #1243
Conversation
|
Reviewed the diff. Implementation looks solid overall — the keyCode → wear-tier mapping in One actionable gap:
Minor/non-blocking:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9114083068
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Found one actionable correctness issue in the shader tuning:
In
Minor: |
|
Correctness:
float wear = isKey
? saturate(input.material.x) * saturate(uniforms.tuningE.z)
: 0.0;
Suggest either dropping the No other correctness issues found; the struct layout changes (new |
The generated reference reads as photographed hardware partly because no two keycaps respond to light identically. The renderer's caps were perfectly instanced: identical gloss, identical grain, highlights landing at exactly the same spot on every cap. - Per-key wear, seeded by key code: fingers polish high-traffic caps, so the space bar, home row, and modifier caps get glossier crowns, calmer grain, and stronger highlight response, while the number row keeps its factory texture. - Per-key highlight jitter: a stable per-key seed tilts each crown fractionally, so highlights land slightly apart cap to cap. - A dim cool fill from the lower left mirrors a studio rig's second source, lifting shadow sides with a two-temperature specular response; it stays a whisper in the dark hold so the room stays black. All four behaviors ride the KeyboardStageTuning uniform block (fillLightStrength, fillSpecularStrength, wearStrength, highlightJitter) with live-tunable defaults; wearStrength/highlightJitter at 0 restore the prior uniform look. Metal goldens re-recorded and compared clean; keyboard-stage, tuning, and snapshot suites pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Results of a live tuning session against the reference image, run through the new offscreen preview loop rather than screen automation: - Add a window-space corner vignette to the composite pass (strength 0.58, inner radius 0.33): the dark room now sinks toward the frame edges like the reference photograph, and the falloff relaxes to 30% at the settled light so the endpoint stays clean. - Raise wear (1.5), highlight jitter (2.2), and the cool fill (0.95/0.15) from their conservative first-pass defaults; each cap now catches the light visibly apart from its neighbors. - Add KeyboardStageTuningPreviewTests, an env-gated utility that renders dark/mid/settled frames through the exact Metal pipeline into a scratch directory honoring KEYPATH_STAGE_TUNING_FILE — deterministic tuning iterations with no installed app or window automation. Metal goldens re-recorded at the new defaults and re-compared clean; tuning, entrance, and both snapshot suites pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 1.5-second hold read as slightly rushed in installed review: the light began rising before the dark room had fully registered. Hold the still frame for 2.0 seconds after the first presented keyboard frame; the reveal itself is unchanged. Contract tests and the plan document follow the new boundary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c1aa3f0 to
3653591
Compare
|
Review findings
No other correctness/reliability issues found; the GPU struct layout changes (added |
The 250ms bound measured real elapsed time and failed on a loaded CI runner (268ms in PR #1243's build-and-test job) despite being unrelated to the change under test. boundedCapture has no injectable clock, so keep the real-clock measurement but widen the bound to a generous 5-second hang guard: it still fails if the timeout path hangs instead of returning first-class timed-out evidence, without depending on scheduler latency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 250ms bound measured real elapsed time and failed on a loaded CI runner (268ms in PR #1243's build-and-test job) despite being unrelated to the change under test. boundedCapture has no injectable clock, so keep the real-clock measurement but widen the bound to a generous 5-second hang guard: it still fails if the timeout path hangs instead of returning first-class timed-out evidence, without depending on scheduler latency. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
Review gate
remote review gate selected— GitHubclaude-reviewis the enforced reviewer.Testing
🤖 Generated with Claude Code