Skip to content
Pipfold

Four stages until the box stood in a room — without a single image file

· design · game

After four days Pipfold was playable and looked like a prototype: flat little boards, one sun, the same surface everywhere. The design review turned into four stages, built over two days. None of them put an image file into the package.

The box before the review: flat colour fields, the wood a single brown, the felt an even green without texture. The flaps are plain rectangles with very large black digits that look printed on. The dice have sharp edges and flat dots, behind them an empty brown background.
before stage 126 July. One brown, one green, sharp edges. Tray, cheeks and flaps differ only by colour.
The same box after the four stages: visible wood grain with growth rings per board, dark butt joints at the corners, chamfered edges catching a line of light, shadow gaps between the flaps, lit digits, sunken pips with contact shadows, a darkened seam around the tray and a vignette at the edges of the frame.
after stage 4Same camera angle, same resolution. All the material is computed — the package contains not one image.

Both shots come from the same tool (tools/schuesse.sh, shot spiel, 1280×720), the left one from a checked-out tree of 26 July.

Stage 1 — the five cheap points

First the things that cost nothing but attention:

In portrait, a hard stripe ran across the picture. That was the edge between table and back wall — a wall that existed only because a room has one. It is gone; the table became 16×16 units and was pulled back, the fog denser. What lies behind now disappears instead of stopping at a line.

The tournament standing read 0:0:0:0 and now reads “Anna 2 · Ben 1”. Empty stays empty instead of showing zeros.

And the wood was differentiated for the first time: light top edges, mid-tone cheeks, dark inner panels. Three tones where there had been one — that alone turns a block into an assembled box.

Alongside, scripts/stil.gd pulled some 34 scattered colour values into one place.

Stage 2 — light in three roles

Before: one sun plus a fill. Now three lights with distinct jobs — a warm key from the front left, a cool rim light from the back right without shadows that draws a bright edge on flaps and dice, and a warm fill at the front.

The biggest gain, though, was something else: contact shadows under the dice. A flat surface with a soft radial gradient that follows the die in x and z, growing and fading with its height during a throw.

That is the information that separates “floating” from “resting”. The sun’s cast shadow alone leaves small things floating — it is too far from the object to say how high it sits.

Plus roughness per surface: felt 0.97 matte, cheeks 0.60, top edges 0.48, flaps 0.44 lacquered, dice 0.34. Before, every surface shared one value — which made felt, wood and bone look like the same material in three colours.

Learned: A GradientTexture2D used directly as albedo_texture stays invisible in the Compatibility renderer. The surface is drawn, the texture never bound. ImageTexture.create_from_image() fixes it. Half an hour went into looking for a gradient that was not there.

Stage 3 — the wood is computed

shaders/holz.gdshader: growth rings around a trunk axis running through the workpiece along the grain, plus compressed fine noise for the longitudinal fibres.

The point where it tips over: every board gets its own trunk position, fixed-seeded so the look is identical on every start. Without that the rings run straight across the joints — and the box looks milled from one block rather than built from boards. The grain direction follows the part: lengthwise on the rails, crosswise on the cheeks, vertical on the flaps.

Along with that:

And then measured

The tabletop fills the entire background; every pixel runs through the shader. So it was measured before it was allowed to stay: at 2560×1440 it costs 0.13 ms per frame — 4.44 against 4.32 without. For that: no sin() in the noise, two octaves instead of three, and the fine grain is switchable. Tabletop and inner panels do not need it.

Learned: The hinge rod first lived in the _klappen array — which is indexed by flap number and whose size decides whether the flap comb gets rebuilt. It would have rebuilt the entire comb on every move. The UI check caught it immediately: “flap hangs downwards”.

Stage 4 — the box becomes a thing in a room

The starting point was a look at landscape orientation: the box lay as a flat band in the middle of the frame, a third of empty dark above and below, the digits stuck on like decals, and the two most important pieces of text looked like debug output.

The digits are now lit (Label3D.shaded). Before, they kept their colour while the wood beneath them went light and dark — which is exactly how the eye recognises a sticker. They also got smaller (0.29 → 0.26) and optically rather than arithmetically centred: the “1” carries a flag on its left and visibly sat too far right. Two-digit numbers went the other way (0.62 → 0.72), because 10, 11 and 12 looked puny beside the single digits.

A real shadow gap between the flaps (spacing 0.012 → 0.022, body 0.96 → 0.94 of the bay width). Before, the row read as a single stripe from the playing camera.

Butt joints at the corners — four thin dark strips, no change in geometry: a block with edges becomes four boards put together.

And the dice got smaller: edge length 0.22 → 0.185. One was nearly as wide as a flap. Plus a separate yaw angle per die and an offset resting position in depth — dice lie in an exact line only when somebody has placed them there.

The geometry that would not be talked round

In portrait, roughly a third of empty table remains at the top, and it is not computable away. A box with an aspect ratio of 2.4 to 1.35 does not fill a frame of 0.46 vertically. Rotating around the vertical axis makes it wider in frame — 2.4·cos + 1.35·sin. 26° was tried and was worse.

What helps is not a trick but a decision: a wider field of view from close in (FOV_HOCH 48°), the front edge is allowed to run out of frame sideways — only the row of flaps must be fully visible — and the box sits lower. The air is at the top, where score and tools live; the box is within thumb reach.

Stage 4 was originally meant to bring physics dice — real rigid bodies instead of computed arcs. Deliberately left aside: it collides with the reproducible sequence of rolls, and the game needs that for “same dice for everyone” and for the test runs. A die that falls differently on every start is more realistic and makes every guarantee unverifiable.

What remains of the four stages

The shipped package contains not one image for any of this. Grain, felt texture, contact shadows, vignette, embossing, butt joints — all generated at runtime or built from geometry.

That was not thrift. It is the reason the game is 14 MB and not 140 — and why a fourth wood set costs two colour values instead of a texture series.

← Devlog