OctoMonic
Character animation v6 · blind bake-off against the reference
Dog Camp loader on the left. Six OctoMonic states on the right. Everything below is measured, not asserted.

The method landed. The staging did not.

Four defects were named and repaired. Three of them measurably improved. The fourth, the one about presentation, was fixed on phones and left broken on desktop, and the contrast table that came with the repair quietly omitted every pair that still fails. Average score across the six states moved from 4.0 to 5.8 out of 10.

31/37adjacent pairs now clear the 3:1 floor. Old worst was 1.14:1.
6pairs still under 3:1, none of them listed in the repair report.
0.40 → 0.92rendered scene opacity, verified in the DOM.
4.58disc-widths apart at 1280. The reference is 1.36. It was 5.00.
320pxnarrowest width where the scene is now visible. It used to vanish under 1024.
1 of 6states still has no rest frame in its entire cycle: idle.

The reference Tom likes

Dog Camp loader

34 primitives, 5 animated parts, 16 inks, one ground line, and a treat that physically travels from one character to the other. The two figures stand 1.36 body-widths apart, close enough that the throw and the catch are one event. Peak motion is 5.70x its own average, and 16% of the loop is completely still. That stillness is what makes the leap read as a leap.

The six repaired states

idle

7.2s loop · still a wheel

2 4 /10

Blinks now, the mouth squashes, and the reach beat exists in the CSS. But the numbers say it is still a wheel: peak motion is 1.73x its own average against the dog's 5.70x, and not one frame in 7.2 seconds is at rest. Two constant bobs at 5.2s and 6.1s run straight over the top of the reach and wash it out. This is the state every visitor sees first and longest.

filling

4.8s loop · real beat

4 6 /10

The closest thing to the dog's rhythm in the set: a real two-hump beat, 35% of the cycle genuinely still, the pen and the pad appear, four writing strokes, and her mouth presses flat then releases. Held back by scale, 28.6 motion against the dog's 100, and by the fact that her cream glove and her cream notepad are the same ink and overlap each other.

choosing

3.6s loop · weak beat

4 5 /10

The eyes dart left, then right, then blink. It starts and ends near rest, which the old metronome did not. But it is the weakest beat of the six (2.41x) and the quietest (26.6 motion), and at a 101px disc the double foot tap is a twitch. Nothing in the drawing says times or choosing.

working

3.2s loop · real beat

5 7 /10

The best-told story of the four in-form states. Raise, stir, stir, taste, with two clear motion bursts and both mouths opening. A prop gives the action a subject, which is exactly the job the dog's treat does. Lost marks: the pot reads as a blue bucket floating with no support, and the steam drifts up and to the right, half off the disc, where it reads as bubbles.

confirmed

3.6s loop · split verdict

6 6 /10

Two different animations wearing one name. On a phone the discs close to a 0.4-disc overlap, the tentacle meets her glove, the squash lands: 7/10, the best thing in the set. On desktop they stop 528px apart and shake hands with half a screen between them, which is 4/10, because contact is the whole point of a handshake. It is also 8.5x the dog's motion energy, looping forever behind a confirmation the customer is trying to read.

error

3.0s loop · real beat

3 7 /10

The biggest gain. Flinch, shrug, hold, sag. Burst 3.89x is the closest any state gets to the dog, 29% of the cycle is at rest, and it contains the only real hold in the set (750ms of complete stillness). The frown is unmistakable on both figures at both sizes. This is the one state where a stranger could tell you what is happening.

Each card stages the pair the way the real page stages them below 1280, which is what most traffic sees. The 1280 staging cannot be reproduced inside a 300px card, so it is shown as photographic evidence below.

Fix 1

Hue separation: verified independently, and it mostly landed

These numbers are not a re-run of the repair's solver. Every colour below was read off a rendered screenshot: probe points in each shape's own SVG coordinates, mapped through getScreenCTM(), sampled as the median of a 3x3 block at 3x device pixel ratio. Whatever the browser actually painted, including the 0.92 group opacity and the yellow plate behind, is in the number. The right-hand column is what the repair report claimed for the same pair.

