Hazel performance analysis · ^report(11) · 2026-10-13

Naming what a slider leaves alone

How close could Hazel get to evaluating only what a slider change touches? ^report(10) found, for one slider on each of three Livelit Playground slides, that 75 to 84% of the steps Hazel evaluates after the edit are in calls whose inputs did not change, with calls named by their position in the evaluation. This report asks the same of every slider on those slides, and asks how the answer depends on how the calls are named, taking the design space of names from the merge sort in Fumola, Matthew's Adapton language, where one computation is named three ways.

Short answers. Preliminary, like 010: counts of evaluation steps, from an oracle that watches and reuses nothing, with every check free. Three measures (below): the picture diff, the shapes an edit changes, a diff of the result; the trace distance, the fewest steps any update could evaluate again, a diff of the computation; and an update's cost, the steps a given update does evaluate again, with its cache and its names. The last two are shares of a run from scratch, and they need not follow the first.

  1. A slider's picture diff can be empty, or most of the picture. Of 63 edits, one per slider, 22 have an empty picture diff and 6 change how many shapes there are; among the rest the picture diff runs from 2 to 85% (Figure 1).
  2. The picture's representation sets its diff. Hazel's pictures are flat lists of shapes in absolute coordinates, so moving a part changes every shape in it and hung from it, up to 113 shapes for one slider. In a picture of groups with transforms, every slider edit that keeps the number of shapes would change at most 14 nodes, at best, except Scenes' red eyes: 26 shapes all scaled alike, 25 nodes in a tree of groups, one with a shared symbol (Figure 5).
  3. What Hazel reuses today depends on where a slider's dependents sit. On Full Body and Witch, the whole figure is one binding that depends on every slider, so every slider edit costs today's incremental run 85 to 95% of a run from scratch, even one with an empty picture diff. On Scenes, the figure's sliders cost 5%: the woods are their own binding, and Hazel reuses them. The sliders the woods read cost 92%, and the blink rate, which the picture never reads, nothing.
  4. An update keyed by position would cost little: below the picture diff or within ten points of it, except on the eye sliders. On top of Hazel's own reuse, it would cost 0.0 to 1.7% of a run from scratch for the 22 edits with an empty picture diff. Of the 35 others, 15 would cost less than their picture diff and 8 at most five points more; 12 would cost more: 8 eye-slider edits, by 30 to 38 points, and 4 others, by 6 to 10 points. The eye sliders are the exception, the clearest case of the trace distance parting from the picture diff: their picture diff is 2 to 14%, and both their estimated trace distance and the cost of an update keyed by position are 32 to 51% (below).
  5. How calls are named matters only when an edit changes which calls are made. On the 57 edits that only change values, an update keyed by position costs at most 0.1 points more than one keyed by content, the estimate of the trace distance; on the 6 that change which calls are made, it costs 1.3 to 4.9 points more (Figure 3).
  6. Naming a call for its operand is the best identity name we tried, as naming a merge for the pair it compares is in Fumola: on the structural edits it costs 0.0 to 1.3 points more than content keys, against 1.3 to 4.9 points by position, and on value edits the same as position. Naming a call for its caller's inputs is the worst: it renames everything under a caller whose inputs moved.
  7. Reads instead of mentions add little, and lists as named cells a little more: recording reads lowers the update cost by at most 0.8 points of a run from scratch; treating lists as named cells lowers it further by 0.6 points at the median, and by at most 5.1 points, on the Witch slide, whose picture has 133 shapes (Figure 2).
  8. Every scheme's control is clean: the same program evaluated twice matches every call under every name, once names that would collide are told apart by their order.

At a glance

