Dress Up reports · ^du_report(12) · 2026-10-18 · about Messed Up Dress Up, a game made with M and N · after ^du_report(11)

The foam, not the makers

Why is the bubble room slow with one bubble maker, even on a modern laptop, and how far can a kid go pressing "+ 🫧 bubble maker" before it breaks? What lets it scale? Measured on GitHub's computers in Chrome and in Firefox (no test browser on the laptop), four runs; fixed in 0.0.306 and 0.0.307.

Short answers.
  1. It was the foam, not the maker. In Firefox at Matthew's screen size, the bubble room with his fairy and one maker ran at 16 to 18 frames a second. Hiding the maker's bubbles changed nothing (18); without the fairy, 21. Held still, the same room with the same maker and fairy ran at 52, and the empty room at 60. The room's own foam moved: 139 animations all over the picture, so the browser painted the whole picture again every frame.
  2. Fixed in 0.0.306: the foam starts still. Matthew's rule for costly effects (off at the start, its cost shown when on). A save still at the old default is made still once; an amount chosen is kept; the slider says what moving costs; and when the room is too busy for more things, the game now says the foam costs the most. His case in Firefox: 18 frames a second before, 60 after.
  3. Many makers: better in 0.0.307, and a kid is stopped before it breaks. All makers now share 12 bubbles a second, and their bubbles float in a picture of their own over the scene. On the slow tier with the room still, 16 makers walked at 53 frames a second instead of 37, and dragged at 43 instead of 24; 64 makers walked at 48 instead of 23. 256 makers stay unplayable (12 to 15). Long before that the game's guard refuses more moving things, once a second of frames measures under 48.
  4. Each maker costs about 4 times the drawing it did in 0.0.239. On the slow tier, dragging: 0.13 ms of drawing a maker today against 0.033 then (256 makers: 41 ms a drawing against 14). Since 0.0.239, placed things are solid, measured through the screen whenever the scene is built again (42 ms at 256 makers), and the things' layer is drawn again every frame. That is the next lever.
  5. With scenery moving, nothing in the game's script helps. Chrome's trace of the room standing with its foam moving: about 1.9 s of rasterizing (painting pixels, on its painting threads) every second, against 80 ms of the game's script. Moving foam stays off, unless it is drawn in a way the browser need not repaint (layers of its own).

What Matthew saw: Firefox, one maker [3]

A fresh headless Firefox for each case, at his screen's size (1250 by 830 at 1.6 device pixels a CSS pixel: 2000 by 1330, his 160%), in the bubble room as 0.0.305 drew it, the friend a fairy with mask and snorkel as he had them, one maker; then each suspect taken away. Frames counted standing for 3 seconds, twice; the first run is the one to read (a second over 60 is headless Firefox not held to the screen's pace).

casefps, first runsecondanimations in the picturemakers' bubbles in the air
room alone, default friend22.445.51390
room, his fairy18.519.41390
room, his fairy, 1 maker (as live)15.831.514017
...the maker's bubbles hidden181814016
...the room held still52113019
...wings not fluttering1834.714019
...no fairy (default friend)20.94314022
...budget lever1631.414018
...overlay lever17.718.214017
...both levers1837.214020
empty room, his fairy, 1 maker60120.3123

The two levers named here are 0.0.307's, tried before it: the makers' shared budget, and their bubbles in a picture of their own.

After the fix: 0.0.306 in Firefox [4]

casefps, first runsecondanimations in the picturemakers' bubbles in the air
0.0.306, room alone, default friend60120.300
0.0.306, his fairy, 1 maker (live)59.7120.312
...overlay59.7120.312
...budget59.36012
...both59.7120.312
0.0.306, his fairy, 4 makers4973.8458
...both levers52.380.5442
0.0.306, his fairy, 16 makers39.980.11669
...both levers41.275.21645
as 0.0.305: foam moving, fairy, 1 maker1836.51402

His case, the fairy and one maker: 18 frames a second on 0.0.305's moving foam, 60 on 0.0.306's still foam. More makers still cost: 4 makers 49, 16 makers 40 in Firefox, the levers adding a few frames at 4 and none at 16 there.

Many makers, and the two levers [2]

The frame-rate gate's walk and drag (two runs each, the better one shown) with 0 to 256 makers along the floor, in Chrome, each lever off and on. At full speed the scenery was kept moving (as on a computer never found slow); on the slow tier (the page 4 times slower) the game held the room still, as it does there.

0153045601 makers, none: 29.8 fps301 makers, both: 22.7 fps2314 makers, none: 5.3 fps54 makers, both: 50.1 fps50416 makers, none: 37.3 fps3716 makers, both: 52.7 fps531664 makers, none: 23 fps2364 makers, both: 48.5 fps4864256 makers, none: 11.8 fps12256 makers, both: 14.9 fps15256bubble makers (the slow tier, the room held still, walking)

no lever (0.0.305) both levers (0.0.307). At 1 and 4 makers the runs disagree with each other by as much as the levers do (one maker: 30 without them, 23 with); read 16 and 64.

The slow tier (CPU 4)

makerswalk, fpsdrag, fps
nonebudgetoverlaybothnonebudgetoverlayboth
057.357.7
129.826.231.522.745.747.754.756.3
45.32745.450.136.933.544.744.3
1637.333.441.352.724.224.729.542.8
642331.532.348.51717.323.824.2
25611.813.212.914.98.49.411.312.3

