Dress Up reports · ^du_report(13) · 2026-10-19, updated 2026-10-22 · about Hazel

Dress Up in Hazel: the hard part of the drawing is what runs every frame, the music can be computed, and the menus need a layout Hazel lacks

Matthew asked three questions about porting Messed Up Dress Up to Hazel (ticket 402): which parts of the drawing would be hardest for Hazel, whether Hazel can do music, and how to offer the game's menus in Hazel while keeping to Always Be Knolling. He also asked for the game's advanced features, staged as challenges. It builds on ^du_report(8) (the port, piece by piece), ^du_report(5) (the menu rules) and ^report(15) (the game and its livelits, measured side by side). The game at 0.0.334; Hazel on its livelits × modular editors branch (hazelgrove/hazel#2635) at 90ed4505bf. That commit already carries all 14 of the performance PRs on Hazel's dashboard, as merged or in #2635's own form, so every Hazel number here is after them; ^report(25) measures what they do.

Short answers.

  1. The drawing's hard part is not its SVG but what runs around it. A Hazel view can draw every element the game uses (41 SVG element kinds, gradients, clips, masks, filters and animations included). What Hazel lacks is what the game does at run time: motion every frame (a livelit has no clock, and a change costs 0.13 to 1.09 s against a frame's 16.7 ms), measuring the drawn picture (about 100 calls place things on it), its sizes (the greenhouse has 1,906 elements; the largest Hazel picture so far, 570 shapes), and byte-exact numbers: Hazel's to_fixed and the game's f1 disagree on 5 of 7 values tried.
  2. Music: Hazel can compute it today; playing it takes a JS player or a new builtin. 63% of the game's music code is pure (the tunes, chords, arrangements and the score of note events). A Hazel program computed a 1,216-event score, the size of the forest tune's, in about 0.2 s with Hazel's command-line tool, and in about 0.14 s through Hazel's JS library in node, the same before and after the performance PRs. A livelit cannot make a sound; Hazel's MVU apps can, a sine beep (Cmd.PlayTone) with timers, which corrects ^du_report(8)'s "no sound binding".
  3. The menus: Hazel meets about two of the nine knolling rules, and the gap is a layout, then speed. Livelits show up inline, stacked in the UX outline, or in a sidebar column; none is a grid of named boxes. The plan: the settings as a tree of modules, a "knolled" UX layout that draws modules as tabs and their members as boxes, and big livelits for the bespoke designers. Even laid out right, each change costs 0.13 to 1.2 s; the rule "keep it fast" waits on Hazel's speed.
  4. The advanced features fall into six stages, from pictures Hazel can draw today, through small builtins, a clock for livelits, interaction inside the picture and the designers, to the whole game at 60 fps, which waits until a change costs about 16 ms.
The castle's greenhouse with the friend, as the game drew it (0.0.304, from the picture archive): 1,906 elements, the most of any place.
The castle's greenhouse with the friend, as the game drew it (0.0.304, from the picture archive): 1,906 elements, the most of any place.
The witch's woods: 1,565 elements, 1,307 of them shapes, with their animations running.
The witch's woods: 1,565 elements, 1,307 of them shapes, with their animations running.

1. Which parts of the drawing are hardest for Hazel

What the game's drawing uses (counted in js/, 0.0.334) and what it rests on at run time:

What the drawing usesCount
SMIL animations (<animate>, animateTransform, animateMotion)180
opacity attributes430
transforms192
clip paths104
gradients (linear 37, radial 55)92
<use>24
<text>28
patterns7
filters (blur, colour matrix)4
masks2

What the drawing rests on at run time

WhatWhat needs it
Measuring the drawn picture (getBBox, getScreenCTM, getBoundingClientRect, getPointAtLength)about 104 calls: collision boxes, where tree-room pictures hang, ladders, foam layers, a lifted thing's centre
Every frame (requestAnimationFrame 71 lines, performance.now 56)walking, climbing, treading water, bubbles and balls, the leak, blowing
The music's clock (the LFOs)poses' phases, and 43 places that time scenery to the music
The DOM after drawingthe depth sort, the friend put in front of what is behind their feet, reusing unchanged pieces, still pictures on slow computers
Seeded randomnessa sine hash, Math.sin(seed·71.3 + i·13.7)·43758.5453, in 467 calls

The five hardest, ranked

FeatureWhy it is hard for HazelThe most plausible route
1Motion every frameLivelits have no clock, and a change costs 0.13 to 1.09 s, against 16.7 ms a frame.Hazel computes the poses at sampled phases and writes the animation as SMIL or CSS; JS keeps the frame loop and the simulations.
2Measuring the drawn pictureThere is no way to measure layout from Hazel; about 104 calls rely on it.A bounds library in Hazel (boxes from the geometry, with transforms and curves), or a measuring command for views.
3SizeThe imagination is 2.45 MB; the greenhouse 1,906 elements; Hazel's largest picture so far is 570 shapes.Static scenery as one picture (an <img> from a Hazel string), only the friend as structured elements; memos keyed by explicit inputs.
4Work done on the DOMThe depth sort, the friend's place, piece reuse and still pictures happen after drawing.The sort and the friend's place in Hazel (they are pure over name, depth and SVG); stills and bitmaps stay in JS.
5Byte-exact numbersf1 is used 7,269 times and differs from to_fixed (below); the sine hash multiplies any 1-ulp difference by 43,758.An f1-like number-to-text builtin, and a check that sin agrees bit for bit.

Tried in Hazel: the numbers in a picture

xHazel: to_fixed(x, 1)The game: f1(x)
12.012.012
0.250.30.3
-0.25-0.3-0.2
1.451.41.5
0.150.10.2
2.352.42.4
7.07.07