(a) The O(1) edits: at most 10 shapes changed02468100%20%40%60%80%100%picture diff: the shapes the edit changedupdate cost, keyed by positionScenes, leg size: picture diff 0, update cost 0%Scenes, blinks a minute: picture diff 0, update cost 0%Full Body, shirt height: picture diff 9, update cost 5%Full Body, neck: picture diff 9, update cost 9%Full Body, shoe size: picture diff 4, update cost 3%Full Body, arm width: picture diff 6, update cost 2%Full Body, alien fingers: picture diff 0, update cost 1%Full Body, star points: picture diff 0, update cost 1%Full Body, star turn (% to the next point): picture diff 0, update cost 1%Full Body, claws: picture diff 0, update cost 1%Full Body, claw length: picture diff 0, update cost 1%Full Body, eye size: picture diff 7, update cost 51%Full Body, eye gap: picture diff 4, update cost 45%Full Body, eye shape (round / narrow): picture diff 2, update cost 42%Full Body, eye angle: picture diff 2, update cost 42%Full Body, eye whites: picture diff 0, update cost 1%Full Body, shine size: picture diff 2, update cost 6%Full Body, smile: picture diff 8, update cost 15%Full Body, spiky teeth: picture diff 0, update cost 1%Full Body, tongue length: picture diff 0, update cost 2%Full Body, tongue width: picture diff 0, update cost 2%Witch, arm width: picture diff 6, update cost 6%Witch, alien fingers: picture diff 0, update cost 1%Witch, star points: picture diff 0, update cost 1%Witch, star turn (% to the next point): picture diff 0, update cost 1%Witch, claws: picture diff 0, update cost 1%Witch, claw length: picture diff 0, update cost 1%Witch, eye gap: picture diff 4, update cost 34%Witch, eye shape (round / narrow): picture diff 2, update cost 32%Witch, eye angle: picture diff 2, update cost 32%Witch, eye whites: picture diff 0, update cost 1%Witch, shine size: picture diff 2, update cost 9%Witch, witch nose length: picture diff 4, update cost 13%Witch, cat size: picture diff 0, update cost 1%Witch, broom size: picture diff 0, update cost 1%Witch, spiky teeth: picture diff 0, update cost 1%Witch, tongue length: picture diff 0, update cost 2%Witch, tongue width: picture diff 0, update cost 2%Full BodyWitchScenes(b) Every edit that keeps the number of shapes0%20%40%60%80%100%0%20%40%60%80%100%in proportion to the picture diffpicture diff: the share of the shapes the edit changedupdate cost, keyed by positionScenes, figure size: picture diff 4%, update cost 5%Scenes, across: picture diff 4%, update cost 5%Scenes, up, down: picture diff 4%, update cost 5%Scenes, leg size: picture diff 0%, update cost 0%Scenes, hut nearer: picture diff 3%, update cost 3%Scenes, eye size: picture diff 5%, update cost 10%Scenes, blinks a minute: picture diff 0%, update cost 0%Full Body, shirt width: picture diff 80%, update cost 21%Full Body, shirt height: picture diff 18%, update cost 5%Full Body, neck: picture diff 18%, update cost 9%Full Body, shoe size: picture diff 8%, update cost 3%Full Body, arms (up / down): picture diff 61%, update cost 17%Full Body, arm length: picture diff 61%, update cost 17%Full Body, arm width: picture diff 12%, update cost 2%Full Body, hand size: picture diff 49%, update cost 23%Full Body, alien fingers: picture diff 0%, update cost 1%Full Body, star points: picture diff 0%, update cost 1%Full Body, star turn (% to the next point): picture diff 0%, update cost 1%Full Body, claws: picture diff 0%, update cost 1%Full Body, claw length: picture diff 0%, update cost 1%Full Body, hand angle: picture diff 49%, update cost 20%Full Body, eye size: picture diff 14%, update cost 51%Full Body, eye gap: picture diff 8%, update cost 45%Full Body, eye shape (round / narrow): picture diff 4%, update cost 42%Full Body, eye angle: picture diff 4%, update cost 42%Full Body, eye whites: picture diff 0%, update cost 1%Full Body, shine size: picture diff 4%, update cost 6%Full Body, smile: picture diff 16%, update cost 15%Full Body, spiky teeth: picture diff 0%, update cost 1%Full Body, tongue length: picture diff 0%, update cost 2%Full Body, tongue width: picture diff 0%, update cost 2%Witch, shirt width: picture diff 85%, update cost 37%Witch, shirt height: picture diff 64%, update cost 19%Witch, neck: picture diff 13%, update cost 15%Witch, arms (up / down): picture diff 23%, update cost 17%Witch, arm length: picture diff 23%, update cost 17%Witch, arm width: picture diff 5%, update cost 6%Witch, hand size: picture diff 18%, update cost 21%Witch, alien fingers: picture diff 0%, update cost 1%Witch, star points: picture diff 0%, update cost 1%Witch, star turn (% to the next point): picture diff 0%, update cost 1%Witch, claws: picture diff 0%, update cost 1%Witch, claw length: picture diff 0%, update cost 1%Witch, hand angle: picture diff 18%, update cost 19%Witch, eye size: picture diff 8%, update cost 42%Witch, eye gap: picture diff 3%, update cost 34%Witch, eye shape (round / narrow): picture diff 2%, update cost 32%Witch, eye angle: picture diff 2%, update cost 32%Witch, eye whites: picture diff 0%, update cost 1%Witch, shine size: picture diff 2%, update cost 9%Witch, witch nose length: picture diff 3%, update cost 13%Witch, cat size: picture diff 0%, update cost 1%Witch, broom size: picture diff 0%, update cost 1%Witch, smile: picture diff 12%, update cost 19%Witch, spiky teeth: picture diff 0%, update cost 1%Witch, tongue length: picture diff 0%, update cost 2%Witch, tongue width: picture diff 0%, update cost 2%Full BodyWitchScenes
Figure 1. Every slider on the three slides, one edit each, against the update cost of a memo keyed by position on top of Hazel's own reuse, as a share of a run from scratch; on these edits that cost is also, within a fraction of a point, the estimated trace distance. (a) The O(1) edits, whose picture diff is a handful of shapes (38 of 57): the picture diff as a count of shapes. (b) Every edit that keeps the number of shapes: the picture diff as a share of the picture's shapes; on the diagonal, the computation would change in proportion to the picture. The six edits that change how many shapes there are are left out: their shapes cannot be compared one by one.
Update cost after the edit, in stepsFull Body, alien fingers121,973104,1891,0851,0851,0701,0701,085Full Body, smile121,973104,18918,81718,81718,59917,81618,817Full Body, eye size121,809104,02561,87661,88161,26959,81061,876Witch, shirt width186,699176,68168,73068,73067,79265,94168,730Scenes, eye size530,960489,10555,20455,20450,83642,93055,192Full Body, human fingers201,998184,214104,289102,031104,069102,900100,391from scratchHazel's update todaykeyed by positionkeyed by operandreads (distance)cells (distance, at most)content (distance)
Figure 2. The ladder for six edits: the steps of a run from scratch, of Hazel's update today, and the update cost of a memo keyed by position, by operand, by the calls' reads, with lists as named cells, and by content: the last three estimate the trace distance.
Update cost, by naming schemevalue edits (57)14.0%15.5%14.0%14.0%structural edits (6)46.8%46.2%44.2%43.6%by positionby callerby operandby content: trace distance, estimated
Figure 3. Each naming scheme for calls, summed over the edits that only change values and over the edits that change which calls are made: the update cost, as a share of Hazel's update today.