Adjacent pair (shapes that actually touch)Measured Report claimedRendered colours
OCTOPUS tentacle : tentacle 1.00not listed #04060B / #04060B
OCTOPUS tentacle : tentacle (r) 1.00not listed #04060B / #04060B
DETECTIVE hat brim : hat crown 1.00not listed #1E0E24 / #1E0E24
OCTOPUS pen : disc 1.18not listed #E2D2AF / #DCC427
DETECTIVE notepad : disc 1.18not listed #E2D2AF / #DCC427
DETECTIVE glove : disc 1.18not listed #E2D2AF / #DCC427
DETECTIVE hat brim : hair 3.173.19 #1E0E24 / #A9472A
DETECTIVE coat : trousers 3.253.29 #0B101E / #526493
OCTOPUS mantle : tentacle 3.273.30 #89459B / #04060B
DETECTIVE coat : sleeve 3.273.31 #0B101E / #266F77
DETECTIVE coat : collar 3.273.31 #0B101E / #266F77
DETECTIVE coat : hair 3.273.30 #0B101E / #A9472A
DETECTIVE sleeve : disc 3.30not listed #266F77 / #DCC427
OCTOPUS pot : tentacle 3.323.35 #2E678C / #04060B
OCTOPUS pot : pot rim 3.323.35 #2E678C / #04060B
DETECTIVE trousers : disc 3.323.30 #526493 / #DCC427
DETECTIVE skin : hair 3.33not listed #E1BE9D / #A9472A
DETECTIVE skin : collar 3.33not listed #E1BE9D / #266F77
OCTOPUS pot : disc 3.483.47 #2E678C / #DCC427
OCTOPUS mantle : disc 3.533.52 #89459B / #DCC427
DETECTIVE sleeve : glove 3.883.85 #266F77 / #E2D2AF
DETECTIVE coat : belt 3.953.96 #0B101E / #B6571C
OCTOPUS mantle : jaw patch 4.154.13 #89459B / #E2D2AF
OCTOPUS mantle : eye white 5.155.12 #89459B / #EAEAEA
DETECTIVE hat crown : hat band 10.4710.50 #1E0E24 / #DCC427
DETECTIVE hat : disc 10.47not listed #1E0E24 / #DCC427
DETECTIVE hat brim : skin 10.5410.63 #1E0E24 / #E1BE9D
DETECTIVE coat : disc 10.8110.86 #0B101E / #DCC427
OCTOPUS tentacle : disc 11.5511.62 #04060B / #DCC427
DETECTIVE skin : eye 11.63not listed #E1BE9D / #04060B
DETECTIVE skin : mouth 11.63not listed #E1BE9D / #04060B
DETECTIVE coat : glove 12.7112.75 #0B101E / #E2D2AF
DETECTIVE coat : notepad 12.7112.75 #0B101E / #E2D2AF
OCTOPUS jaw patch : mouth 13.5813.65 #E2D2AF / #04060B
OCTOPUS jaw patch : open mouth 13.58not listed #E2D2AF / #04060B
OCTOPUS eye white : pupil 16.8516.89 #EAEAEA / #04060B
DETECTIVE eye : catchlight 16.85not listed #04060B / #EAEAEA

Fix 2

The face: this one is clean

  • Both figures have two mouth elements, a curve and an open "oh". The detective had none at all before.
  • .om-m is animated in all six states: om-think, om-grin, om-press, om-frown, om-mouthhide. .om-mo cross-fades in on working. The old stylesheet touched the mouth in zero states.
  • Every state blinks. idle uses a double blink, filling / working / confirmed a single, error a slow one, and choosing's blink is folded into the eye-dart keyframe rather than a separate rule.
  • Dead class names: .om-smile, .om-oh and .om-doh exist in the JSX and are referenced by no CSS rule at all. Harmless, but the critic's original finding was phrased around .om-smile, and it is worth knowing the answer was to route around it rather than use it.

Fix 3

Presentation: fixed on phones, still broken on desktop

