· genfit series · day 2

ONE-ARM BANDIT

Pull the lever, the machine deals you a mood, a piece of gear and a length of time, and the man behind the glass attempts it in complete earnest until something breaks. Day 2 of the GenFit series.

Desktop:
Click anywhere or press space to pull the lever. That is the whole game. Leave it alone and it pulls itself.
Mobile:
Tap anywhere, one thumb. Leave it alone and it pulls itself.
Rate this game

No account needed.

Devlog

This one is marketing too. Day 2 of a series of games for my own app, said out loud because saying it is the bit.

The app is GenFit. You give it a mood, whatever gear you actually have, and how long you've got, and it gives you a real workout. 873 exercises, twelve kinds of equipment, free. Read that sentence again and it's a three-reel slot machine. Mood, gear, time. So tonight it's a slot machine.

One input. Pull. There's no aiming, no timing, no fail state, nothing you can drain or trip. The reels clunk down one at a time, the man in the display window starts that workout in complete earnest, and about two seconds later something breaks. The score is PANES: sheets of glass replaced. Six wrecks, one per piece of gear. The kettlebell goes through the front glass at you. The park bench gets ridden into the side wall. The pull-up bar comes out of the ceiling with him still on it. Nothing at all means shadowboxing, and the glass is not shadow.

Yesterday's game was the reason for all of that. SKIP DAY got nine blind judges to sign it off and then a person picked up a phone and the joke never happened once, because casual play kept tripping a reset that stopped the fall. So tonight the gag is the input. Every pull ends in a wreck. There is no way to not see it. Under two seconds of doing nothing he fidgets, taps the glass, does one sad half-rep, and at two and a half seconds of nothing the machine pulls its own lever. A player who never touches the screen still watches a man put a kettlebell through a window every seven seconds.

That isn't an aspiration, it's a gate, and it's measured. A Playwright thumb at 390×844 taps one random spot every 1.5 to 2.5 seconds with no idea what's coming, for a minute. On the rebuilt machine: first wreck at 4.5 seconds, seven of them inside fifty, worst gap between two wrecks 8.65 seconds against a ceiling of 15. No judge score counted until that had passed.

The gate nearly didn't. The first run had a 17.9-second hole in it and it took me a while to see why: the jackpot. A coherent combination — furious, one kettlebell, seven minutes, a workout GenFit would actually hand you — pays out instead of breaking, and the ticket slot prints a real workout card. I'd made that card a state. The machine stopped and waited for a tap. Which is exactly, precisely the thing that killed yesterday's game, rebuilt from scratch in a different shape. So the card is data now, not a state. It prints over the bottom of the screen, it never blocks a pull, it clears itself after seven and a half seconds, and the machine goes on wrecking itself behind it. The jackpot ends in broken glass too — it pays so hard it takes its own window out. Every loop, no exceptions, ends in a pane.

There's no physics library in this repo. So the man, the kettlebell, the bench, the glass shards and the coins all run on one verlet solver: points, distance constraints, gravity, six iterations. He's driven toward a pose by springs rather than animated, so the mood changes how hard and how fast he does everything and the wreck is just the springs letting go. That turned out to be the right constraint rather than a workaround — a floppy man made of sticks is funnier than a correct ragdoll, and he costs nothing on a phone.

Nothing downloads. Every texture in the machine is drawn into a 2D canvas at load: the marquee, the three reel strips, the crack web, the speckled black paint, the man's four faces. The whole scene is seven thousand triangles.

The build was the dream loop. Lock a target screenshot, build toward it, hand a fresh judge the target and a live frame and let it be as rude as it likes. I ran eight rounds, watched the score climb to 7.5, and called it done. That got rejected, and a judge that had never seen the game put the same build at 2.2 out of 10. It was right and I wasn't. A score I watched go up over eight rounds turned out to measure how used to the thing I'd got, not how good it was.

Two findings in the first reopened round were worth the whole rejection.

The first was my own test rig. The clip is a named frame — SMUG, one kettlebell, two minutes, the bell coming through the glass at you — and my screenshot script had never pinned it. It shot whatever the reels happened to roll. So the judge spent its entire verdict carefully describing a park bench as "a hammer" while comparing it to a kettlebell target. Every round before that had been graded against the wrong picture. The machine already had a function to force a combination; the script just never called it. It calls it now, on every poll, because setting it once loses the race against a pull already in flight.

The second was the paint. The judge sampled the cabinet body at #14161C against the target's #8A8E93. I'd painted the entire machine the black crackle that belongs only in the recessed panels. On the target it's two materials with a hard line between them — light brushed metal for everything structural, black crackle only where something is inset into it. That one error had been flattening every round's score and I'd been reading it as a lighting problem.

Then the lighting problem, which was also real. A pawn-shop machine in a dark room is a contrast problem, not a brightness one. Ambient came down to a quarter of what it was, two cold lights went in behind the cabinet's shoulders so every vertical edge gets a line down it, and the marquee got promoted from a sticker to an actual lamp. And the display window, which had been a flat grey quad, got rebuilt as a real box: the back wall had been wider than the opening with its side walls parked exactly on the window edge, so they hid behind the front plate and there was no room in there at all.

