2026-09-07 · MITTENS THE KITTENS

A walk that was half a walk

Bruiser's walk cycle turned out to be four frames played twice, and his nose was facing the wrong way. Mittens' eyes went green, the dogs got real lighting, and yarn stopped looking like a hairball.

Two sessions, mostly about making things look drawn rather than generated, and about checking claims that had only ever been inferred.

Bruiser's sprite sheet

Bruiser's fight had never been properly tested, and when it was, the problems turned out to be in the art and in the arena around him rather than in the fight code. The sprite sheet had three specific faults, all measurable. All 212 frames across four sheets were rebuilt. Cell sizes, frame counts and sheet dimensions did not change, so nothing downstream needed re-importing.

The walk did not walk. The eight-frame cycle was a four-frame cycle written out twice. Frames 0 and 4 held identical legs, and frames 1/5 and 2/6 differed by one leg out of four. A dog on that table paces: both sides step together at twice the intended rate, and the 10 fps the clip is authored at becomes an effective 20. That is the difference between a heavy animal placing its feet and a wind-up toy, on the boss whose whole design note is that he is heavy.

Each leg now has its own eight-frame cycle (lift, swing, reach, plant, carry, pass, push, break), read at four offsets two frames apart. A planted foot also travels backwards in body space from +4 to -3 across its stance, because the dog moves over it. Without that the legs change drawing without going anywhere, which is the other way a cycle reads as a toy.

Frame pairDifference beforeAfter
0 and 46.2%17.4%
2 and 66.2%18.1%
3 and 75.6%15.7%

Before, every frame was a near-copy of the one four later. After, the minima sit between adjacent frames, where they belong.

The nose faced the wrong way. It was drawn with two nostrils side by side, a front-on nose stamped onto a head that is in profile in every frame of the game. At 17 by 11 pixels of near-black on a brown muzzle, the pair read as a domino mask. It also had no top: eleven rows of flat dark with a highlight stuck on the back corner is a hole cut in the muzzle, not wet leather. It is now smaller, with one nostril, a warm rim that ties it into the coat, and the highlight moved onto the lit surface. His two teeth came down from pure white too. At #F0F0EF they were the brightest thing on the whole animal, brighter than the chest and socks, so two three-pixel nubs out-read a 190-pixel dog.

The body had no form. Every coat grid is a diagonal ramp, but it is the same gradient at the same angle on barrel, haunch and shoulder, with each part stamped over the last. On the shipped sheet 57.5% of his barrel was one colour and 29.2% was one other. Three passes now run on the assembled animal rather than on each part alone, which is the real difference: one rolls every mass away from the window, one dithers interior seams only where a value step runs straight for seven pixels or more (what a stamped rectangle gives you and a hand-dithered gradient never does), and one drops the shadow the head casts onto the chest. The barrel's luma now falls steadily from 140 at the top of his back to 33 at the deepest point under him. Before, it wandered: 133 at the back, 103 at the belly, then 143 again lower down.

Two mistakes were made on the way and are only visible as numbers, both of which would have shipped looking fine:

  • The helper that moves a colour along its ramp had its two tables swapped, so the pass meant to add shadow was removing it. The idle frame's mean luma went up from 99.6 to 119.0 when it was switched on. It still looked shaded, because the separate rim pass was lifting the top edge.
  • The first seam-softening pass averaged the whole animal and requantised it. That feathers the panels, and it also drags the authored gradient toward its mean, lifting the belly rows from luma 42 and 34 to 63 and 55. The seams went and so did his weight.

The first boss, fought end to end

Stage 1 has now been played to its end screen twice, from a real gamepad. The chain held: hairball, stagger meter, stomp window, stomp, phase change, defeat, back to the map. The fight is dangerous. He took the cat from three hearts to one in a normal exchange, with correct knockback, and the second run landed three stomps to take him from 3/3 to 0/3.

The fight is built for a human. The stagger meter decays over 1.8 seconds and the snout-hit cooldown is 0.5, so the four hits that stagger him have to land inside about five seconds. The anti-spam rule works as designed: three hairballs inside one window bank a single point.

Four bugs surfaced by playing rather than reading:

  • "Bruiser did not render." He rendered fine. The debug warp cast a ray down from y 40 to find the floor, and the mud room has a roof. The ray hit it, so the cat was placed above the ceiling and the camera followed, leaving the bottom of the frame at y 5.78 while the top of his art is at y 5.03. He was drawn and animating 24 pixels below the screen with his health bar correctly armed.
  • The arena was a sealed box. The standalone test room's west wall was emitted into the stage as a solid pillar one unit east of the doorway, capped by the ceiling. Nobody could walk into the boss fight on foot.
  • Standing on the floor counted as being buried in it. The embed test was an overlap check at the collider's exact size, so a box resting flush on a surface, the most common state in the game, reported an overlap. It now shrinks the box by half a pixel.
  • The victory strut walked the cat into the wreckage. She was placed at the dog's paw height with no clearance, which on a real kill is inside the laundry basket he fell beside. The first fix cast a ray down, found nothing and changed nothing, while the push-out routine one line later detected the same basket. Asking the same question the collision code asks is what fixed it.

Eyes

