Skip to content

Front-end visual revamp: inked gel on a membrane in the living world - #245

Draft
genixpro wants to merge 14 commits into
Globulation2:masterfrom
genixpro:claude/glob2-ui-visual-design-h2axqo
Draft

Front-end visual revamp: inked gel on a membrane in the living world#245
genixpro wants to merge 14 commits into
Globulation2:masterfrom
genixpro:claude/glob2-ui-visual-design-h2axqo

Conversation

@genixpro

Copy link
Copy Markdown
Contributor

Draft — this is a stylistic direction, and I want feedback on the direction before polishing it. Please look at it in motion, not only in the captures: the fade, the hover swell, the cloud shadows, the camera drift and the glob are all things a screenshot can't show.

The problem

The recent front-end work (#202, #236, #237) is a real UX improvement, and people are right that it feels sterile. The review in docs/ui-visual-review/README.md measured why: the chrome ran 11–38% saturation over a world at 56–75%, in a gray-cream nothing in the game uses; it separated surfaces with 4%-lightness steps and hairlines where every Glob sprite is a heavy ink contour; and the "live colony" behind it had a fixed camera, cloud shadows that never reached the panel, and a 97%-opaque sheet plus a cream veil on top — functionally a wallpaper.

Before (shipped) After (this branch, OpenGL)
before after

Why not just bring back the grass?

We looked at it. The old front end — grass texture tiled across the window, gold bevelled buttons — is what people say they miss:

old style

I think what they actually miss are three properties it had, not the execution: the chrome used the game's colours, the controls read as physical objects, and the menu felt like it was standing in the world. The execution — a seamless tile repeating at 1:1, bevel-embossed gradients — is what dates it, and going back would also throw away the hierarchy and layout work the refresh got right. So this branch tries to restore those three properties without the texture.

The style we're going for

A membrane over a living colony, drawn by the same hand as the sprites.

  • One palette from the game's own art. Ink from the grass, gold, the water's violet for keyboard focus, a warm sand membrane. Nine near-cream fills across five drifted copies collapse to FrontendPalette.
  • Ink line. The concept drawings in datasrc/gfx/concept-art/ and every sprite are wobbly hand-inked contours around soft forms. Controls are now blob()s: per-corner radii, a gentle rect-seeded wobble, a 2 px ink ring, a gel highlight. That's the wordmark's shape language extended to the chrome.
  • The world reads through. Panels are a translucent membrane the colony tints; the front page especially. Cloud shadows cross the panel and its controls (Style::afterPaint), and the camera drifts slowly. The interface is a thing in the world, not a window over a screenshot.
  • Jelly. Buttons swell under the cursor and squash when pressed; the front page fades in. Globs are jelly.
  • A glob lives on the front page. The colony's own worker, in its team colour, walking the band that used to be empty.

Settings, with the same treatment:

settings

More captures (lobby, load, campaign, results, credits, software-backend variants) are in docs/ui-visual-review/.

What I'd like feedback on

  1. Does this feel like Glob to you, or like a different generic?
  2. Front-page membrane opacity (frontPageAlpha, currently 140): too open, or not open enough?
  3. Cloud-shadow strength over the large Settings sheet — it follows the world's cloudMaxAlpha; easy to scale down if it reads as mottling.
  4. Wobble amplitude (1 px on controls, 2 px on panels) — deliberately restrained; should it be more overtly hand-drawn?
  5. The glob: charming or gimmick?

Scope and constraints

Draw-only: no hit rects or widget geometry change; no new assets, no new widget framework; Glob2Style and the in-game HUD are untouched. Engine additions are a Style::afterPaint hook (3 lines, same optional-hook pattern as onFrame) and a DRAW_NO_CLOUDS option nothing else passes. Cloud shadows over the UI and the camera drift are OpenGL-only by the engine's existing guards; the software backend gets everything else.

Feel note per AGENTS.md: this changes how the front end feels, on purpose. Nothing here touches gameplay, simulation, saves, replays or the network.

Verification

All run on this branch, SDL_VIDEODRIVER=dummy unless noted:

  • MenuColonyHarness check — presentation counts, text bounds, colony checksums, main-menu routes
  • MenuColonyHarness navigation — enter/cancel 14 real screen loops
  • MenuColonyHarness sessions — play/quit, replay, results, editor round trip
  • CustomGameSetupHarness model + visual (Xvfb)
  • test/run-game-speed-tests.py (Xvfb) — simulation checksums identical (8b9c6da6), confirming drift and clouds are presentation-only
  • All 22 harness screens captured and reviewed for the double-sheet / private-scrim mistake settings had; none remain

Measured on a 120-frame OpenGL recording: world shifts (−9, −4) px over 80 frames; panel-band luminance moves 198→204 as a shadow crosses. The harness's Preview::render now mirrors dispatchPaint, so captures show what the game shows.


Copied from genixpro#1.

🤖 Generated with Claude Code

https://claude.ai/code/session_012MQFLk4h8B69C9PvvHfGE5

claude and others added 12 commits September 10, 2026 19:57
Diagnosis of why the refreshed menus read as sterile, with the measurements
behind it and one demonstration spike. Nothing here is proposed for merge.

The review is in docs/ui-visual-review/README.md. In short: the refresh
removed the old style's bad execution (tiled grass, bevelled gold) along with
three properties worth keeping — the chrome used the game's colours, controls
read as objects, and menus matched the match. Flat design puts all the
personality on palette, shape and type, and the current front end has a
palette derived from nothing in the game, no outlines, the stock system font,
and none of the game's art.

Measured: the world runs 56-75% saturation, the chrome 11-38%; the greens are
not even the same hue family; the theme draws a cream veil over the colony to
make the pale chrome legible. Nine near-identical panel fills are spread over
five drifted palette definitions. Settings and the lobby each re-implemented
their own drawing layer, so a full rewrite of FrontendTheme leaves the
Settings panel interior pixel-identical.

The spike changes surface treatment only, in about 40 lines: chrome recoloured
to the game's own hues, a drawn ink contour on panels and controls, and a dark
scrim instead of the cream veil. Layout, fonts and assets are untouched. It
shows the reading is right where the theme owns drawing, that palette alone
does not fix the bounding-box composition, and that Settings is unreachable
from the theme — which is the argument for landing the token unification
first.

Captures are from the real client renderer via MenuColonyHarness at 1152x720
on the software backend; before/after for four screens are alongside the
document.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Five copies of the menu palette had drifted apart (theme, main menu, lobby
controls, lobby screen, settings layout). Introduce a single set of named
tokens in FrontendTheme.h and route every front-end colour literal through
it. Values are the ones the review settled on; Settings, which carried a
third unrelated palette, shifts to match the others. No drawing logic
changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Add FrontendTheme::blob() and ring(): a rounded contour with per-corner
radii and a gentle, rect-seeded edge wobble, drawn as a two-pixel ink ring
around a fill with a one-pixel gel highlight. Every theme hook, the main
menu, the lobby's pad/button/dropdown/slider/popup helpers and the settings
screen (through two small fill/control helpers replacing its raw rect
pairs) now draw through it, so all three drawing layers share one shape
language.

Panels become a translucent membrane that the colony tints through
(opaque under low-speed graphics); the two lobby screens paint the world
only and own their page membrane, instead of layering a sheet over the
theme's widget-union panel. List rows stay quiet until chosen; keyboard
focus is a violet ring, distinct from the ink hover ring, which had become
invisible once line and ink shared a value. Widget geometry and hit rects
are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
…drift

The colony's cloud pass used to run inside drawMap, under the panel, where
widgets painted after it could never receive it. Add a DRAW_NO_CLOUDS
option so the menu colony skips that pass, and a Style::afterPaint hook
(the same optional-hook shape as onFrame) that Screen::dispatchPaint calls
once every widget has painted. FrontendTheme uses it for one shadow+cloud
pass over the whole window, computed by MenuColony from the viewport of
its last draw so the field stays anchored to the terrain. One compute per
frame, as before; the engine's own guard keeps it OpenGL-only.

MenuColony also drifts its camera about a tile and a half on a slow
Lissajous, applied as a sub-tile transform on OpenGL; the software path
keeps its integer-tile viewport. Both are presentation state, outside the
simulation and its checksums.

The harness's Preview::render now mirrors dispatchPaint by invoking the
style's afterPaint, so captures and recordings show what the game shows.
Measured on a 120-frame OpenGL recording: the world shifts (-9,-4) px
over 80 frames and the panel band's luminance moves 198→204 as a shadow
crosses it; before the harness change the band was a flat 202.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Buttons drawn through the theme and the main menu inflate by up to two
pixels as their highlight rises and shrink one pixel while pressed; lobby
pads swell one pixel under the cursor. Hit rects are untouched. The front
page fades in over the screen's first ten frames using Screen's existing
animationFrame, panel, wordmark and buttons together. Harness previews
render the settled state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
A worker from the menu colony, in its team colour, walks the empty band
under the utility buttons at about 24 px/s, turning at the panel's edges
and pausing now and then. The classic sheet's frames carry a faint dark
matte that never showed on grass but does on a pale membrane, so the
sixteen walk frames are composited once with that matte removed, the way
the wordmark is pre-fitted. It fades in with the rest of the page and
cloud shadows pass over it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Replace the review's spike captures with the shipped result on both
backends, rewrite the review README as a record of what changed and how it
was verified, and update the front-end refresh notes for the membrane
palette, camera drift and the cloud pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Lower the front-page membrane to alpha 140 (from 214), so the terrain
shows through the sheet rather than only tinting it. panelAlpha() takes the
normal-mode value as a parameter; other screens keep theirs. Buttons are
opaque gel, so legibility is unchanged. Refresh the two main-menu captures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
blob() painted every row full-width in ink and laid the fill over it, so a
translucent fill blended against opaque ink rather than the world: lowering
the alpha only turned the sheet gray. Draw the ink as a true two-pixel ring
and the fill only between, so nothing overlaps. Every membrane is brighter
and warmer as a result, and the colony now reads through the front page at
alpha 140. Refresh all captures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
The settings screen darkened the world with its own alpha-200 scrim and
drew a near-opaque sheet over the theme's widget-union panel, so nothing
read through and its colour sat apart from the front page. Paint the world
and the shared scrim only, then one sheet at the common membrane alpha.
Refresh the settings captures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRgZCRPabZw9EKnhWNJG1S
Playtesting surfaced three concrete bugs behind an overall "feels 90s"
reaction:

- Button hover swell was computed as int(hi)*2/255 -- integer division
  on a smooth 0-255 highlight collapsed it to 2-3 whole-pixel states
  instead of a continuous grow. Add FrontendTheme::swell(), which draws
  the base size opaque and crossfades a one-pixel-larger outline over
  the fractional remainder, so hover/press reads as continuous. Used by
  the main menu buttons and the shared theme's button background.

- MenuColony's camera drift floored to a whole pixel before splitting
  into tile + sub-tile fraction, so the camera sat still for seconds
  near the drift's turning points and then hopped a pixel. Keep the
  drift a double all the way to the GL transform, matching how the
  in-game camera's own fraction is already a float (TorusView::draw)
  for the same reason.

- blob()/ring() rounded every corner and wobble offset to a whole
  pixel with no anti-aliasing, and the wobble was applied inside the
  rounded-corner curves themselves, chipping them into notches. Carry
  fractional insets through to the boundary pixel, blended by coverage,
  and fade the wobble to zero inside the corner curves.

None of this changes layout, hit rects or the simulation: re-ran
MenuColonyHarness check/navigation/sessions, CustomGameSetupHarness,
and run-game-speed-tests.py (checksum 8b9c6da6, unchanged).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR
Regenerate all after-*/after-gl-* captures via MenuColonyHarness
capture (software and GL) so the documentation matches the current
render exactly, and record the three fixes and their re-verification
in the review README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR
@genixpro

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up after playtesting the built client: three concrete rendering bugs behind the "feels a bit 90s" reaction, all draw-only fixes (no layout/hit-rect/simulation change, re-verified — see docs/ui-visual-review/README.md):

  • Button swell was only 2-3 discrete states, not a smooth grow (int(hi)*2/255 truncation). Now crossfades continuously via FrontendTheme::swell().
  • Menu-colony camera drift was jarring — it floored to a whole pixel before deriving the sub-tile fraction, so the camera would sit still for seconds then hop a pixel. Now stays a double through to the GL transform, matching the in-game camera's own convention.
  • Contour edges read as pixel art with notches at cornersblob()/ring() had no anti-aliasing and let the wobble chip into the rounded corners. Now anti-aliased with the wobble fading out inside the corner curves.

All 11 after-*/after-gl-* captures are refreshed to match.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR

genixpro and others added 2 commits September 10, 2026 19:18
Playtesting pushed back on the cream/sand chrome itself, and separately
on how see-through the front page had become:

- FrontendPalette::membrane/gel/gelDisabled are a green tint instead of
  sand, at the hue actually sampled off the rendered terrain (~HSL
  120/52/25), rotated onto the cream tokens' existing saturation/
  lightness shape and then pushed a step more saturated. ink, gold and
  violet are unchanged.
- The front page's membrane alpha, deliberately dropped to 140 earlier
  in this branch to let the colony read through, overshot -- raised to
  210. The shared panelAlpha() default used by Settings and custom-game
  moved from 214 to 235 to match.

Re-ran the same harness gates and run-game-speed-tests.py; checksum
unchanged (8b9c6da6).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR
Regenerate all after-*/after-gl-* captures via MenuColonyHarness
capture (software and GL) to match the green chrome and reduced
transparency, and record the change in the review README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR
@genixpro

Copy link
Copy Markdown
Contributor Author

Second follow-up: the cream/sand chrome itself was the remaining complaint ("still feels a bit 90s"), plus the front page had gotten too see-through.

  • FrontendPalette::membrane/gel/gelDisabled rotated from sand to green — hue sampled off the actual rendered terrain (not eyeballed), landing at membrane(168,230,168) after an over-saturated (103,228,103) attempt read as too much.
  • Front-page membrane alpha raised from 140 → 210 (it was intentionally dropped to 140 earlier in this branch, which overshot); shared panelAlpha() default raised from 214 → 235 to match.

Screenshots refreshed again. Simulation checksum still unchanged (8b9c6da6).

🤖 Generated with Claude Code

https://claude.ai/code/session_01WkvtS9itQDnSNEqi6M3CbR

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