Picture diff, trace distance and update cost

An edit can be measured by what it does to the picture, and by what it does to the computation that draws the picture. The second is found by comparing the edit's evaluation with the one before it step by step: the steps of the new run that match steps of the old one, the same work with the same inputs, need not be taken again. Three quantities follow:

The picture diff and the trace distance are different diffs, and they need not follow each other. They would, if every shape were computed on its own at a cost in proportion to the shape: the diagonal of Figure 1. A slider can feed computation that sits between it and the picture, a fit or a check over many parts, and then the trace distance exceeds the picture diff: the eye sliders, whose outline every check of the mouth against the eyes walks again (below). Or it can change many shapes that are cheap to compute again, and then the trace distance falls short of it: the shirt's width. For example, Full Body's eye size: a picture diff of 7 of 49 shapes (14%), an estimated trace distance of 51% of a run from scratch; and Full Body's shirt width: a picture diff of 39 of 49 shapes (80%), an estimated trace distance of 21% of a run from scratch.

^report(10) measured all three without naming them: its "shapes changed" is the picture diff; its "steps a memo by name skips" are a run's steps less an update's cost, and its "left to run" is the update cost of a memo keyed by position.

Every slider

Each slider on the Full Body, Witch and Scenes slides was moved by a fifth of its range (down, if up would leave the range), with its literal's id kept, as a slider's commit keeps it. How far a slider moves does not set its picture diff; which shapes depend on it does. Moved by one instead, Full Body's shirt width, arm length and hand size change the same 39, 30 and 24 of its 49 shapes. Each slide was cut at its picture, as in ^report(10), and evaluated before the edit, then after it twice: from scratch, and incrementally against the first run's cache, as the eval worker does. Every call of the two runs after the edit was compared with the run before.