Full speed (CPU 1), the foam moving

makerswalk, fpsdrag, fps
nonebudgetoverlaybothnonebudgetoverlayboth
033.527.5
122.822.523.123.123.622.223.523.3
420.820.921.121.721.22121.620.8
1619.219.119.219.519.918.619.719.8
641616.116.51615.215.416.416.2
2569.89.59.69.598.29.59.3

Each number is the better of two runs on one page; the slow tier's first walks were often far slower than its second (5 to 15 against 23 to 57), so differences of a few frames there are within the noise. The large ones (16 and 64 makers, both levers) held in walk and drag alike.

Where a frame's time goes

Chrome's own trace of the room standing for 3 seconds, the bubbles blowing: milliseconds of each kind of work in a second. "Raster" is the painting threads turning the picture into pixels; the rest is the page's main thread.

pagemakersleversfoamfpsscriptstyle and layoutpaintrasterGC
CPU 10nonemoving23.380364019502
CPU 116nonemoving19.71346566190324
CPU 116bothmoving19.31054158188726
CPU 1256nonemoving9.719651103199515
CPU 1256bothmoving9.31414093192310
CPU 40nonestill602579611771611
CPU 416nonestill28586240187130195
CPU 416bothstill43.348315020890668
CPU 4256nonestill8.362998240173834
CPU 4256bothstill4.757114714871075

With the foam moving, rasterizing is nearly two seconds of work every second, whatever the makers: the whole picture painted again each frame. Held still, it falls to 0.7 s with no makers, and the makers' bubbles in their own picture cut it again (16 makers: 1.3 s to 0.9 s).

Each maker, against 0.0.239 [1]

case0.0.303: ms a drawing, 0 → 256 makers0.0.239ms a maker, 0.0.3030.0.239ratio
CPU 1, walk1.7 → 8.51.4 → 4.60.0260.0132.1×
CPU 1, drag1.6 → 10.31.4 → 3.00.0340.0065.4×
CPU 4, walk8.3 → 32.46.2 → 13.90.0940.0303.1×
CPU 4, drag7.1 → 40.96.1 → 14.30.1320.0324.1×

Drawing time grows with the number of things in a place, and since 0.0.239 it grows about 4 times as fast a maker: placed things became solid (ticket 283), their boxes measured through the screen whenever the scene is built again, and the things' layer is drawn again every frame. Not what made Matthew's room slow, but what limits many things in any place.

What a kid's endless taps meet

The moving-things guard (tickets 201 and 203): after each moving thing is put in a place, the game counts a second of frames; under 48, every "+" is greyed and the things that move the most are marked, until one is taken away (or, now, the bubble room's foam is stilled). So a kid pressing "+ 🫧 bubble maker" is stopped at a few makers on a slow computer, at more on a fast one, never at a frozen page. Matthew's screenshot was that guard at work, blaming the maker for the foam.

Found on the way

What next

  1. 1. The things' layer drawn once: draw the place's things again only when they change, not every frame (today 256 things cost a part() call each per drawing).
  2. 2. Placed things' solids kept per thing: measure a placed thing's boxes once and keep them by its kind, place and size, so a scene built again does not measure every maker through the screen (standingHere: 42 ms at 256 makers, on every tap that adds one).
  3. 3. The first walk on a slow computer: the slow tier's first walk in the room ran at 5 to 12 frames a second before the game held the scenery still; find out sooner (from the first frames) that a computer is slow.
  4. 4. Moving foam that costs little: if the moving foam is wanted back on by default: its bands as layers of their own the browser moves without repainting, not 139 animations inside the picture.
  5. 5. The bubble room in the frame-rate gate: the room with 4 makers, at full speed and on the slow tier, in Chrome and Firefox, each release (makers340 checks the facts already: the foam still, the overlay lined up, the budget).

How this was put together

Written on 2026-10-18 by Claude, for Matthew, from ticket 340: Matthew: "bubble room with one bubble machine is stressing my modern laptop"; "let's do a long-run analysis about performance, and write a new du_report where we add bubble machines in bubble rooms, and see what we can do to support scaling better"; "scaling to the extreme, the way a kid would use the system (pressing +bubble machine until it breaks)". Then, at 23:39, with a screenshot of the live game in Firefox, one maker, "About 19 frames a second here": "debug --- but without crashing my machine." Every measurement ran on GitHub's computers, from branches made for it and deleted after (measure-340, measure-340b, measure-340c), never merged; nothing ran in a test browser on the laptop. Chrome's frame-rate gate is tools/fpsgate.mjs; the traces are Chrome's own (Tracing, devtools.timeline); Firefox's cases were driven by WebDriver (geckodriver). The data are in data/; the page is built by drivers/build.py.

  1. measure-340 (run 39202069060): the gate's walk and drag, 0 to 256 makers, 0.0.303 against 0.0.239, Chrome, CPU 1 and 4
  2. measure-340b (run 39204930956): the two levers each way, 0 to 256 makers, Chrome, with traces
  3. measure-340c, first round (run 39205805155): Firefox, his screen, each suspect taken away
  4. measure-340c, second round (run 39206388768): Firefox on 0.0.306, the levers, 1, 4 and 16 makers
  5. 0.0.306: the foam still at the start
  6. 0.0.307: the makers' budget and their own picture