Playtest feedback, off a photograph of the real cat: his eyes were more green than yellow, and lighter. Sampling the lit part of the real iris gives #878B7E. Green exceeds red there, and green minus blue is only +13, a desaturated sage. The gold it replaced had red minus green at +74. It was not a shade too warm. It was the wrong hue at roughly twice the saturation.

Five on-palette options were rendered on his face and one was picked: an iris of #B2BB5E under a lit arc of #EDD643. It keeps the iris clearly green while the highlight stays warm enough to read as a wet catchlight. The literally accurate sage read as a grey, blind eye.

His face is drawn in five places, not the three that were named, which is why the first pass left the title screen gold. Two of them needed new palette letters rather than a repoint, because the old letters also draw his bell and the gold text on the HUD, and pointing them at green would have turned the collar green. The portrait of his ancestor on the wall in the first room stayed gold on purpose.

Both dogs are lit

The two dogs did not look professionally made. Every coat grid was authored in two tones with a one-pixel edge, and the pass meant to fix that had no horizontal term at all. It measured depth straight down each column, which lights an animal as though the only lamp in the room were directly overhead, while dithering every band edge through a Bayer matrix. At three times size that is not a gradient. It is mange.

  • Depth is measured along the beam, not down the column: how far a pixel sits under the surface the window actually falls on, found by walking back up the light direction until it leaves the animal. A barrel gets a lit upper-left, a mid flank and a deep underside because of where the light is.
  • The bands are hard. Every previous version stippled the band edges, and at 3x that reads as a flat brown dog with a rash.
  • Samus' legs are rigged. Forty-six rows of the same letter cannot tell you where the knee is, so a wrong knee can only be fixed by retyping forty-six rows and hoping, which is how the same wrong leg survived being raised twice. Her poses are now built from landmarks (hip, stifle, hock, pastern, foot), with checks that every grounded pose reaches the ground row and no airborne one does. Rigging them also gave exact shading for free: project a pixel onto the segment, and its offset over the local radius is the cosine of the angle around the tube. Her legs measured two values across before. They carry four now.

The seam-softening pass then had to be told to leave the new work alone. It fires on long straight one-step edges, which used to signal two stamped parts meeting, and stopped doing so the moment legs arrived with a real cross-section, because a cylinder's band edge is exactly a long, straight, one-step edge. It stippled the clean bands back into noise. It now fires only on two-step jumps.

The bloodhound turned out to be flat too. His rim falloff was a constant 7 pixels, so everything deeper landed on one anchor colour: 43 consecutive identical pixels down his ribcage, and a saddle that was 71.2% one colour. The dog everyone pointed at as the good one was, in its middle third, as flat as the two being complained about. The falloff is now a fraction of each stroke's own radius.

Yarn is not a hairball

Feedback: all the yarn should be different colours, but not purple, which belongs to hairballs. The HUD yarn icon looked like a hairball on the hairball meter. That was exact. Both icons were drawn entirely from the same purple ramp by the same function, one at 14 pixels and one at 10, a few hundred pixels apart on the same HUD, one counting what you have collected and one counting what you can fire.

The wool is now blue, green or orange, chosen per placement from the ball's own position so it is stable across rebuilds. It avoids gold (the paw token) and pink (the heart and the secret). The HUD icon changed twice over, deliberately. At 18 pixels in peripheral vision, hue is the first thing to go, so what separates them reliably is silhouette: the icon now has a loose end hanging off it. A ball of wool has one. A hairball is a closed lump. Three pixels, and it survives a glance.

The first attempt at varying the colour was dead code. An animated pickup dresses itself from a clip baked into its prefab, so the sprite table never ran. The colour has to be repointed on the instance, on both the animator's clip and the renderer's resting frame, which must agree, or the ball is one colour in the editor and another the moment it starts spinning.

Corrections

Two things previously reported were not true, and both had been written from screenshots rather than from the scene.

  • A backdrop in the second world's border stage was reported as mis-tiled, with vertical seams. It is not. The backgrounds tile cleanly. Those bands are the stage's ten patches of long grass, alternating sun and shade, which is the mechanic working.
  • An object over the second boss's stomp box was reported as sitting on the wrong layer. It is a placeholder standing beside him on the ground, so a ceiling probe finding nothing there is correct.

Beating the second dog

Both previous updates recorded the same gap: every state in his fight confirmed except the stomp, five attempts, nothing landed. It is now three stomps in a row, with the window open every time. It was the test setup, not the fight. The window was shutting between two separate commands, each a round trip, and the drop onto him was blind to the posts in his yard and only looked straight up. The fight was never too fast. The test was slower than it.

His yard needed a route through it too. Everything elevated stopped dead at x 196, exactly where the fight starts, with open sky over the dog himself, so the only way onto him was to walk off a roof and land on his back, which is not a stomp. It now has the same shape as Bruiser's room: four heights spread across the width with a way back up from wherever you land, all outside his roaming limits so the dog gets clear floor and the player gets the scenery.

A recompile kills things quietly

Entering the first room after a rebuild threw 512 null errors and climbing, one per physics step per platform. The moving platforms built their setup once on creation, and a script reload does not run creation again, so after a recompile during play every platform in the level was dead. The camera had the same fault. It kept a target in the inspector and none where it actually reads from, and stopped following, in silence, permanently.