The crowd sheet becomes an animation sheet: three frames, and the stand sits back

Oliver: "when clapping I barely see any movement at all now.. also all of the
art could be colored a little more dim as it is perspective wise farther in the
background and should not steal too much attention" — and then, "can't we make
an animation sprite sheet of the new crowd?", which is the answer to the first
half of the first one.

A third roll (_crowdroll.py mid) takes the arms-up frame as its reference and
asks for the same standing people with their hands brought together in front of
their faces. It drifts by 53% of its pixels and that does not matter: the same
filter keeps only what was stand and became person. 100 arm blobs in pose 1, 74
in pose 2, 134 spectators once merged, 28 of them owning BOTH poses — those 28
cycle between two raised poses, which is a clap; the rest alternate with the
seated frame, which is arms going up and down.

The point is the cycle, not the pose. A raiser is ~8x6 logical px on a phone and
one that appears and then holds for a second is not something the eye catches;
arms moving at 3.5 Hz is. Measured: 2-3% of the stand band now changes every
90 ms during a won game, against near zero before.

The sheets are one file per court now — three frames stacked, graded as a single
image so they cannot drift apart in colour — and x_crowd2/_c2/_h2 are deleted.

TUNE.standBack 0.22 dims the crowd and the boards on every court before the
night court's own dim and the splatter rot, in the one function both the cached
backdrop and the live raisers already read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-21 13:59:02 +02:00
1 parent 1ad2898530
commit 0250e9bd20
14 files changed
+333 -198

No files matched your search