The game's f1 is (Math.round(x · 10) / 10).toString(): it rounds halves up and drops a trailing .0; Hazel's to_fixed(x, 1) keeps the .0 and rounds the binary value. Run with Hazel's command-line tool (013/data/numbers.hz) and the game's f1 in node.

2. Can we do music in Hazel?

Part of the music codeLinesWhat
Pure1,256notation and part builders, the 45 tunes and their arrangements, the score (songOf: a sorted list of events, each with a start, length, notes, part and velocity), timing, the LFOs, the mix's maths
Run time and controls72634 synthesizer instruments in WebAudio, effects and reverb, a scheduler that sends notes 0.5 s ahead on the audio clock, the mixing board, the transport

Hazel's options

OptionPossible?How
The tunes and the score as Hazel data and functionsYes, todaya Hazel program computes the events (the try below)
A view that plays the archive's recordingProbably (untested)a view can draw any element, so an <audio> of the tune's MP3
A sine melody from an MVU appYes, crudelyCmd.PlayTone(frequency, ms) with Sub.Every; one oscillator, no scheduling
The game's instruments, from a score Hazel computesWith a JS playerthe game's own instruments and scheduler, reading Hazel's events as JSON; same events, same sound
Raw audio samples computed in HazelNoa tune is millions of samples (the forest: about 3 billion evaluation steps)

Tried in Hazel: a tune's score

ProgramWall time, three runs (s)Its work (s)
the program 0 (start-up only)1.47, 1.47, 1.400
1,216 events (76 bars of 16 notes)1.67, 1.63, 1.64about 0.2
12,160 events (760 bars)2.56, 2.60, 2.72about 1.2

A tune as data, one bar of (note, length) pairs repeated for a form, turned into (start, note, length) events by a recursive Hazel function (013/data/score.hz). Wall time of Hazel's command-line tool on the Ubuntu laptop (Intel i7), three runs each; the work is the difference from the start-up-only run. Re-timed on 2026-10-22 through Hazel's JS library in node, on ^report(25)'s three builds (before: 1 of the 14 performance PRs; + #2681 and #2661; after: all 14), the mean of two rounds: 1,216 events 140, 132 and 138 ms; 12,160 events 1,278, 1,300 and 1,418 ms (the last from rounds of 1,517 and 1,319 ms). The same within noise: the PRs speed up type annotations, which this program has none of, and statics, of which it has little.

The nine rules of Always Be Knolling (^du_report(5)), and how Hazel's livelits measure up today:

RuleHazel today
A box for each thing you can see or hear, named onceMisses: a UX cell is headed by the binding's name, the Projectors card by printed syntax
Nesting by boxes aloneMisses in the UX outline (a flat stack); nested livelits could do it
Knolled: a grid, things of a kind the same sizeMisses: a stack or a column
A row on one lineMeets, for a slider
Show the thingMeets: views draw pictures
One decision, one placePartly: one binding, but a box cannot be shown in two places
Mirror the picturePartly: the picture can be a UX cell, not a sticky first card
The same at every scaleMisses: three different layouts
Keep it fastMisses: a change costs 0.13 s (Kids' Choice) to 1.2 s (Full Body in the UX outline), against Dress Up's 1.4 ms

The options

OptionWhat it gives, and what it costs
One big livelit whose view is the menuFull control of tabs, grids and pictures; but its open tab and folds live in its model, so every click is an edit (and an undo step) costing about a second; good for bounded designers (the world grid, the castle map, the mixer)
A knolled UX layoutModules as tabs, their members as boxes in a grid, the picture as a sticky first card, tab and fold state kept by the editor, not in the program: meets rules 1, 2, 3 and 8, partly 7
The program organized as the menuA module per tab and a submodule per box, each setting a livelit; works with today's UX outline, but only as a flat stack
An MVU app in the sidebarFast, but its state is not program text, so the program cannot read the settings: rewriting the game as an app

The plan: The program organized as the menu, with a knolled UX layout over it, and big livelits for the bespoke designers: as the game itself has generic boxes for most tabs and its own full screens for music, the world and the castle.

4. The advanced features, staged as challenges for Hazel

StageFeaturesWhat Hazel needs
1Pictures from settings, in Hazel todaythe friend, a pose at a given phase, each place as a still picture, the tunes and the score as datanothing new (byte-exact pictures need stage 2's number builtin)
2Small builtinspictures that match the game's byte for byte, collision boxes from geometry, the depth sort and the friend's place, a score played by a JS player, the archive's recordingsan f1-like number builtin; a bounds library; a score export
3A clock for livelitswalking, crawling and swimming poses following the music's LFO; scenery animations written by Hazel; bubbles and foam on a timera subscription for livelits (a tick, the audio clock)
4Interaction inside the pictureclick to walk (walks planned around what is in the way), click to sit, ladders and climbing, pits and trap doors, lifting and dropping thingspointer events on a view's picture, stage 2's bounds, updates at frame rate
5The designers and the menusthe world designer and the castle map as big livelits; the settings in a knolled UX layoutthe knolled layout; per-livelit state outside the program
6The whole game at 60 fpseverything, as the game plays todaya change costing about 16 ms; today 0.13 to 1.09 s

What next

  1. Port the face alone (431 lines) and time it, as ^du_report(8) proposed; add an f1-like builtin first, so the picture can be compared byte for byte.
  2. Port the forest tune's score: compare its events one by one with the game's songOf("forest"), then play them through the game's own instruments from JSON.
  3. Try a view with an <audio> of the archive's recording, the cheapest sound in Hazel today.
  4. Prototype the knolled UX layout on one tab (the friend's face): modules as boxes, the picture as the first card.
  5. Ask the Hazel team about a clock (a subscription) for livelits: it unlocks stage 3 and most of stage 4.

How this was put together