Dress Up reports ยท ^du_report(1) ยท 2026-10-16 ยท about Messed Up Dress Up, a game made with M and N

60 frames a second, and what checks it

Does Messed Up Dress Up hold 60 frames a second for what a game controller does in a place? How much of that do our tests check, and where do they run? From Matthew's rule, made on 2026-10-16 after the castle and ๐Ÿ’ญ our imagination dropped frames: "The priority is 60 FPS at all times, for all actions that a game controller does in the scene."

Short answers. Measured with the frame-rate gate (below) at 1920ร—1080 on Matthew's laptop, in headless Chrome; each figure the median of three runs.

  1. Where we measure, yes: every case holds 59.3 to 60 frames a second on 0.0.78 (fce3163): walking and dragging the friend in seven places. On 0.0.75, the same screen, 4 of the 10 cases in the first five places fell below 55, the lowest the castle drag at 45.8; and ๐Ÿ’ญ our imagination managed 18 on 0.0.77 (results, what it took).
  2. What we measure is narrow: 2 of the 8 kinds of controller action, in 7 of the 24 places (the castle's rooms and the tree house's count as one each). Running, crouching and crawling, the jumps, climbing, going through doors and into pictures, and the on-screen controller are not measured yet (coverage).
  3. None of it runs in CI. The repository has no workflows; GitHub only deploys the site. The gate runs by hand on Matthew's laptop before each release. A frame rate on a shared runner would mean little, but a count of what costs, per place, would not depend on the machine, and could fail a build (CI).
  4. What costs is kept off by default. Two things a background can draw, Gaussian blurs and the imagination's 22 pictures drawn live, are now settings in the game's โš™๏ธ Advanced tab, off until they can be made fast; on, walking in the imagination drops from 60 to 33 frames a second (settings). The slow computer the kids use, the black laptop, is not measured yet (the slow computer).
14 of 14
gate cases at 55 frames a second or more on 0.0.78 (lowest 59.3)
18 โ†’ 59.7
frames a second walking in ๐Ÿ’ญ our imagination, 0.0.77 โ†’ 0.0.78
2 of 8
kinds of controller action measured, in 7 of 24 places
0
tests that run in CI
Frames a second for each scene and action: before the fixes and on 0.0.78 0 15 30 45 60 55: the gate ๐Ÿฐ outside the castle, walk 48.7 (0.0.75) 59.3 ๐Ÿฐ outside the castle, drag 45.8 (0.0.75) 60 ๐Ÿง™ outside the witch's house, walk 60 (0.0.75) 60 ๐Ÿง™ outside the witch's house, drag 55.6 (0.0.75) 59.3 ๐Ÿง™ inside the witch's hut, walk 60 (0.0.75) 60 ๐Ÿง™ inside the witch's hut, drag 60 (0.0.75) 60 ๐ŸŒฟ inside the healer's house, walk 60 (0.0.75) 60 ๐ŸŒฟ inside the healer's house, drag 60 (0.0.75) 60 ๐Ÿฆ† swimming in the duck pond, walk 51.7 (0.0.75) 60 ๐Ÿฆ† swimming in the duck pond, drag 54.7 (0.0.75) 60 ๐Ÿ”ฒ the empty room, walk 60 (0.0.77) 60 ๐Ÿ”ฒ the empty room, drag 60 (0.0.77) 60 ๐Ÿ’ญ our imagination, walk 18 (0.0.77) 59.7 ๐Ÿ’ญ our imagination, drag 17.4 (0.0.77) 59.7
Figure 1. Frames a second for each place and action: the thin pale bar before the fixes (0.0.75 for the first five places, 0.0.77 for the empty room and the imagination, which joined the gate later), the thick bar on 0.0.78. The dashed line is the gate's 55. The empty room was new in 0.0.77, already at 60.

Where it stands

The gate on 0.0.78 (fce3163), at 1920ร—1080. The figure is the median of three runs; the runs, the 95th-percentile gap between frames (16.7 ms is a frame at 60), and the game's own drawing time are beside it. The game's drawing takes 1 to 3 ms in every case: when frames are lost, the browser's painting is the cost, not the game's code.