+52 -20
View File
@@ -21,8 +21,8 @@ the state-machine contracts that have historically produced the show-stoppers. T
it as a build stage, so a red suite cannot deploy — but finding out from Coolify is the slow way.
Last updated 2026-08-21 (THE CROWD: rolled again, in far more detail, once for all three courts;
the clap frame is now the seated sheet plus the raised arms and nothing else, and the stand animates
ONE SPECTATOR AT A TIME off `src/crowd.js`. The wave, the idle fidget, paparazzi that scale with the
every frame is the seated sheet plus that pose's raised arms and nothing else, the sheet is a real
three-frame animation sheet, and the stand animates ONE SPECTATOR AT A TIME off `src/crowd.js`. The wave, the idle fidget, paparazzi that scale with the
moment, and an aww that still claps. See "THE CROWD, ROLLED AGAIN". Before that 2026-08-20 (THE SHELL: a Capacitor iOS project, haptics on the existing sfx call
sites, portrait enforced in Info.plist, and swipe-left-to-go-back in the menus — the web build
unchanged and still primary. See "THE SHELL"). Before that, same day: the friend across the net gets his OWN sheets — the player's kit, drawn
@@ -348,24 +348,36 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
`mopRound` walks him to a stain, holds him there for `TUNE.boyMopDwell` while the push cycle
animates in place, then moves him on. Three sheets: `x_boymop` (stopped, working),
`x_boycarry` (walking with the mop shouldered), `x_boyrun` (tennis-mode fetch).
- **THE CLAP FRAME IS THE SEATED FRAME PLUS THE ARMS, and that is meant literally.** `x_crowd2` is
built FROM `x_crowd` by `assets/_crowdpass.py`, which copies only the pixels that were STAND in
the seated roll and became PERSON in the clap roll — 93 pairs of raised arms and hands, 1.2% of
the sheet. Not the raiser's face, not his shirt, not his seat: those are the seated sheet's own
pixels and cannot drift. Each pair of arms has a box in **`src/crowd.js`** (generated), and
render.js raises **one spectator at a time** out of that table. It was NOT true before 2026-08-21:
the clap sheet was a separate roll that differed from the seated one in 13-16% of its pixels,
everywhere, so the animation re-hashed the crowd rather than raising arms — Oliver, "the crowd
wobbles with a client side animation right now.. that feels odd." **Never hand-edit one crowd
frame, and never edit `src/crowd.js`.** Re-roll with `_crowdroll.py`, then re-run `_crowdpass.py`,
which rebuilds the pair, the three court grades and the table together. Draws stay integer-aligned
with **no vertical offset**: a fractional `dy` on a scaled image re-snaps its pixel rows every
frame under nearest-neighbour — that boiling beside the HUD was the "scoreboard flicker".
- **THE CROWD SHEET IS AN ANIMATION SHEET, and every frame of it is frame 0 plus arms.** One file
per court, `FRAMES` frames stacked vertically: 0 seated (what the cached backdrop draws), 1 arms
raised high, 2 hands together in front of the face. `assets/_crowdpass.py` builds 1 and 2 by
copying, out of two further rolls, ONLY the pixels that were STAND in the seated roll and became
PERSON — about 1.2% of the sheet each. Not a raiser's face, not his shirt, not his seat: those are
frame 0's own pixels and cannot drift. **`src/crowd.js`** (generated) boxes each of the 134
spectators who can move, with a mask of which raised frames they appear in — 28 own both, and
those 28 cycle between the two poses, which is a clap rather than an appearance. render.js
animates **one spectator at a time** out of that table.
It was NOT true before 2026-08-21: the clap sheet was a separate roll differing from the seated
one in 13-16% of its pixels, everywhere, so the animation re-hashed the crowd rather than raising
arms — Oliver, "the crowd wobbles with a client side animation right now.. that feels odd."
**Never hand-edit a crowd frame, and never edit `src/crowd.js`.** Re-roll with `_crowdroll.py`,
then re-run `_crowdpass.py`, which rebuilds every frame, all three court grades and the table
together. Draws stay integer-aligned with **no vertical offset**: a fractional `dy` on a scaled
image re-snaps its pixel rows every frame under nearest-neighbour — that boiling beside the HUD
was the "scoreboard flicker".
- **A raiser is ~8x6 logical px, so a pose that appears and holds is invisible.** What reads as
applause is the CYCLE — arms high, hands together, up, down, at about 3.5 Hz — and how many people
are doing it at once (`crowd.pop`). Oliver on the first per-person cut: "when clapping I barely see
any movement at all now."
- **The three courts are ONE crowd, graded.** `x_crowd`, `x_crowd_c` and `x_crowd_h` are the same
roll under three per-channel histogram matches onto the courts' old palettes, so anything the
crowd art gains shows up on all three. They were three separate rolls until 2026-08-21, and the
cost of that was exactly what Oliver reported of the first pass: "the whole thing somehow does not
translate well to the other courts, there the difference is barely visible."
roll under three per-channel histogram matches onto the courts' old palettes, applied to the whole
stack at once so the frames cannot drift apart in colour. They were three separate rolls until
2026-08-21, and the cost of that was exactly what Oliver reported: "the whole thing somehow does
not translate well to the other courts, there the difference is barely visible."
- **The stand sits BACK.** `TUNE.standBack` (0.22) dims the crowd and the boards on every court,
before the night court's own `dim` and the splatter rot. Oliver: "all of the art could be colored
a little more dim as it is perspective wise farther in the background and should not steal too
much attention." It is the only thing on screen that is thirty metres away.
`assets/_sheet_crowd1.png` is the pre-brightening original of the FIRST crowd, kept for history.
## Settled feel decisions — do not regress silently
@@ -2314,7 +2326,7 @@ it is the WRONG tool, superseded by the reference-sheet flow. Route by job:
| THE SAME CHARACTER IN SOMEONE ELSE'S CLOTHES, every pose at once | `_frontroll.py` — famroll's trick pointed at the tennis poses: image 1 is the ten `o_*` frames composed from disk, image 2 is the body to dress them in. Two calls made the whole netplay family. Ten `_regen` batches would have been ten chances for the man to drift. |
| Single-sheet remaster, boss roster, any non-family sprite | `_regen.py` + `_regen_batches.json` — the only jobs it is still for. |
| Standalone art (logo, panels) | `_key.py`; coarse legacy sheets, `_slice.py`. |
| The CROWD — the art itself | `_crowdroll.py` (two rolls: the seated master, then the same picture with arms up) -> `_crowdpass.py` (throws the drift away, keeps the arms, grades the three courts, writes `src/crowd.js`). Both frames of all three courts come out of one run; there is no such thing as editing half of it. |
| The CROWD — the art itself | `_crowdroll.py` (three rolls: `seat`, then `clap` from it, then `mid` from that) -> `_crowdpass.py` (throws the drift away, keeps the arms, stacks the frames, grades the three courts, writes `src/crowd.js`). Every frame of all three courts comes out of one run; there is no such thing as editing half of it. |
**And when installed sprites look WORSE than the `_refcheck/fam_*.png` sheet they came from, the
suspect is the CUT, not the roll** — the sheets on disk are the evidence, and a re-cut is free (see
@@ -2595,6 +2607,26 @@ never the right tool. It is deleted. The art is rolled.
- **MORE PHOTOGRAPHERS.** `TUNE.flashEvery` 5.0 -> 1.9. At a five-second mean, jittered, spread over
a stand this wide, Oliver never saw one.
### The clapping was invisible, and the stand was too loud (2026-08-21, same day)
*"when clapping I barely see any movement at all now.. also all of the art could be colored a little
more dim as it is perspective wise farther in the background and should not steal too much
attention."* Then: *"can't we make an animation sprite sheet of the new crowd?"* — which is the right
question and the answer to the first half of the first one.
- **A THIRD ROLL, and the sheets became animation sheets.** `_crowdroll.py mid` takes the arms-up
frame as its reference and asks for the same standing people with their hands brought together in
front of their faces. It drifts harder than the first (53% of pixels) and it does not matter: the
same filter keeps only stand-turned-person. 100 arm blobs in pose 1, 74 in pose 2, 134 spectators
once merged, **28 of them with both poses** — those 28 cycle between two raised poses, which is a
clap; the rest alternate with the seated frame, which is arms going up and down. Either way the
stand is now MOVING rather than holding, and that is what the eye catches at 8x6 logical px.
Measured: 2-3% of the stand band changes every 90 ms during a won game, against near zero before.
- **One file per court instead of two.** Three frames stacked vertically, graded as one image so the
frames cannot drift apart in colour, and `x_crowd2`/`x_crowd_c2`/`x_crowd_h2` are deleted.
- **`TUNE.standBack` 0.22** — the whole stand, on every court, pushed back before anything else is
added. One knob, applied in the one place both the cached backdrop and the live raisers read.
## THE STAND STOPS WOBBLING, AND STARTS BEING A CROWD (2026-08-21)
*"the crowd spritesheet could use some love (resolution and movement wise)... also the crowd wobbles