ViewportStateDisc Centre gapDisc-widthsEdge gap LayoutOpacity
1920idle300px1080px3.60780pxblock / absolute0.92
1440idle245px1025px4.19780pxblock / absolute0.92
1280idle218px998px4.58780pxblock / absolute0.92
1280confirmed218px746px3.43528pxblock / absolute1
1024idle118px142px1.2024pxblock / relative0.92
768idle118px142px1.2024pxblock / relative0.92
375idle101px122px1.2020pxblock / relative0.92
375confirmed86px52px0.60-34pxblock / absolute1
320idle92px110px1.2018pxblock / relative0.92
The booking page at 1280px
/book at 1280. The two figures sit in opposite corners, 780px of edge gap with an entire column of copy between them. The dashed floor that is supposed to join them is masked out across the middle. This is the "two corners, not a scene" failure the CSS comment claims to have avoided.
The booking page at 375px in Hebrew
/book at 375, RTL. The real win. The band is in flow at the top of the column, both figures 20px apart on one floor, both facing in, at full size. This used to be display:none. RTL mirrors losslessly.
The confirmed handshake at 1280, five frames
The handshake at 1280, five frames through the contact beat. They raise their arms and squash on impact 528px apart. There is no impact. On a phone the same animation overlaps the discs by 40% and works.
The WhatsApp button on top of the detective's disc
New defect, not in the original four. At 1280 the WhatsApp floating button paints on top of the detective's disc, green on brand yellow. Nothing is blocked, the scene is pointer-events:none, but it looks like a mistake.

Fix 4

Beat or wheel: four of six now have a shape

One full cycle per state, 32 frames, animations paused and the clock seeked so every frame is reproducible. Cropped tight to the characters. Motion is mean frame-to-frame change with the dog set to 100. Burst is peak over mean, which is what separates a beat from a wheel. Rest is the share of the cycle that is effectively still, and hold is the longest unbroken run of stillness, in frames of 1/32nd of a cycle.

StateMotionBurst RestHoldMotion through the cycle
DOG (reference)1005.70x16%3
idle481.73x0%0
filling292.99x35%3
choosing272.41x16%3
working433.15x10%2
confirmed8523.88x10%2
error663.89x29%8

Build and page health

Nothing is broken

  • npm run build passes. Characters chunk 4.71 kB, 1.39 kB gzipped. The character CSS is 15.4 kB with comments stripped, 3.5 kB gzipped.
  • The scene mounts on the built /book at 1280 and 375, in both English and Hebrew. Zero console errors in all four combinations.
  • The state machine is live: typing into the first field moves the scene from idle to filling on the real page.
  • prefers-reduced-motion leaves zero running animations, keeps the figures visible and posed, mouth closed on a smile. Never hidden.
  • Lighthouse accessibility 100 on /book, zero failing audits.
  • No horizontal scroll and no overlap with form controls at 375 or 1280.
Ship it, with one more short pass

Is this good enough to put in front of a paying customer?

On a phone, yes. The mobile band is genuinely good: two characters 20px apart on one floor, readable at 101px, reacting to what the visitor is doing, with a payoff on confirmation that actually makes contact. That is the majority of traffic and it is the first thing in five attempts that would not embarrass anyone.

On desktop, no, and it is not close. Two figures 780px apart in opposite corners, performing a two-hander where one offers a pen to nobody and the other shakes hands with half a screen of air, with the WhatsApp button parked on her disc. A customer would not read that as a scene. They would read it as two stickers.

Do not drop it. The remaining work is small and mostly deletion. The method is right now: simple primitives, five or six moving parts, squash in the keyframes, arcs on offset-path, eyes and a mouth that move. What is missing is staging, and staging is cheap:

1. Delete the 1280 branch. Use the band at every width. Two figures 1.2 disc-widths apart is the reference; 4.58 is not. This is a deletion, not a build.
2. Give idle a rest. Cut the two continuous bobs or gate them to part of the cycle, so there is a second and a half where nothing moves. Stillness is what makes the dog's leap land.
3. Move the scene out from under the WhatsApp button.
4. Decide the palette question deliberately. Eight of twelve inks are off-brand. Either accept that these are flat-illustration mascots rather than the emblem, or go back and buy separation with drawn contours and value inside black and yellow, which is what noir cartoons do.

Three earlier QA passes waved animations through that Tom then rejected. For the record: I would put the phone version in front of a paying customer today. I would not put the 1280 version in front of one, and I would expect Tom to reject it for the same reason he rejected attempt one, which is that the figures are not in a scene together.