Skip to content

Watering can: a shader Setup where staying put grows you a garden - #165

Merged
charliehuge merged 22 commits into
mainfrom
claude/watering-can
Nov 6, 2026
Merged

charliehuge merged 22 commits into
mainfrom
claude/watering-can

Conversation

@ii-impy

@ii-impy ii-impy commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Adds watering_can, a new shader player Setup (ADR-0021). Closes #163.

What visitors see

  • Empty room: plain paper with a muted crayon sun in the top-right corner.
  • Standing in one spot: sprouts start at the lowest points of the silhouette (feet, hands, the bottom of a raised arm) and climb. They become dense leafy vines, and over about 20 s they bloom into a mixed bouquet. The person is never drawn: you only see them through what grows on them. Colour ripens with growth, from muted to vivid, so full bloom is the payoff.
  • Waggling is free: the plants ride on the body. Arm waves, flailing, swaying and bending don't cost any growth.
  • Walking rewinds: if the feet move to a new spot, in any direction (including toward or away from the camera), the growth plays backwards. Once the person stops, it resumes from where the rewind left off.
  • The sun follows the garden: it brightens and grows rays as total growth rises.

How it works

  • Buffer B, body memory (per pixel):
    • Presence: soft, hole-filled and eased.
    • Run extents: the bottom and top of the vertical stretch of body each pixel sits in. They are measured on a screen-fixed grid, so a run's rows agree on its ends.
    • Owner slot.
    • A "hold": a lost person's plants fade over 2 s instead of popping, and every texel of a person steps together.
  • Buffer C, garden state:
    • Per tracked person: growth, the planted spot, and the base (feet), measured locally.
    • Walking detection.
    • A ghost per slot, so someone who briefly drops out of tracking or gets a new ID keeps their growth.
    • Inheritance on tracker splits.
    • The sun level.
    • A coarse "body nearby" grid, so an empty paper pixel costs one fetch.
  • Image: paper, then the crayon sun, then vines, leaves and flowers derived from B and C every frame.
  • Tunables: all of them are at the top of common.glsl. Edit and press R.
  • Design and plan: the design is docs/superpowers/specs/2026-11-06-watering-can-design.md, with the approved mockup next to it. The build plan is in docs/superpowers/plans/.

Testing

  • test_watering_can.py adds 53 headless GL tests, driven by the real BodyBlobs tracker on synthetic masks with a 10 fps camera under a 30 fps render. They cover:
    • full bloom in about 20 s under edge flicker and dropout holes
    • arm waving and weight shifts not rewinding
    • sideways and toward/away steps rewinding
    • growth resuming after a walk
    • neighbours staying independent
    • two touching still people whose contact flickers both keep growing
    • tracker loss, re-ID, merge/split and split-off limbs keeping growth
    • fades that don't pop or tear
    • thin arms blooming
    • the frame being stable under flicker
    • timing independent of render rate (1/30 to 1/240)
    • bounded state
  • make test: 452 passed.
  • I checked stills of each growth stage by eye. --mock-camera --windowed ran at 30 fps (the frame cap) on a desktop GPU.

Known issue (not fixed in this PR)

  • Two people walking together whose contact flickers faster than every 0.5 s keep growing instead of rewinding.
    • Cause: the re-plant that protects touching still people from merge/split flicker also suppresses step detection.
    • Suggested fix: keep counting a step whenever the locally measured base moves past WALK_DIST from the spot, even while re-planting.
    • Smaller related cases: a re-planting neighbour can mask a merged coasting track's walking, and someone arriving close to a walker suppresses that walker's steps for 0.5 s.

Not verified yet

  • Frame rate on the show Pi 5. Check this before the show. The Setup is in setups.json. Per pixel, a body pixel costs about 29–47 B taps; C costs about 590k fetches a frame. If it's short, cut in this order: leaf count, petal count, VINE_NEIGHBOURS, RUN_STEPS.
  • The real Orbbec camera. None of the following has been checked with it: the walking threshold (WALK_DIST), feet window (BASE_WIN), flicker immunity or re-ID inheritance. On site with --camera:
    • stand 25 s: you should reach full bloom with no rewind
    • wave your arms: nothing should rewind
    • take one sideways step: it should visibly rewind
    • two people stand touching for 25 s: both should keep growing
  • Jumping in place: it may count as walking. Tune on site.
  • Mouse check and Shadertoy paste: the --test-input mouse check and pasting the tabs into Shadertoy haven't been done.

🤖 Generated with Claude Code

ii-impy and others added 22 commits November 6, 2026 13:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Constants changed: none retuned (stills at 640x360 matched the Dense look).
  Added named sun constants to common.glsl in place of inline literals
  (SUN_WOBBLE_RATE, SUN_DRIFT, SUN_EDGE_SCALE, SUN_RIM_*, SUN_FILL_SOFT,
  SUN_GRAIN_A, SUN_HATCH, SUN_RAY_*), values unchanged.
- Fix: the sun ray at atan's branch cut had two hash ids (-6 and 6) and popped
  as drift carried it across; the id is now wrapped with mod(id, SUN_RAYS).
  Test added (RED on the old image.glsl, GREEN now).
- fps with --mock-camera --windowed: 30.1-30.2 (frame cap) over 2 min on a
  desktop GPU. Not measured on the Pi 5.
