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.
- 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.
- 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.
- 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.
- 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.
- 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).
| case | fps, first run | second | animations in the picture | makers' bubbles in the air |
|---|---|---|---|---|
| room alone, default friend | 22.4 | 45.5 | 139 | 0 |
| room, his fairy | 18.5 | 19.4 | 139 | 0 |
| room, his fairy, 1 maker (as live) | 15.8 | 31.5 | 140 | 17 |
| ...the maker's bubbles hidden | 18 | 18 | 140 | 16 |
| ...the room held still | 52 | 113 | 0 | 19 |
| ...wings not fluttering | 18 | 34.7 | 140 | 19 |
| ...no fairy (default friend) | 20.9 | 43 | 140 | 22 |
| ...budget lever | 16 | 31.4 | 140 | 18 |
| ...overlay lever | 17.7 | 18.2 | 140 | 17 |
| ...both levers | 18 | 37.2 | 140 | 20 |
| empty room, his fairy, 1 maker | 60 | 120.3 | 1 | 23 |
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]
| case | fps, first run | second | animations in the picture | makers' bubbles in the air |
|---|---|---|---|---|
| 0.0.306, room alone, default friend | 60 | 120.3 | 0 | 0 |
| 0.0.306, his fairy, 1 maker (live) | 59.7 | 120.3 | 1 | 2 |
| ...overlay | 59.7 | 120.3 | 1 | 2 |
| ...budget | 59.3 | 60 | 1 | 2 |
| ...both | 59.7 | 120.3 | 1 | 2 |
| 0.0.306, his fairy, 4 makers | 49 | 73.8 | 4 | 58 |
| ...both levers | 52.3 | 80.5 | 4 | 42 |
| 0.0.306, his fairy, 16 makers | 39.9 | 80.1 | 16 | 69 |
| ...both levers | 41.2 | 75.2 | 16 | 45 |
| as 0.0.305: foam moving, fairy, 1 maker | 18 | 36.5 | 140 | 2 |
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.
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)
| makers | walk, fps | drag, fps | ||||||
|---|---|---|---|---|---|---|---|---|
| none | budget | overlay | both | none | budget | overlay | both | |
| 0 | 57.3 | 57.7 | ||||||
| 1 | 29.8 | 26.2 | 31.5 | 22.7 | 45.7 | 47.7 | 54.7 | 56.3 |
| 4 | 5.3 | 27 | 45.4 | 50.1 | 36.9 | 33.5 | 44.7 | 44.3 |
| 16 | 37.3 | 33.4 | 41.3 | 52.7 | 24.2 | 24.7 | 29.5 | 42.8 |
| 64 | 23 | 31.5 | 32.3 | 48.5 | 17 | 17.3 | 23.8 | 24.2 |
| 256 | 11.8 | 13.2 | 12.9 | 14.9 | 8.4 | 9.4 | 11.3 | 12.3 |
Full speed (CPU 1), the foam moving
| makers | walk, fps | drag, fps | ||||||
|---|---|---|---|---|---|---|---|---|
| none | budget | overlay | both | none | budget | overlay | both | |
| 0 | 33.5 | 27.5 | ||||||
| 1 | 22.8 | 22.5 | 23.1 | 23.1 | 23.6 | 22.2 | 23.5 | 23.3 |
| 4 | 20.8 | 20.9 | 21.1 | 21.7 | 21.2 | 21 | 21.6 | 20.8 |
| 16 | 19.2 | 19.1 | 19.2 | 19.5 | 19.9 | 18.6 | 19.7 | 19.8 |
| 64 | 16 | 16.1 | 16.5 | 16 | 15.2 | 15.4 | 16.4 | 16.2 |
| 256 | 9.8 | 9.5 | 9.6 | 9.5 | 9 | 8.2 | 9.5 | 9.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.
| page | makers | levers | foam | fps | script | style and layout | paint | raster | GC |
|---|---|---|---|---|---|---|---|---|---|
| CPU 1 | 0 | none | moving | 23.3 | 80 | 36 | 40 | 1950 | 2 |
| CPU 1 | 16 | none | moving | 19.7 | 134 | 65 | 66 | 1903 | 24 |
| CPU 1 | 16 | both | moving | 19.3 | 105 | 41 | 58 | 1887 | 26 |
| CPU 1 | 256 | none | moving | 9.7 | 196 | 51 | 103 | 1995 | 15 |
| CPU 1 | 256 | both | moving | 9.3 | 141 | 40 | 93 | 1923 | 10 |
| CPU 4 | 0 | none | still | 60 | 257 | 96 | 117 | 716 | 11 |
| CPU 4 | 16 | none | still | 28 | 586 | 240 | 187 | 1301 | 95 |
| CPU 4 | 16 | both | still | 43.3 | 483 | 150 | 208 | 906 | 68 |
| CPU 4 | 256 | none | still | 8.3 | 629 | 98 | 240 | 1738 | 34 |
| CPU 4 | 256 | both | still | 4.7 | 571 | 147 | 148 | 710 | 75 |
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]
| case | 0.0.303: ms a drawing, 0 → 256 makers | 0.0.239 | ms a maker, 0.0.303 | 0.0.239 | ratio |
|---|---|---|---|---|---|
| CPU 1, walk | 1.7 → 8.5 | 1.4 → 4.6 | 0.026 | 0.013 | 2.1× |
| CPU 1, drag | 1.6 → 10.3 | 1.4 → 3.0 | 0.034 | 0.006 | 5.4× |
| CPU 4, walk | 8.3 → 32.4 | 6.2 → 13.9 | 0.094 | 0.030 | 3.1× |
| CPU 4, drag | 7.1 → 40.9 | 6.1 → 14.3 | 0.132 | 0.032 | 4.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
- The app's browser pane keeps no picture clock. With no frames drawn, the SVG's clock stands still while the page's runs, so the scene's music-timed begin times drift and the whole scene is built again four times a second there. Not a game bug, but it showed what such a rebuild costs with many things: 42 ms at 256 makers, measuring every placed thing's solids through the screen.
- The guard blamed the maker. The moving-things guard marks the things put in a place that move the most; it could not see that the room's own foam moved more. It names the foam first now, and measures again when the foam is stilled.
- The first walk on the slow tier. In the first measurement the slow tier's first walk ran at 5 to 15 frames a second, the second at 30 to 57: the game holds the scenery still only after it has seen a few slow frames, and the walk before that stutters.
What next
- 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. 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. 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. 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. 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.
- 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
- measure-340b (run 39204930956): the two levers each way, 0 to 256 makers, Chrome, with traces
- measure-340c, first round (run 39205805155): Firefox, his screen, each suspect taken away
- measure-340c, second round (run 39206388768): Firefox on 0.0.306, the levers, 1, 4 and 16 makers
- 0.0.306: the foam still at the start
- 0.0.307: the makers' budget and their own picture