SlideSliderEditPicture diffHazel's update, stepsUpdate cost, by positionby operandby content
Scenesfigure size100 → 13823 of 57027,9825%5%5%
Scenesacross0 → 12023 of 57027,9835%5%5%
Scenesup, down0 → 7623 of 57027,9845%5%5%
Scenesleg size100 → 1560 of 570489,1050%0%0%
Sceneshut nearer0 → 2018 of 570489,1053%3%3%
Sceneseye size70 → 9026 of 570489,10510%10%10%
Scenesblinks a minute30 → 780 of 570410%0%0%
Full Bodyshirt width66 → 8839 of 49108,55821%21%21%
Full Bodyshirt height146 → 2029 of 49108,0485%5%5%
Full Bodyneck28 → 409 of 49105,1979%9%9%
Full Bodyshoe size100 → 1584 of 49104,1893%3%3%
Full Bodyarms (up / down)55 → 12730 of 49104,19317%17%17%
Full Bodyarm length95 → 21330 of 49104,18917%17%17%
Full Bodyarm width16 → 276 of 49104,1892%2%2%
Full Bodyhand size14 → 3024 of 49104,18923%23%23%
Full Bodyhuman fingers5 → 2549 → 129 shapes184,21452%51%50%
Full Bodyalien fingers3 → 230 of 49104,1891%1%1%
Full Bodystar points5 → 250 of 49104,1891%1%1%
Full Bodystar turn (% to the next point)0 → 200 of 49104,1891%1%1%
Full Bodyclaws4 → 240 of 49104,1891%1%1%
Full Bodyclaw length100 → 2760 of 49104,1891%1%1%
Full Bodyhand angle0 → 7224 of 49110,02520%20%20%
Full Bodyeye size50 → 1177 of 49104,02551%51%51%
Full Bodyeye gap38 → 784 of 49102,42945%45%45%
Full Bodyeye shape (round / narrow)0 → 202 of 49104,18942%42%42%
Full Bodyeye angle0 → 242 of 49104,02942%42%42%
Full Bodyiris size0 → 3049 → 51 shapes112,42456%54%54%
Full Bodyeye whites0 → 300 of 49104,1891%1%1%
Full Bodyshine size45 → 752 of 49104,1896%6%6%
Full Bodysmile85 → 1458 of 49104,18915%15%15%
Full Bodymouth open0 → 6049 → 54 shapes134,28229%28%27%
Full Bodyspiky teeth5 → 80 of 49104,1891%1%1%
Full Bodytongue length100 → 1580 of 49104,1892%2%2%
Full Bodytongue width100 → 1440 of 49104,1892%2%2%
Witchshirt width66 → 88113 of 133176,68137%37%37%
Witchshirt height146 → 20285 of 133176,17119%19%19%
Witchneck28 → 4017 of 133171,87815%15%15%
Witcharms (up / down)55 → 12730 of 133170,87417%17%17%
Witcharm length95 → 21330 of 133170,87017%17%17%
Witcharm width16 → 276 of 133170,8706%6%6%
Witchhand size14 → 3024 of 133170,87021%21%21%
Witchhuman fingers5 → 25133 → 213 shapes256,73544%44%42%
Witchalien fingers3 → 230 of 133170,8701%1%1%
Witchstar points5 → 250 of 133170,8701%1%1%
Witchstar turn (% to the next point)0 → 200 of 133170,8701%1%1%
Witchclaws4 → 240 of 133170,8701%1%1%
Witchclaw length100 → 2760 of 133170,8701%1%1%
Witchhand angle0 → 7224 of 133176,70619%19%19%
Witcheye size50 → 11711 of 133170,70642%42%42%
Witcheye gap38 → 784 of 133169,11034%34%34%
Witcheye shape (round / narrow)0 → 202 of 133170,87032%32%32%
Witcheye angle0 → 242 of 133170,71032%32%32%
Witchiris size0 → 30133 → 135 shapes179,25150%45%45%
Witcheye whites0 → 300 of 133170,8701%1%1%
Witchshine size45 → 752 of 133170,8709%9%9%
Witchwitch nose length100 → 1744 of 133170,87013%13%13%
Witchcat size100 → 2980 of 133170,8701%1%1%
Witchbroom size100 → 2980 of 133170,8701%1%1%
Witchsmile85 → 14516 of 133170,87019%19%19%
Witchmouth open0 → 60133 → 138 shapes201,32928%24%23%
Witchspiky teeth5 → 80 of 133170,8701%1%1%
Witchtongue length100 → 1580 of 133170,8702%2%2%
Witchtongue width100 → 1440 of 133170,8702%2%2%