- NOT verified with the real camera: the walking threshold, flicker immunity
  and re-ID inheritance. On site with --camera: a visitor stands 25 s and
  reaches full bloom with no rewind; waving arms does not rewind; one
  sideways step visibly rewinds.
- Mouse (--test-input) check not done by eye (no human at the screen).
  Shadertoy pasting not checked (no browser).
- Docs: watering_can added to "Setups in rotation".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nal review

- Buffer B: a lost person's hold is rounded with one frame-wide golden-ratio
  offset instead of a per-texel hash, and every texel of theirs lets go the
  frame the Body leaves (not as eased presence crosses BODY_OFF), so a run's
  texels agree on the hold all the way through the fade and vines don't tear.
- Buffer C: the sun's easing is stochastically rounded to GROW_Q so it reaches
  full instead of stalling at ~0.96-0.98 in a half float.
- Tests: constants mirrored from common.glsl are parsed from it; new
  test_fading_hold_agrees_along_a_run; walker test checks body texels hold ~1;
  merge test uses unequal levels; sun test requires > 0.99 at 30 and 60 fps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the tracker merged a neighbour into a planted person's blob, the base
(the presence-weighted mean of the feet band across the whole box, y = the box
bottom) jumped by about half the gap and counted as a step, and jumped back on
the split: two still people in contact lost most of their growth.

- The base is measured locally: the feet band within BASE_WIN of the last base
  (mean-shifted BASE_ITERS times so it keeps up with a walk), with y the box
  bottom while the window finds feet there (BASE_FEET), else the person's own
  lowest body row (B's run bottom BASE_UP above the last base).
- A neighbour within SPLIT_GAP changing tracker state this frame (a merge or a
  split) re-plants the person for REPLANT_S instead of counting a step: the base
  snaps to the feet and the spot follows. BASE.z is negative while re-planting.
- A coasting track merged into a confirmed neighbour's box shares that
  neighbour's walking or not, so merged people grow (or rewind) together.
- Tests: touching still people whose contact toggles every 0.3 s, every 1 s
  and on 50% of frames each grow at 1/GROW_S; a passer-by costs a planted
  person nothing once clear.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tapered, ink-outlined trunks that rise in pairs and merge as one shape,
forking twigs ending in sprigs of pointed petals, and daisies on half the
branch tips. Keeps the colour palette and ripening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…so moving plants don't tear

Each texel decided it was body from this frame's presence, but the march found its
run's ends over last frame's body, so wherever the body had just arrived or left
the texels of one run disagreed about its ends and the plants tore into rows.
Membership now comes from last frame's stored texel alone (a frame of latency),
and the per-texel easing of run ends, which gave each texel its own history, is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Branches and twigs spaced further apart (about 0.76 of the density). Growth
reaches GROW_EARLY (0.3) in GROW_EARLY_S (2 s), then slows to full bloom at
GROW_S as before. Tests take expected levels from the same constants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Buffer C keeps a ring of FOOTPRINTS in row 3 (head in the SUN texel). A seen,
walking person drops one at their feet once FOOT_GAP from every live footprint,
only into an expired slot, so a live one never pops. Each grows a small clump of
the same ink branches, sprigs and a daisy off the floor, fast, holds, then
rewinds away. Footprints join the near grid and draw behind people's plants.
The daisy is shared out of bloom().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Daisies in the pale yellow (#fbe9a0), light blue (#719aad, the site's band)
and orange (#e8862f), centres picked to stand out from each. Branches are ink
outlines on paper, as the concept art's, so the flowers carry the colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Half the twigs end in a daisy that opens as the growing tip climbs past it:
small and pale low on a run, larger and bolder higher up (so later). Flowers
pale toward the paper, keeping their hue, rather than greying. The tips' late
daisies stay the largest. Leaves and buds are ink on paper, as the branches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the per-column vines, each rooted at its own run bottom, with a single
tree. Buffer C keeps a 1/48-screen-height grid in place of the near grid: each
body cell's parent on the cheapest path back to the person's root (dearer near
the silhouette's edge, so paths keep to each limb's middle), and how many seed
cells' paths run through it. Branches are the seeds' paths, curved through
their nodes and as wide as their seed count; seeds carry the sprigs and daisies.
Growth is a front of path distance: up both legs from the root on the floor
between the feet, through the torso, out the arms and up the neck. Nothing roots
anywhere else, and waving keeps an arm's growth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The root point (the middle of the lowest cells) jumped as a foot's edge
flickered, and seed counts, which set branch widths, rippled down the trunk
every frame. The root now eases (TREE_ROOT_EASE_S), as do seed counts
(TREE_WIDTH_EASE_S; children pass on eased counts, so a settled tree adds up).
Cells join the body at TREE_FILL and stay until half that, and keep their way
back to the root unless another is TREE_PARENT_HYST cheaper. A still person's
lower body changes about a quarter as many pixels a frame as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@charliehuge
charliehuge merged commit cdc8297 into main Nov 6, 2026
1 check passed
@charliehuge
charliehuge deleted the claude/watering-can branch November 6, 2026 22:38
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.

FundingCAPTCHA Setup: Watering Can — plants grow up through still silhouettes

2 participants