Then I over-corrected and a judge caught that too. My new interior sampled at #a0a7b8 where the target is #47545c — I'd built a photo studio inside a slot machine. Turning lamps down did nothing, because the culprit wasn't a lamp. The environment map lights every surface whether or not a light can reach it, and a roughness-1 wall still drinks it. Cutting the environment on the interior materials and swapping the hood light for a spot pointed at where he stands is what finally made the cavity black. Which immediately turned the kettlebell black too, since it leaves through the other side of the window — so the bell carries its own lamp now, and it travels with it.

Three judges in a row called the kettlebell a padlock. The third one finally said why, and it was nothing I'd been fiddling with. The handle's legs ran down outside the ball and past its equator, with a gap where they met and a different material on each. That's a shackle. Every kettlebell cue was missing: no collar, no flat base, and an opening taller than it was wide. Rebuilt in units of the ball's diameter — a cone frustum for the shoulder, an extruded rounded-rect grip whose hole is wider than tall, legs terminating inside the collar and inboard of the ball's own silhouette — and it stopped being a padlock in one round. The same judge also told me to scale it up by two. I measured the target myself, got 26% of the frame width against its claim of 56%, and didn't. At two it buried the entire machine behind a kettlebell. The geometry was the padlock; the size never was.

The outline pass then filled the handle's hole with black. An inverted hull scales a shape's hole open along with everything else, so the shell's own back face shows through the one gap that proves it's a handle. The ball gets the ink line now and the grip doesn't.

The cobweb was the best debugging of the night, and it came from reading how the texture is sampled rather than how it's drawn. The web is a 1024-pixel canvas stretched across the pane with clamp-to-edge wrapping, and at full spread the window only ever sees the middle nine hundred or so pixels of it. Every crack was being drawn out past that. The clamp took those edge pixels and smeared them into long, straight, uniform-weight lines running to the frame — which is exactly what a spiderweb decal looks like. Nothing in the drawing code was wrong. The mismatch was between where the lines were drawn and where the pane could see. Cap every line inside the visible window, let the spokes die at random lengths, and it turns into broken glass.

One more of that shape: the bell and the hole it made were in different places on screen. The web is pinned to the impact point at the glass; the bell keeps going toward the lens; and perspective pulls them apart, because at twelve units out a given position projects nearly twice as far from centre as the same position back at the pane. So the web sat left while the bell drifted to the middle and the frame stopped reading as cause and effect. It's a presentation fix, not a physics one — while the prop is in the air its screen position is locked to the epicentre. It comes out of its own hole at you now.

And the glass shards, twice. Small ones read as white confetti stuck to the cabinet's paint. Scaling them up so they'd read as glass rushing the lens turned them into paper aeroplanes crossing the whole frame. The target has no loose glass anywhere — the hole, the flash and the shake carry the hit — so they're clipped to the pane and that's the end of it.

Six blind judges on the reopened build, each one new to it: 2.2, 3.4, 3.3, 3.85, 4.2, 4.9. The numbers are much lower than the ones I'd been giving myself and they're the honest ones — every round of the first pass had been graded against a park bench standing in for a kettlebell. What moved fastest wasn't craft, it was measurement. One judge told me the target's kettlebell is 56% of the frame's width and to double mine; another wrote a PNG decoder, gave me the pixel coordinates of the ball's edges, and got 24.6% against my 30.3%. I'd already tried the doubling by then and it buried the whole machine behind a kettlebell. A judge that asserts a number and a judge that shows its working are different instruments.

The clumsy gate ran four more times on the rebuilt machine and passed every time: first wreck between 2.0 and 4.5 seconds against a ceiling of 20, worst wreck-to-wreck gap between 8.7 and 12.3 against a ceiling of 15.

The last fix was the one I'd been circling all night. The man and the wall behind him were within five percent of each other in value — and ours had the wall brighter than him, where the target has it the other way round. I turned the hood lamp down first, which did nothing, because turning a lamp down moves the subject and the background together by the same amount. The wall's own paint was the problem. Painted the cavity nearly black, put the lamp back up on him, and he had a silhouette for the first time: 75 against 52, where it had been 49 against 68. Everything in that box — the man, the iron, the white of the cracks — had been fighting a lit wall it should have been sitting in front of.

It is not at the target. It's at about half of it, and I'd rather write that down than round it up like I did the first time.

The end screen is where the brand is loud. The ticket slot prints a card that looks like the app's own output: the combination you landed, a real workout for it — kettlebell swings, goblet squats, dead hangs, whatever that gear actually gives you — your panes, genfit.fit. The line under it is "the app skips the lever."

What's still on the list. The coin tray is a drawer now, but the coins land scattered in it rather than heaped over the lip, and the target has one caught mid-fall below it that mine doesn't. The man is a box torso and a box head with four drawings on a 128-pixel strip for a face; he's got a neck and hands and a domed beanie now, and he still wants a jaw that isn't a separate lump. And the workout ticket says genfit.fit in type where the brief asked for a QR code, because there's no QR encoder in this repo and I wasn't going to add a dependency at midnight for it. Day 3.

Watch

ONE-ARM BANDIT · 3:42

Leaderboard

Loading…