Repository navigation
fix(rendering): let colors go brighter than white - #710
Merged
Merged
Conversation
Color no longer clamps red, green and blue to 1, so a tint can brighten a sprite past its texture and, on an hdr camera, bloom more than a white-tinted one. Negative channels are still clamped to 0 and alpha to [0, 1]; toRGBAString writes channels above 1 as 255, and fromHSLA throws for a saturation or lightness outside 0-100. Adds e2e coverage for both cases (8-bit canvas and hdr + bloom) and updates the bloom, HDR and UI button guides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBx4SStcu1rxjYyqgkEC1n
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBx4SStcu1rxjYyqgkEC1n
stormmuller
enabled auto-merge (squash)
October 6, 2026 17:59
stormmuller
pushed a commit
that referenced
this pull request
Oct 6, 2026
…j6xxo Brings in explicit font atlas URLs (#713) and HDR colors (#710). The game states demo loads the default font atlas from the package, as the other demos now do, since static/fonts is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE
stormmuller
added a commit
that referenced
this pull request
Oct 6, 2026
…es (#711) * feat(states): add game states, run conditions and state-scoped entities Implements design/game-states.md. - ecs: `runIf` on `addSystem` and `addSystemGroup`, checked just before the system or group runs; a gated system isn't queried. - ecs: a built-in `firstSystemGroup` that runs before every other group. Groups ordered after it run at the start of the tick, before every other group, so a state's exit and enter groups run before input and gameplay. - states: new module with `createGameState`, `inState`/`onEnter`/`onExit` and `addStateScopedComponent`. - Docs: run conditions and the first group in the ECS guides, a new Game States guide and a game states demo. - design/game-states.md: records the start-of-tick placement the exit and enter groups need, and the scoped-removal group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * Merge remote-tracking branch 'origin/dev' into claude/serene-mendel-1j6xxo Brings in explicit font atlas URLs (#713) and HDR colors (#710). The game states demo loads the default font atlas from the package, as the other demos now do, since static/fonts is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): link the demo where the guide uses it, not in the intro Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): name the example state type GameStateName, not Screen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): say what happens without runIf, drop the dependency-injection aside Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): describe run conditions plainly instead of as gates Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): describe paused systems without implying they hold state, drop round-specific wording Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): drop the Time aside from the run conditions section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): drop the input section from the game states guide Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): describe enter and exit systems as reacting to transitions, not setting up a state Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): drop the several-worlds section from the game states guide Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(skills): make document-feature produce isolated, literal technical documentation Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): rewrite the game states docs as isolated technical reference Applies the document-feature skill to the game states guide, the state-scoped entities guide, the run conditions section of the System guide, the first group section of the World guide, and the GameState and firstSystemGroup JSDoc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(skills): document-feature writes feature guides, leaving per-member detail to the API reference The skill now separates guides (the user manual) from the generated API reference, requires an outline of headings named after the feature's concepts and tasks, and bans generic headings such as "Worked example", "Gotchas" and "Common mistakes". The game states guide is rewritten to it as a single page; the separate state-scoped entities page is folded in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE * docs(states): leave entity removal behavior to the World guide The game states guide no longer describes what happens to a removed entity's children; that's removal and parenting behavior, documented where those features are. The document-feature skill gains the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
design/hdr-colors.md.Color's constructor clamped every channel to[0, 1]. Nothing after it in the pipeline does: the sprite shader multiplies the texture by the tint, instance tints are uploaded as floats, andhdrrender targets keep values above1for bloom and tone mapping. So the clamp inColorwas the only thing stopping a tint from brightening a sprite. Unity, Godot and Bevy all leave their color types unclamped for this.Changes
Color0at the bottom, because negative light has no meaning.[0, 1], because premultiplied blend factors would go negative on a float render target.toRGBAStringwrites channels above1as255, since a CSS color can't be brighter than white.fromHSLAnow throws for a saturation or lightness outside0–100. Before, out-of-range values were hidden by the output clamp.easingthat overshoots (easeInOutBack,easeInOutElastic) now briefly passes a target color above1instead of being cut off at1. Unity and Godot tweens behave the same way. A unit test now covers this.Colorunit tests and aui-transition-systemovershoot test.tint-brighter-than-texture: on the 8-bit canvas, a gray sprite tinted1.5draws 1.5× brighter than one tinted white.hdr-tint-bloom: on anhdrcamera with bloom and tone mapping, a sprite tinted3blooms more than one tinted white (halo 147 vs 59).Color.createWhiteSquareImageis nowcreateSquareImage(fillStyle), so a scene can generate a gray texture.bloom.mdtip and emissive section, the opening ofhdr-rendering.md, and a new "Hover and press colors" section inui/buttons-and-interaction.md.The
solution-revieweragent returned REVISE on the first plan. Everything it flagged is addressed above: the easing-overshoot reasoning and test, the 8-bit e2e check, a new HDR scene instead of changingbloom-over-background, and thefromHSLAdecision.Follow-up outside this repo: the Galactic Journey demo can rest its buttons at
Color.whiteand drop the "Dimmed at rest" workaround once it depends on a Forge release that includes this change.Related issue(s)
design/hdr-colors.mdVerification checklist
npm run check-typespasses with 0 errorsnpm testpasses (1796 tests)npm run lintpasses with 0 errorsnpm run cspellpasses with 0 errorsnpm run check-exportspassesindex.ts(no new exports)/documentation-site/docs/docsis updated/dist; ran the docs site'stypecheckandbuild; loaded easing-functions, ui-button, space-shooter and ui-main-menu in Chromium, and they render with no engine errors. No demo code changes were needed.check-types:e2egives the same two errors intext-effects-overlap.tsthat it gives ondev.Changelog
## [Unreleased]inCHANGELOG.md(#### Changed)🤖 Generated with Claude Code
https://claude.ai/code/session_01EBx4SStcu1rxjYyqgkEC1n
Generated by Claude Code