Naming, a design space

A memo, like the paper's demanded computation graph, needs a name for everything it keeps: a name that finds the same work in the next run, and that no other work in the same run claims. Hazel's evaluation needs names in several places, and for each there is a space of choices:

What needs a nameHazel todayChoices
The input: a slider's literalThe editor's tile id, kept while the tile is editedKeep it. It is what the user edits, so it needs a name that stays put while its value changes: Fumola's rule for a pointer name.
A call: a memo entryNone: calls are not cachedBy position (the call path and a count), by the caller (its site and inputs), by its operand (its site and its argument), by content (its code and every input). Measured below.
A list cell evaluation builds (the picture's list of shapes)A fresh id at every stepBy index, by the cell it came from (the paper's map forks an output cell's name from its input cell's), or by content (^report(9)'s Merkle names).
A shapeA fresh id, and a name string the program gives it ("eye", "red eye")The program's own name, told apart from others of the same name by order: names carried by the data, as Fumola's elements carry symbols.
A function that captures the slider (Scenes' map is given one)A fresh idIts definition site with the names of what it captures; by content, it changes whenever the slider does.

A worked example: naming a merge

Fumola's merge sort (levelTree.fumola, with its notes on naming) builds a merge network over a balanced tree of the input list and names each cell of each merge in one of three ways. The computation and its answer are the same under all three; only the names differ, and with them what an edit to the input list can reuse:

Fumola's schemeA merge cell is named forClosest Hazel scheme, for calls
pathwiseits position in the recursion: the path from the root of the treeby position: the call path and a count (^report(10)'s)
inductivethe tree node it belongs to and the element that won the comparison, in a namespace per nodeby the caller: the caller's site and inputs, made unique by order
cartesianthe pair of elements compared, which no other cell of the network comparesby operand: the call site and the argument's value

In Fumola, a position survives only as long as the shape it addresses: under an edit that reshapes the tree, pathwise leaves several times as many cells unmatched as the others, and cartesian, whose name is nearly the content of the comparison, bounds the worst case. The notes read all three as approximations of content addressing with identity keys, and give a rule: what is edited gets a pointer name, a name that stays put while its value changes, and what is derived gets a structural name, found again by content. Hazel's picture is derived through and through, with one pointer at its root, the slider.

Two properties make a scheme usable. A name must find the same work in the next run, and it must be unique in a run: Fumola's cartesian names are unique because a merge network never compares the same two elements twice. Hazel's drawing code makes no such promise: the same helper is called from the same site with the same argument under different callers, so names by caller and by operand collide: in one run of Full Body, 1,375 calls share a name by operand with an earlier call, and 609 by caller. As inductive opens a namespace per tree node, each name here is told apart from the others of the same name by its order in the run; a name by position is unique without that.

Four outcomes

Fumola's notes compare namings with a microscope that only names can have. Each call after the edit is equal (a call before had its name and the same inputs), notEqual (its name, other inputs), or onlyRight (a name new to this run); a call before whose name is gone is onlyLeft. A memo can reuse only the equal; notEqual is a call that keeps its identity and changes its value, which a demanded graph could repair in place. Content keys cannot have notEqual at all: under them a changed call is simply a different one.

The calls after the edit, by outcomesmile, by positionequal: 2,9272,927notEqual: 595595onlyLeft: 42onlyRight: 42smile, by callerequal: 2,7972,797notEqual: 63onlyLeft: 704704onlyRight: 704704smile, by operandequal: 2,9272,927notEqual: 129onlyLeft: 508508onlyRight: 508508fingers, by positionequal: 2,9492,949notEqual: 553553onlyLeft: 62onlyRight: 1,9811,981fingers, by callerequal: 2,8632,863notEqual: 46onlyLeft: 655655onlyRight: 2,5742,574fingers, by operandequal: 3,0123,012notEqual: 85onlyLeft: 467onlyRight: 2,3862,386equalnotEqualonlyLeftonlyRight
Figure 4. The calls after two edits, by outcome, under each scheme. A value edit (the smile on Full Body) and an edit that changes which calls are made (the human fingers on Full Body, 5 → 25).

Reads instead of mentions

^report(10)'s oracle took a call's inputs to be the values of every variable its body mentions. The paper records instead what a thunk actually read, each read with what it saw. Here every variable a call's own steps look up, and the result of every call it makes, is recorded with the hash of its value; a call whose reads all saw what the call of its name saw before is clean, and skips its own steps, while its callees are judged on their own reads. This is the paper's demanded graph, re-checked, in steps.

Summed over the 63 edits, after today's reuse, an update that matches each call's own steps by what it reads would cost 1,678,586 steps, against 1,696,510 for one that compares what the bodies mention: 1.1% less. A function whose body mentions a slider's value almost always reads it.

Lists as named cells

A walk over a list takes the rest of the list at each step, and a rest that holds a changed element is itself changed (^report(10)). The paper's remedy is a name for each cell, so that a step reads one cell and the rest is a reference whose name does not change with what it holds. Here every list a call reads is compared by its head alone, with the rest stood in for by such a name. That is an upper bound on what cell names could save: right for a function that walks a list cell by cell, too generous for one that hands the whole list to a builtin, as @ does.

Over the same edits, with lists as named cells an update would cost at most 1,509,299 steps, 10.1% less than reads. Under reads, a call that walks a list already runs again only its own steps; the work it calls for each element is judged on its own reads, and skipped where the element did not change. Cells save the walks' own steps, and over a picture's long lists those add up: most of the saving is on the Witch slide, where all six of the largest gains are.

Why the eye sliders cost so much

The eye sliders are where the trace distance parts most from the picture diff: Full Body's eye size: a picture diff of 7 of 49 shapes (14%), an estimated trace distance of 51% of a run from scratch; Full Body's eye gap: a picture diff of 4 of 49 shapes (8%), an estimated trace distance of 45% of a run from scratch; Full Body's eye shape (round / narrow): a picture diff of 2 of 49 shapes (4%), an estimated trace distance of 42% of a run from scratch; Full Body's eye angle: a picture diff of 2 of 49 shapes (4%), an estimated trace distance of 42% of a run from scratch; Witch's eye size: a picture diff of 11 of 133 shapes (8%), an estimated trace distance of 42% of a run from scratch; Witch's eye gap: a picture diff of 4 of 133 shapes (3%), an estimated trace distance of 34% of a run from scratch; Witch's eye shape (round / narrow): a picture diff of 2 of 133 shapes (2%), an estimated trace distance of 32% of a run from scratch; Witch's eye angle: a picture diff of 2 of 133 shapes (2%), an estimated trace distance of 32% of a run from scratch. The eyes feed geometry the face computes around them, and the diagnosis below names it. The largest part is the check that the mouth clears the eyes: clear_of_eyes asks eye_below at 41 points along the lip, and each time below_poly's helper go walks every edge of the eye's outline. A new eye size is a new outline, so every one of those walks runs again, whatever the update's names. Then the head grows to fit the eyes (eye_need) and the eyes are drawn again (round_eye): parts of the picture diff.

Added later on 2026-10-13: this is by design. For a small face or body, the head is meant to fit the eyes, so its geometry, and everything connected to it, the hair and the rest, depends on eye size. The eye sliders' trace distance is that dependence, not work an update could avoid.

Full Body, eye size: the functions whose calls run again, by their own steps, and the variables whose values differ most often among those calls.

FunctionCallsOwn stepsDiffering
go94329,765go 557, first 557, qs 536, x2 495, x1 495
round_eye776,339x 77, r 77, px 72, py 67, y 22
shift_points1745,724vs 162, tl 150, v 144, y 140, x 100
eye_need504,418eye_need 50, eye_half_w 50, eye_half_h 50, eye_dx 50, u 46
clear_of_eyes422,882eye_below 42, clear_of_eyes 42
scale_points361,634sy 36
skinned261,183ps 25, tl 24, p 7
on_head561,044ps 52, p 50, tl 48

Full Body, eye gap: the functions whose calls run again, by their own steps, and the variables whose values differ most often among those calls.

FunctionCallsOwn stepsDiffering
go94328,685go 531, first 531, qs 512, x2 490, x1 490
round_eye776,339x 77
eye_need504,418eye_need 50, eye_dx 50, u 48, px 48, here 24
shift_points1123,680x 104, vs 104, v 104, tl 96
clear_of_eyes422,882eye_below 42, clear_of_eyes 42
skinned241,091ps 23, tl 22, p 4
on_head561,044ps 52, p 52, tl 48
to_head52988u 52

Witch, eye size: the functions whose calls run again, by their own steps, and the variables whose values differ most often among those calls.

FunctionCallsOwn stepsDiffering
go94329,765go 557, first 557, qs 536, x2 495, x1 495
shift_points2538,346vs 237, tl 221, v 219, y 215, x 100
round_eye776,339x 77, r 77, px 72, py 67, y 22
dressed1105,030ps 109, tl 108, p 11
skinned1104,699ps 109, tl 108, p 11
eye_need504,418eye_need 50, eye_half_w 50, eye_half_h 50, eye_dx 50, u 46
hat_verts793,131head_lift 79, hat_verts 79
clear_of_eyes422,882eye_below 42, clear_of_eyes 42

Witch, eye gap: the functions whose calls run again, by their own steps, and the variables whose values differ most often among those calls.

FunctionCallsOwn stepsDiffering
go94328,685go 531, first 531, qs 512, x2 490, x1 490
round_eye776,339x 77
dressed1004,546ps 99, tl 98, p 4
eye_need504,418eye_need 50, eye_dx 50, u 48, px 48, here 24
skinned1004,245ps 99, tl 98, p 4
shift_points1123,680x 104, vs 104, v 104, tl 96
clear_of_eyes422,882eye_below 42, clear_of_eyes 42
on_head561,044ps 52, p 52, tl 48

How the picture's representation sets its diff

Measured later on 2026-10-13: ^report(12) built Full Body as a picture of groups with transforms and measured it: every slider edit that changes the picture changes at most 14 nodes (this section estimated 14 at best), and the tree, flattened, is today's picture exactly. Not flattened, the trace distance follows: moving a part costs about what an edit that changes nothing costs.

The picture diff counts shapes of the picture as Hazel's Drawing library represents it: a flat list of polygons and paths, every point in absolute coordinates. The library places a part by moving every one of its points (shift_points, on_head, to_head, among the largest functions that run again in ^report(10) and above). So moving a part changes every shape in it, and every shape hung from it: the witch's shirt width changes 113 of 133 of her shapes.

In a picture made of groups with transforms, as with SVG's <g transform> or a scene graph, a part keeps its shapes in its own coordinates, and moving, scaling or turning it changes one transform. To estimate what that would change, every shape an edit changed was compared with its old self: moved (every point translated alike), scaled (and maybe moved), rotated (and maybe scaled and moved), each one transform of the shape's old points; or reshaped, its geometry itself changed. Shapes changed by one same transform would share one changed node, and the others count one each. That is the best case, with the groups lined up with the transforms the edit applies.

Changed: shapes as a flat list, or nodes with transformsWitch, shirt width11311Witch, shirt height854Full Body, shirt width397Full Body, arms (up / down)302Full Body, arm length308Witch, arms (up / down)302Witch, arm length308Scenes, eye size2625Full Body, hand angle242Full Body, hand size2414Witch, hand angle242Witch, hand size2414Scenes, figure size238Scenes, across231Scenes, up, down232Scenes, hut nearer181Witch, neck172Witch, smile163Witch, eye size119shapes changed, the flat listnodes changed, groups with transforms (at best)
Figure 5. The slider edits that change at least ten shapes: the picture diff as Hazel represents the picture (a flat list of shapes in absolute coordinates), against the nodes that would change in a picture of groups with transforms, at best.
SlideSliderShapes changedmovedscaledrotatedreshapedDistinct transformsNodes changed, with transforms
Full Bodyshirt width393610257
Full Bodyarms (up / down)300030022
Full Bodyarm length302400628
Full Bodyhand angle240024022
Full Bodyhand size24012012214
Full Bodyneck9800112
Full Bodyshirt height9800123
Full Bodysmile8600213
Full Bodyeye size7040325
Full Bodyarm width6000606
Full Bodyeye gap4400022
Full Bodyshoe size4040022
Full Bodyeye angle2002022
Full Bodyeye shape (round / narrow)2000202
Full Bodyshine size2020022
Sceneseye size26026002525
Scenesfigure size230160718
Scenesacross232300011
Scenesup, down232200112
Sceneshut nearer180180011
Witchshirt width113545603811
Witchshirt height858400134
Witcharms (up / down)300030022
Witcharm length302400628
Witchhand angle240024022
Witchhand size24012012214
Witchneck171600112
Witchsmile161400213
Witcheye size11040729
Witcharm width6000606
Witcheye gap4400022
Witchwitch nose length4030134
Witcheye angle2002022
Witcheye shape (round / narrow)2000202
Witchshine size2020022

What next

  1. A picture of groups with transforms in the Drawing library, and shared symbols for repeated shapes: then measure its picture diff, its trace distance and its update cost on the same edits. Of everything here, it would change the most. Done later on 2026-10-13: for Full Body, in ^report(12).
  2. Edits with diffs in between: a slider's picture diff does not depend on how far it moves, so other edits are needed for the middle of Figure 1: a ^choice switched (human hands to alien ones), or one vertex of a ^polygons editor moved, which changes one shape, or the few anchored to it.
  3. Time a memo keyed by operand. It finds what content keys find on value edits and most of it on the others, and its name costs one hash of the argument, already there with ^report(9)'s Merkle names kept on the values. Build it at the call boundary, with names told apart by order, and time it against today's run.
  4. Names carried by the data, for the places where operand names collide: a shape's own name, and list cells named from the cells they came from, as the paper's map does and as Fumola's elements carry symbols.
  5. Look at what the eye sliders' work computes, and whether its answers could be cut off sooner: the calls whose inputs change but whose results do not. Closed later on 2026-10-13: not needed. The dependence is by design, Matthew notes: for a small face or body, the head is meant to fit the eyes, so its shape, and everything connected to it, the hair and the rest, depends on eye size.
  6. Edits beyond sliders: typing in a definition, adding a shape, reordering a list. These are the edits that reshape, where Fumola's measurements say names matter most.

How this was put together