PlaceActionFrames a secondRuns95th-percentile gap (ms)A drawing (ms)
๐Ÿฐ outside the castlewalk59.359.3, 57.7, 59.716.8 / 16.8 / 16.81.51 / 1.42 / 1.5
๐Ÿฐ outside the castledrag6060, 60, 6016.7 / 16.7 / 16.81.63 / 1.57 / 1.7
๐Ÿง™ outside the witch's housewalk6060, 60, 6016.8 / 16.7 / 16.81.1 / 1 / 1.03
๐Ÿง™ outside the witch's housedrag59.359.3, 59.3, 6016.8 / 16.7 / 16.81.51 / 1.36 / 1.2
๐Ÿง™ inside the witch's hutwalk6060, 60, 6016.7 / 16.7 / 16.71.25 / 1.23 / 1.15
๐Ÿง™ inside the witch's hutdrag6060, 60, 6016.7 / 16.7 / 16.81.35 / 1.2 / 1.41
๐ŸŒฟ inside the healer's housewalk6060, 60, 6016.8 / 16.7 / 16.71.64 / 1.51 / 1.6
๐ŸŒฟ inside the healer's housedrag6060, 60, 6016.8 / 16.7 / 16.71.61 / 1.68 / 1.75
๐Ÿฆ† swimming in the duck pondwalk6060, 59.7, 6016.7 / 16.7 / 16.71.79 / 1.83 / 1.96
๐Ÿฆ† swimming in the duck ponddrag6060, 60, 6016.8 / 16.7 / 16.72.14 / 1.99 / 1.93
๐Ÿ”ฒ the empty roomwalk6060, 60, 6016.7 / 16.7 / 16.81.83 / 1.87 / 1.82
๐Ÿ”ฒ the empty roomdrag6060, 60, 6016.7 / 16.7 / 16.81.97 / 2.08 / 1.89
๐Ÿ’ญ our imaginationwalk59.757.7, 59.7, 6016.8 / 16.7 / 16.71.37 / 1.45 / 1.59
๐Ÿ’ญ our imaginationdrag59.759, 59.7, 6016.8 / 16.8 / 16.71.62 / 1.67 / 1.68

How it got here

Each cause was found the same way: freeze or hide one thing at a time and measure again, then trace what was left. Three kinds of thing cost frames, and none of them was the game's code:

