Roman Kliuchkovych

Personal

A coin that isn’t there

Four Ukrainian coins, turning on their axis. Each one is three flat photographs and a single number.

about Нові українські монети (in Ukrainian), a page of coins you can turn

1.00°
Drag the coins directly, or use the slider. Nothing here is a video or a canvas — every pixel is a CSS background on a rotated div.

Rotation is a squash

With no perspective set, a turning element does not change shape and does not go anywhere. It only gets narrower. Turn it by θ and its width on screen is cos θ of what it was: full width flat on, 71% at forty-five degrees, nothing at ninety. That is the whole of it.

One thing follows. A surface sitting at some depth behind the front does not stay put while the coin turns — it slides sideways, by its depth times sin θ. So every surface in the coin comes down to two numbers: how narrow it got, and how far it slid.

Five surfaces

The markup gives each coin three children — .coin-face, .coin-edge, .coin-back — but there are five real surfaces, because the edge carries a ::before and an ::after. The edge photograph is used three times per coin: once as a narrow slab stood side-on, and twice as a full-size disc sitting in the front and back face planes.

Pull the five apart and the whole argument is over in about a second. The thing you read as the side of the coin is not the slab. The slab is that narrow strip standing side-on, and it spends most of the turn hidden behind the obverse, contributing nothing. What you have been looking at is the crescent of the back disc, sliding out from behind the face because the two sit at opposite depths and separate by d · sin θ.

Push them back together and switch the back disc off on its own to confirm it: the volume leaves with it.

What makes the substitution hold is that the five surfaces add up to the right outline. Their combined silhouette is a rectangle flanked by two half-ellipses — which is precisely the parallel projection of a cylinder. The coin is not a cylinder; it just casts the same shadow as one.


  
0px
145.00°
Each surface is tinted to match its switch below; the three edge surfaces share a hue because they are the same photograph three times over. “Pull apart” separates them sideways within the coin’s own space. Separating them in depth would show you nothing: without perspective, translateZ cannot change what you see except through rotation.

The edge that isn’t there

Reeding is a set of ridges running around the rim. Vertically it has rhythm; along the axis the code stretches it, it is featureless — there is no landmark by which a stretch or a misalignment could be caught. The code leans on that hard. The strip is pulled to one and a half coin diameters, a figure that is essentially arbitrary because nothing in the picture shows the stretch. A real rim is a cylinder, so its ridges should crowd toward the edge of the silhouette; here they lie on a flat disc and compress linearly instead. And the slab and the discs share one background-size at very different widths, so each shows a different crop of the same picture at a different scale — the slab about 6% of it, the discs about 67%.

Rule the lines across that axis instead and all three surface at once. Nothing about the geometry changes; only whether you can see it.

All of it depends on there being no perspective. Plain squashing is what lets five flat surfaces add up to a cylinder’s outline; the moment lines start converging they add up to something else, and the coin picks up a camera position. In a row it is worse: each coin would sit at a different angle to the vanishing point, so comparing denominations — the entire point of the original page — stops working.

none
62.00°
Flat planes rather than photographs: the same five surfaces, with nothing to look at but where they are. Rule the reeding and the slab and the crescent visibly disagree about which part of the strip they are showing. Slide perspective in and the silhouette stops being a rectangle between two half-ellipses.

Light that isn’t there

Nothing on this page is lit. There is no light source, no material, no surface normal to take a dot product against — and the coins still read as metal sitting in a room. Two flat films and one shadow do all of it, and each is driven by the same --rotate that turns the coin.

The obverse darkens as it swings away: a black film at rotate × 0.002, about a third of an opacity point by the time it is edge on. The reverse carries the opposite, a veil starting at 0.63 that thins as that face comes round toward you.

The third, the reflex, is the one worth stealing. The obverse casts a box-shadow coloured with var(--bg), the colour of the ground the coin stands on, so against that ground it is invisible. It only shows where it falls on the neighbouring coin, washing the ground’s colour across it — which is what a real coin on a real table does. Whether it reads as light or as a stain depends on which side it arrives from, and nothing in the code decides that. The veil darkens the face turning away, the reflex brightens the edge turning toward you, and the two agree only because the coins overlap the way they do. Reverse the row and the reflex comes from the shadow side, lighting the face the veil is darkening.

22.00°
ground
Two coins, because the reflex has nowhere to land on one. Flat planes, so the only thing changing is the shading. Switch the ground and watch the veil and the reflex follow it — that is the entire lighting model.

None of this needs 3D

Every surface is a plane at a fixed depth, so every one of them is nothing but scaleX(cos θ) plus a sideways offset of d · sin θ; the slab, being perpendicular, is scaleX(sin θ). Five 2D transforms, the same pixels, no preserve-3d, no depth at all. CSS has cos() and sin() now, so it does not even need script.

What the 3D actually buys is bookkeeping. It does the trigonometry, so one rotateY per coin replaces five hand-derived expressions that would have to be kept mutually consistent. It culls the backfaces, so obverse and reverse swap over at 90° by themselves. And it sorts the layers by depth, so the crescent emerges from behind the face without anyone maintaining a z-order that changes halfway through the turn.

The illusion is built out of flat pictures and a handful of scalar expressions. The 3D is there to keep those expressions honest, not to make the volume.