PlaceBefore (walk / drag)0.0.78What it tookVersion
๐Ÿฐ outside the castle48.7 / 45.8 (0.0.75)59.3 / 60The friend in the moat was drawn through an SVG mask (the water's), which the browser draws again offscreen on every frame they move: now two clipped copies of the friend. The picture's viewBox was written on every drawing, unchanged, which made the browser paint the whole picture again: now only when it changes. Facing us, the friend's arms were drawn over the hair and face through another mask: now a clip.0.0.76 8d81a39, 0.0.77 5c77b16
๐Ÿฆ† swimming in the duck pond51.7 / 54.7 (0.0.75)60 / 60The water's mask, as in the moat.0.0.76 8d81a39
๐Ÿง™ outside the witch's house60 / 55.6 (0.0.75)60 / 59.3The arms' mask, as in the castle.0.0.77 5c77b16
๐Ÿ’ญ our imagination18 / 17.4 (0.0.77)59.7 / 59.7Its map holds 22 pictures of whole places: 7,728 of its 8,080 shapes, 197 animations and 24 Gaussian blurs. Hiding them gave 59 frames a second; taking out the blurs alone, 25; drawing each picture still, as an image, and blurs off, 60. Both are now settings, off by default.0.0.78 fce3163
๐Ÿง™ inside the witch's hut, ๐ŸŒฟ inside the healer's house60 / 60 (0.0.75)60 / 60Already smooth. In the gate because Matthew named them: the hut's cauldron fumes held it to 20 to 33 frames a second until 0.0.72.

Coverage

What the gate measures today, by controller action and place. The places are the ones that have had trouble: Matthew named the first five, and the empty room and the imagination joined with the imagination's slowness. Following his call to grow coverage slowly, a place joins when it shows trouble, and the next actions to add are in What next.

Action๐Ÿฐ outside the castle๐Ÿง™ outside the witch's house๐Ÿง™ inside the witch's hut๐ŸŒฟ inside the healer's house๐Ÿฆ† swimming in the duck pond๐Ÿ”ฒ the empty room๐Ÿ’ญ our imaginationthe other 17 places
walk (arrows)โœ…โœ…โœ…โœ…โœ… (swims)โœ…โœ…โ€”
drag (mouse)โœ…โœ…โœ…โœ…โœ…โœ…โœ…โ€”
run (held, with an arrow)โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
crouch, crawlโ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
the two jumpsโ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
climbing a ladder by handโ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
through a door, โœจ into a picture, backโ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
the on-screen controller (touch)โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”

Other checks, not about frame rates, also run before a release: the sweep, which draws every place, castle room and costume and compares each drawing's hash with the last release's, so a change can't alter a drawing unnoticed; a dump of the settings and their rules; and checks of single features (the on-screen controller with several fingers at once, climbing and the picture that glows). A change to how the friend is drawn is compared with the last release pixel by pixel.

What runs in CI

Nothing yet. The repository has no workflows: the one thing GitHub runs is its own deployment of the site, on every push. The gate and every other check run by hand on Matthew's laptop before each release, and their tools live outside the repository.

A frame rate measured in CI would not say much: GitHub's runners are shared and have no graphics card, and their speed varies from run to run, so a laptop's 60 is not a runner's. What could run in CI and mean the same everywhere is a count of what costs. Each kind of cost above is a countable thing in the drawing: masks on the friend, blurs, shapes drawn live in pictures, animations of a CSS property. A budget per place, counted at its defaults, would fail a build the moment a new mask or blur arrived on a path the friend moves through, before anyone felt it. Beside it, the drawings' checks can fail a build too, being exact, and the frame-rate gate can run on a runner as information, kept but not failing; the gate that fails stays the laptop's, before release.

The settings kept off until they are fast

In the game's โš™๏ธ Advanced tab since 0.0.78, each off unless switched on, on that computer only. Switched on, each says how many it draws in the place; ๐Ÿ“ measure here walks the friend for 2 seconds with it off and 2 with it on, and says what each managed. Matthew: "Each time we find some feature of the background that's threatening our FPS being 60 at all times, let's make it optional, and off by default, and add to the advanced panel in a similar way."

SettingWhat it drawsWhat it cost, measured
๐ŸŒซ๏ธ soft blursGaussian blurs: the pictures' soft edges, the waterfall's mist, the leaves outside the cave. Off, pictures are sharp and the mist is a soft gradient.In the imagination with its pictures drawn live (0.0.77): walking, 25.5 frames a second without them, 18 with them (one run each).
๐ŸŽž๏ธ moving pictures in the imagination's mapThe map's 22 pictures drawn live, each place's animations playing. Off, each is that place held still, as an image.Walking in the imagination, with ๐Ÿ“ in the game: 60 frames a second off, 33 on (1.2 and 1.4 ms a drawing; one run each, 1400ร—900).

The slow computer

The kids also play on the black laptop, N's old one, where ๐Ÿ’ญ our imagination was too slow to walk in on 0.0.76, while walking with no background was fine. The gate runs on Matthew's much faster laptop and does not stand in for it yet. Chrome can slow a page's main thread (throttling) and can be started with a single thread for painting, which together come closer to an old computer; the gate has a slow mode for that, not yet set to match the black laptop. The surest measure is the black laptop itself: the โš™๏ธ Advanced tab's meter and ๐Ÿ“ run there with nothing to install.

What next

  1. 1. Add running, the two jumps and climbing a ladder by hand to the gate, in the places it already covers (climbing in ๐Ÿ’ญ our imagination, where the ladders are). Running changes more of the picture each frame than walking; a jump lifts the friend through more of it.
  2. 2. Measure on the black laptop itself, with the โš™๏ธ Advanced tab's meter and ๐Ÿ“ measure here in each covered place, then set the gate's slow mode (Chrome's CPU throttling and one raster thread) until it gives the same numbers, so the gate can stand in for it.
  3. 3. Cost budgets that CI can run: for each place at its defaults, count the masks, blurs, SMIL animations of CSS properties, and shapes drawn live, and fail when one goes over its budget (no masks on the friend, no blurs by default). They do not depend on the machine, so they can fail a build.
  4. 4. Move the test tools into the repository and add a workflow: the budgets and the drawings' checks (the sweep, the structure dump) failing the build, the frame-rate gate recorded alongside, not failing.
  5. 5. Cover touch: the on-screen controller's arrows and buttons, held and tapped, in the covered places (a phone's or tablet's screen size).

How this was put together

The gate is fpsgate.mjs (a copy in 001/drivers/), run against the game's own file in headless Chromium 155 started muted, at 1920ร—1080, on Matthew's laptop, with nothing else heavy running. For each place it loads the page once, sets the place from the game's defaults with the music muted, and runs a warm-up walk and drag, then three of each:

Frames are counted with requestAnimationFrame while the friend moves; a run's frames a second is the count over the time; a case's figure is the median of three runs, and it passes at 55 or more. The game's draw() is timed around each call. Causes were found with the gate's TWEAK (code run in the page before the runs, to freeze, hide or remove one thing at a time) and with dragtrace.mjs, which sums a Chrome performance trace of one walk or drag by thread and event, with Chrome's reasons for each invalidation. One run per variant was used to find causes; the figures in this report are the median-of-three gate runs.

The data is here twice: embedded in this page as JSON (<script type="application/json" id="report-data"> at its end: every gate run's raw readings, the versions and commits, and what each measure is), and as the gate's own files in 001/data/. The 0.0.77 run of the empty room and the imagination wrote no file; its numbers are copied from the gate's printed output, and its file says so. The page is made by 001/drivers/make.py from those files.

Builds: 0.0.75 bdc744f, 0.0.76 8d81a39, 0.0.77 5c77b16, 0.0.78 fce3163.