Closed tickets
A copy of the Hazel Work Dashboard that Matthew runs on his laptop, published within a minute or two of each change to its notes, and every 15 minutes for the status from GitHub. Its localhost links work only there.
← the Hazel Work Dashboard. Updated 2026-10-16 01:15 MDT. Our tickets once closed, newest first: done, or closed for another reason (not needed, dropped, superseded), with what came of each. A number is never reused; the open tickets are on the dashboard: the work queue and the full backlog.
| Ticket | Area | Status, closed | Outcome | The ticket, as it last read |
|---|---|---|---|---|
^ticket(131) | dress-up | done 2026-10-15 | Dress Up 0.0.57: in profile the sick puddle is drawn on its own (faceSvg's sick), the stream from where the lips meet the face's front edge, the pile whole under it at 0.4 width (between Matthew's third and half), symmetric. Facing us unchanged. ⏱ opened 2026-10-15 22:13, closed 23:18 (66 min) · 4 min of work, timed · 1 messages from Matthew, 0 questions to him · ~120 chars (~30 tokens) · ~0 min together | Dress Up, in profile: the sick puddle symmetrical, and narrower. Matthew, 22:11, with a screenshot (~/du-shots/profile-sickness-2026-10-15.webp: the friend facing right in the witch's hut, sickness 300): 'Full sickness (puddle on floor) should be 1/3 or 1/2 width, but symmetrical, not like this.' The cause: the puddle (sickPile) is drawn inside the mouth's group, and in profile the mouth is its far half carried to the front (0.0.52), so the puddle was cut in half and moved too. Fix: in profile draw the puddle on its own, centred under the friend and a third to a half as wide as facing us, and keep the stream from the mouth whole. Check every face option that lives in the mouth's group in profile (tongue out, teeth, candy, the explode lid) on a contact sheet. Tonight (🌙). from Matthew, 2026-10-15 |
^ticket(130) | dress-up | done 2026-10-15 | Dress Up 0.0.57: in profile in a place (not in water), the far arm, sleeve and hand are drawn apart (#figure-far) a body's depth (26 units) behind the feet among the place's things: hugging the witch, the near arm is in front of her and the far one behind (checked: layer order far, witch, friend; ~/du-shots/r42/hug_crop.png). ⏱ opened 2026-10-15 22:11, closed 23:18 (68 min) · 4 min of work, timed · 1 messages from Matthew, 0 questions to him · ~160 chars (~40 tokens) · ~0 min together | Dress Up: the far arm behind, the near arm in front, among the place's things (a hug). Matthew, with a screenshot of the friend in profile in the witch's hut, arms out toward the witch: 'is there a layer difference between near and far arm? I'm trying to give the witch a hug, but the layers do not make it possible ATM.' Today the friend is one layer among the place's objects, sorted by their feet (99's depth): both arms are in front of the witch or both behind. So in profile, give the far arm and hand (and perhaps the far leg) a depth of their own, a body's thickness behind the feet, drawn among the objects there, and the near ones in front: standing just in front of the witch, the far arm goes behind her and the near arm in front, a hug. Check with every costume and in the places with people (the witch, the healer, the cooks...). Tonight (🌙). from Matthew, 2026-10-15 |
^ticket(129) | dress-up | done 2026-10-15 | Dress Up 0.0.57: a Pose tab first (standing; cross-legged meditating with hands in the lap, eyes closed, feet up over the pants; dance, a different one of five each time), face.closed (calm lids), feet that turn (feetTurn and poseFootL/R). Sheets: ~/du-shots/r42/poses2.png. Not done: turning a foot by touch. ⏱ opened 2026-10-15 21:59, closed 23:18 (80 min) · 9 min of work, timed · 1 messages from Matthew, 0 questions to him · ~420 chars (~105 tokens) · ~0 min together | Dress Up: feet that turn, eyes that close, and a menu of poses. Matthew, 2026-10-15 21:59, with a reference picture of feet in a lotus position (~/du-shots/lotus-feet-reference-2026-10-15.webp): 'Feet in a lotus position (legs crossed) do not look like this -- we need to rotate, or possibly redraw them: let's permit feet to rotate, like hands, and have a menu of poses for the main char: standing facing camera (default); cross legged, with hands on lap [he wrote 'lamp' and corrected it: lap], in meditation (eyes closed? we need that too); random dance pose -- be creative.' So: (1) each foot turns, as hands do (a turn per foot, and the pose's foot angle), so crossed legs show the soles and toes as in the picture; (2) eyes that can close (for meditating, and for blinking or sleeping later); (3) a pose menu: standing (the default), cross-legged meditation (legs crossed in a lotus, hands resting in the lap, eyes closed), and a dance pose (a new one each time it is chosen, or a few to pick from), built on 121's pose offsets so dragging still works from there. Tonight (🌙). 22:11, Matthew: 'The pose chip menu should be top level, before face': the pose chips in a row of their own at the top of the controls, before the 😀 Face tab (with facing and sitting there too, where they fit). from Matthew, 2026-10-15 |
^ticket(128) | dress-up | done 2026-10-15 | Dress Up 0.0.57: swim trunks, briefs and the skin under any swimsuit follow each thigh on posed or bent legs, facing us (shortLegs) and in profile (hip block and thigh bands); straight legs unchanged. Crouch contact sheet of every outfit and costume: ~/du-shots/r42/crouch.png. ⏱ opened 2026-10-15 21:59, closed 23:18 (80 min) · 6 min of work, timed · 1 messages from Matthew, 0 questions to him · ~140 chars (~35 tokens) · ~0 min together | Dress Up: shorts and swim trunks fit posed legs. Matthew, 2026-10-15 21:58, with a screenshot (the friend in swim trunks, knees out and feet in, posed by dragging): 'the pants do not change shape to fit the legs when they are shorts.' The underwear or swimsuit bottom (undiesBottom) and shorts keep their standing shape while the thighs turn out, so the legs come out of the wrong places. Make shorts, swim trunks, briefs, bikini bottoms and the one-piece follow each thigh as posed (as long pants already do since 0.0.47), and check this pose for all costumes (a contact sheet: every costume and outfit in this crouch). Tonight (🌙). from Matthew, 2026-10-15 |
^ticket(126) | performance | done 2026-10-15 | PUBLISHED 2026-10-15 21:44 on Matthew's word ('publish 15 update', after 'looks pur fect'): https://matthewhammer.org/hazel/performance-analysis/015/#update-box. A dated update box and an Update section with Figure 7, shapes per measured picture (Hazel Full Body 49, Kids' Choice 93, the Scenes slide 570 for scale; Dress Up the friend alone 46, the forest 457 with 16 animated, the witch's woods 1,452 with 463 animated), and the game's size (0.0.48: 12,647 lines, 24 places, 1,370 settings, 402 controls, 28 tunes) against Full Body's 167 items: only the friend alone is like for like. Only additions. Homepage 397acd0, series repo 3262f63; card updated. ⏱ opened 2026-10-15 21:35, closed 21:44 (10 min) · 12.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~330 chars (~82 tokens) · ~0 min together | Revise ^report(15) with shape counts, to quantify scene complexity. Matthew, 2026-10-15 21:30: 'I want to emphasize that Dress Up is now far more complex than what Hazel can handle, and what we are measuring as a comparison. It's not really apples to apples. This revision enhances, and does not alter, the story.' 21:35: DRAFT ready for his review: http://localhost:8131/series/015/#update-box (local commit in ~/hazel-perf-report, not pushed). A dated update box under the short answers and an Update section with Figure 7: shapes per measured picture (Hazel Full Body 49, Kids' Choice 93, the Scenes slide 570 for scale; Dress Up the friend alone 46, the forest 457 with 16 animated, the witch's woods 1,452 with 463 animated), each with what its drag did; the game's size (0.0.48: 12,647 lines, 24 places, 1,370 settings, 402 controls, 28 tunes) against Full Body's 167 items. Only additions: 20 lines, nothing of the published story changed. The card line is drafted too. To publish: copy 015 and the series index to the site, push both repos. 21:41, Matthew: 'looks pur fect'. Ready to publish on his word ('publish'); not published yet.from Matthew, 2026-10-15 |
^ticket(125) | dress-up | done 2026-10-15 | Released as Dress Up 0.0.53 at 20:04 (31255a8, tag dress-up-0.0.53; Pages green, live check 20:05). '👂 ears' in the face tab: none / lobe hanging free / lobe attached / 🧝 elf or fairy (pointy top, free lobe), default free; '👂 ear size'. earSvg in the skin's color with a rim curl. Facing us: at the sides at eye height, behind the head; under long hair, over shorter hair, elf ears always over (tips poke through). In profile the same shape on the side of the head unless long hair covers it; hoods hide them. With ears 'none', every facing draws as 0.0.52 except the profile's old default ear (now drawn only when ears are on). ⏱ opened 2026-10-15 19:59, closed 20:05 (7 min) · 12.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~330 chars (~82 tokens) · ~0 min together | Dress Up: ears, in every view. Matthew, for M, who wanted them (after seeing the ear ^ticket(124) gave the friend in profile): ears on the friend facing us and in profile, in the skin's color; three shapes for now: (1) normal, with a separate (free) lobe; (2) normal, with an attached lobe; (3) elf or fairy ears: pointy at the top, with a separate lobe. And a size control. Long hair and hoods cover them as they would; the visibility panel's head slider applies; facing us they stick out at the sides of the head, at eye height.from Matthew, 2026-10-15 (for M) |
^ticket(124) | dress-up | done 2026-10-15 | Released as Dress Up 0.0.52 at 19:59 (52ebf99, tag dress-up-0.0.52; Pages green, live check 20:00). '↔️ facing' (toward us / left / right) and '🪑 sitting' in the body tab. Profile: narrower body, limbs and shoes point the way they face, near arm in front and far behind, per-leg pants under a hip block, the face's near-side features at the front with the mouth's far half (smile turns toward the cheek, after Matthew's 'looks like a frown'), side nose bump, an ear, side hair for every style (after 'render the hairstyle from the profile view'), hoods open at the front with the brim's animal face (after 'tiger hood needs a profile version'), witch hat bending back with edge-on buckles (after 'belt on witch's hat'), tails behind. Sitting: facing us short thighs + knee creases (after the 'butt' note), in profile thighs level forward. Posing works in both (drags land within 0.1 units). Facing us standing: byte-identical to 0.0.51 on 143 random friends. Left for 50: seats in the scenes and other characters sitting; not done: bird wings and the ghost stay front-on in profile. FOLLOW-UP 0.0.53 (20:04): Matthew: wings in profile should be in perspective or edge on, not flat: back wings (fairy, falcon, eagle) turned away (back half, foreshortened 0.42, far wing behind), bird-suit arm-wings edge on. ⏱ opened 2026-10-15 17:40, closed 20:00 (141 min) · 20.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~225 chars (~56 tokens) · ~0.0 min together | Dress Up: the friend drawn sideways and sitting. Matthew, for tonight: "sideways drawing of main char (facing to left or right, in profile). sitting drawing of main char (facing forward, left and right)." Plan: a facing choice (front, left, right) and a sitting pose, drawn so every costume, hair style and face still fits (profile face, one eye, nose; hips and knees bent for sitting, built on 99's knees). The sheets show each costume facing each way, standing and sitting. Ties in with 121 (posing by touch) and the motions (walking and running face sideways). Added 17:40: Matthew: "We want to use the sitting posture for the chars throughout the scenes." So the sitting drawing is one the other characters use too, not only the friend, and the scenes get seats where a character sits: the living room's couch, the dining room's chairs, logs by the camp fire, and benches. With 99's depth model, a seat is at its object's depth, and the sitter is drawn just before whatever stands in front of the seat. 2026-10-15T19:26: Matthew: to the top of the queue, right after the current ticket (no longer a 🌙 long run). from Matthew, 2026-10-15 |
^ticket(123) | dress-up | done 2026-10-15 | Released as Dress Up 0.0.49 at 19:34 (414bbfd, tag dress-up-0.0.49; Pages green, live check 19:35). In deep water (WATER places but the shallow stream and brook) treadNow() gives the stroke: arms scull out and in together, legs kick in turn, a bob twice a stroke, one stroke per LFO B wave (2 beats), added to the pose (poseT); the friend floats at the ground of their legs as posed (no jolt); the bob moves the friend inside the water mask. While treading, a rAF loop redraws the friend ~30/s (not during drags or hidden), ~2.5 ms a redraw; headless Firefox kept ~54 fps. '🏊 treading water where it's deep' slider (default 100). Dry/shallow figures byte-identical to 0.0.48 on 134 random friends. FOLLOW-UP 0.0.56 (22:14, 68c132c): Matthew: the arms need to go into the water ('maybe for very short arms, the body should be lower'): treading, the waterline is half an arm below the shoulders (8 + armLen/2, clamped), and the stroke keeps the arms down. ⏱ opened 2026-10-15 17:37, closed 19:35 (119 min) · 9.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~130 chars (~32 tokens) · ~0 min together | Dress Up: a treading-water animation. Matthew: "Eventually, we'd want a 'treading water' animation for that -- it's fine to leave that detail to another ticket, later." After 122 (the friend knows water) and 121 (each limb posed on its own): in water the friend treads, arms sweeping and legs kicking under the surface, timed to the music clock like the other motions (walking, running, climbing, crawling, swimming, dancing from 99). from Matthew, 2026-10-15 |
^ticket(122) | dress-up | done 2026-10-15 | Released as Dress Up 0.0.48 at 18:48 (08abedf, tag dress-up-0.0.48; Pages green, live check 18:49). In water the friend is see-through below the waterline (mask; '🌊 see-through under the water', default 70) with a wake: three spreading rings + dashed foam, far half behind and near half in front ('💧 a wake round the friend in the water', default 100). Water per place from the scenes' own shared helpers (WATER): lake, sea, waterfall pool, castle moat (near half, not the bridge), tree house stream, cemetery brook. Waist-deep, knee-deep in the stream and brook; rings no wider than the water (waterAcross). Built by a helper in a worktree (14 min, node-verified: 124 scenes byte-identical, dry friend identical at 1,904 spots); merged, tuned and checked in the pane by me. Redraw in water 1.2-1.4 ms vs 0.9 dry. FOLLOW-UPS from Matthew's look, 2026-10-15: 0.0.50 (19:38, d3e744e) the ripples hovered half a body above the water (and over the trees when far back): the friend now sinks, the waterline placed where they stand; 0.0.51 (19:42, 2765dfa) legs under the water showed over the sand, bank and grass: below the waterline the friend shows only where the place's water is (waterRuns from WATER, mask + ripple clip), 2-6 ms a draw. ⏱ opened 2026-10-15 17:37, closed 18:49 (73 min) · 21.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~200 chars (~50 tokens) · ~0 min together | Dress Up: the friend knows water. Matthew: "let's have a special knowledge of water. In water, by default, let's make the bottom half transparent, and add a 'wake' effect (water moving, like near the base of the waterfall now)". Plan: builds on 99's depth model. Each place says where its water is on the ground (the lake, the sea at the beach, the waterfall's pool, the tree house's stream, and others); when the friend's feet are in it, their lower half below the water line shows through (clipped copy at low opacity, not a mask, for drag speed), and ripples spread from them like the waterfall's base. A setting to turn it off ("by default"). Under the sea the friend is all underwater, so no water line there. from Matthew, 2026-10-15 |
^ticket(121) | dress-up | done 2026-10-15 | Released as Dress Up 0.0.47 at 18:40 (584833b, tag dress-up-0.0.47; Pages green, live check 18:41). Press a hand, foot, elbow or knee (nearest within 30 screen px) and drag: hands/feet reach by two-bone IK keeping the bend's side, elbows/knees swing the limb. Per-limb turn+bend stored as body.pose{Arm,Elbow,Leg,Knee}{L,R} on top of the sliders; zero pose draws byte-identical to 0.0.46 (200 random friends + witch/fairy/ghost). Posed legs stand on the lower foot or kneel; posed pants mitred at the knee; ground and (no-scene) frame held while a joint is held. Body tab: how-to note + '🧍 stand straight again'. Checked in the pane: forest and no-background drags, all 19 costumes x pants styles x undies posed without errors. ⏱ opened 2026-10-15 17:37, closed 18:41 (65 min) · 14.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~240 chars (~60 tokens) · ~0 min together | Dress Up: pose the friend by touch, dragging the hands, feet, elbows and knees. Matthew: M plays on an iPad with touch; a game she already has lets you drag the joints, hands and feet to pose the character. Can we offer that? Plan: builds on 99's elbows and knees. Each arm and leg gets its own angle and bend (today both sides bend alike), and dragging a hand or foot solves the elbow or knee from where the finger is (two-bone inverse kinematics, law of cosines); dragging an elbow or knee turns the upper limb. Round, finger-sized handles (about 44 px) in a pose mode, so an ordinary drag still moves the whole friend; touch-action none on the handles; the drag pause from 117 kept. Check on a touch-emulated tablet viewport. from Matthew, 2026-10-15 |
^ticket(120) | perf | done 2026-10-15 | PUBLISHED 2026-10-15 ~19:40 on Matthew's word ('publish report 15 -- looks really nice. Love all of the figures.'): https://matthewhammer.org/hazel/performance-analysis/015/ (homepage 480be55, listed on /hazel/; series repo 9f67dd4, pushed alone, ^report(8)'s draft kept local on top). Dress Up vs its livelits on #2635 at 032fe3aca6, headless Firefox 157: Dress Up never froze (17 ms gaps), Kids' Choice froze 0.11 s / final 0.13 s after release, Full Body froze 2.9 s / final 1.1 s; per change 1.4 ms vs 225 ms vs 1,010 ms; then-and-now; a labelled guess for Sept 2027.⏱ opened 2026-10-15 17:28, closed 20:50 (202 min) · 30.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~330 chars (~82 tokens) · ~0 min together | A report draft: Dress Up's performance today against what Hazel can handle today. Matthew: compare the two (the plain-JS game, the prototype, and the same features as livelits in Hazel), with speculation about what can bridge the gap over the next 11 months, the duration of Matthew's job with Hazel as far as he knows now. A draft (likely a ^report in the performance series, measured, with its data embedded; noise floor and baselines stated). 2026-10-15T19:15: DRAFT ready for Matthew's review: ^report(15) 'Dress Up and its livelits, side by side', http://localhost:8131/series/015/ (local commit 54c2f21 in ~/hazel-perf-report, not pushed). Measured this evening, headless Firefox 157, 8 drags of 1.5 s per set-up: Dress Up 0.0.48 never froze (longest gap 17 ms; 33 ms in the witch's woods), picture final at release; #2635 at 032fe3aca6: Kids' Choice froze 0.11 s, final 0.13 s after release; Full Body froze 2.9 s (whole program) / 3.2 s (UX), final 1.1 / 1.2 s after. Per change: Dress Up 1.4 ms, Kids' 225 ms, Full Body 1,010 ms (profiled). Then-and-now figure, and a labelled guess for Sept 2027 (Kids'-sized 30-60 ms, Full Body 100-300 ms) with the steps it rests on. Took about 23 min (18:52 to 19:15).from Matthew, 2026-10-15 |
^ticket(119) | dress up | done 2026-10-15 | Published 18:22: matthewhammer.org/hazel/dress-up/ (explainer: livelit prototyping + a creative place with M and N), music.html (newest day's tunes, player with list/repeat/shuffle, Media Session, #tune links), pictures.html (day select, group chips, viewer with swipe/arrows, PNG/SVG/settings links, link to the full raw lists). Archives gained days.json and 480px WebP thumbs (thumbs.mjs, run by archive-today.sh). /hazel/ links all three. Pages runs green, live check 18:27. ⏱ opened 2026-10-15 17:27, closed 18:27 (61 min) · 21.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~590 chars (~147 tokens) · ~0 min together | Pages for Dress Up under matthewhammer.org/hazel. Matthew, for later tonight: (1) a page explaining Dress Up as a prototyping platform for livelits, and a creative place for playful experiences and feedback from real children: N and M, and maybe someday their friends and family (cousins, etc.); (2) the latest MP3 track list, with an in-browser player and a track selector; (3) a nice-looking way to view the pictures in the archive, with a link to the full, raw list Matthew has been reviewing. (The archives: the public dress-up-music and dress-up-pictures repos, by day, ^ticket(106).) 18:06: Matthew pulled it in from tonight's long runs: "let's pull in ... ticket-119 next." from Matthew, 2026-10-15 |
^ticket(118) | dress up | done 2026-10-15 | Released as Dress Up 0.0.55 at 20:34 (b237b6d, tag dress-up-0.0.55; Pages green, live check 20:35). As the 17:09 prototype found: the fumes' stroke-width and stroke-opacity (CSS properties) leave SMIL for Web Animations started at the picture clock's phase (startHelix after a mount; helixSync pauses with a drag and resyncs on letting go); x1/x2 stay SMIL. Headless Chromium idle, 2 s: witch's hut 27 -> 119 frames (long tasks 2.0 s -> 0), woods 34 -> 86 (1.3 s -> 0); SMIL counts 326 -> 170, 463 -> 255. Drags (drag.mjs WARM AFTER): hut 1.73 -> 1.22 s, woods 2.60 -> 2.37 s, 0 rebuilds. Widths/opacities equal to the SMIL version within 5% at four clock times. Headless Firefox was 60 fps idle before and after. Not measured on the iPad (Safari). ⏱ opened 2026-10-15 17:03, closed 20:35 (213 min) · 7.0 min of work, timed · 3 messages from Matthew, 0 questions to him · ~180 chars (~45 tokens) · ~7.0 min together | The witch's woods and the witch's hut are heavy even when idle. Found while fixing ^ticket(117): with nothing being dragged, the witch's woods run about 460 SMIL animations and the witch's hut about 330 (the next heaviest place has 145). In the software-only test browser they draw at 10-15 frames a second idle (every other place but our imagination holds 60), with long tasks most of the time: 1.8 s of every 2 s in the woods. This was already so in 0.0.32 (10 fps), so it is not a regression. Most of the main thread goes to restyling the animated elements each frame (a trace showed ~200 style recalcs a frame). It may stall the music in those two places even without a drag. To do: measure on Matthew's laptop's real browser too, find which drawings carry the animations (the cauldrons' double-helix fumes and bubbles are the likely bulk), and draw the same look with far fewer animated elements (e.g. one animation moving a group instead of one per particle), keeping every picture the same at rest. Tools: ~/du-shots/tools/idle.mjs and drag.mjs. 2026-10-15T17:10: Matthew: come back to it tonight, after hours (a 🌙 long run).from Matthew, 2026-10-15 (from 117) |
^ticket(117) | dress up | done 2026-10-15 | Released 0.0.45 (295f402). Cause: since 0.0.35's LFOs, lfoBegin timed each scene animation from the picture's SMIL clock minus the music's phase, read afresh at every draw; the picture's clock moves once a frame and the music's continuously, so the begins wobbled past their rounding, the scene's text changed on every draw, and the whole scene was rebuilt on every move of a drag (each slow frame widening the wobble; 0.0.24's drag pause froze the picture's clock outright). Fix: picGapNow() keeps the gap between the two clocks (re-measured only when it moves by more than 0.25 s: music starting, stopping, seeking; never while paused), and letting go of a drag moves the picture's clock on by the time held. Measured with real 60-move drags in every place, music playing: 0 rebuilds (0.0.44: one per move); imagination 20.6 -> 3.4 s, witch's woods 5.0 -> 2.3, living room 6.7 -> 1.6, witch's hut 7.8 -> 1.8, tree house room 3.8 -> 1.1, every other place at 60 fps; live game checked; sweep: all 82 pictures identical. Not changed: the witch's woods and hut are heavy even idle (460 and 330 SMIL animations; 10-15 fps in the software-only test browser since at least 0.0.32). ⏱ opened 2026-10-15 16:29, closed 16:58 (29 min) · 26.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~190 chars (~47 tokens) · ~0 min together | Dragging the friend while the picture animates grinds, and the music freezes. Matthew, after 0.0.44: a resurfaced UX issue: dragging the main character while the animations continue does not work; the experience grinds down and the music freezes. (0.0.24 had fixed a drag that crawled past the cauldron by holding the scene's animations still during a drag and redrawing only the friend.) from Matthew, 2026-10-15 |
^ticket(116) | dress up | done 2026-10-15 | Released 0.0.44 (1d1889a). Doors: the hallway's and the dining room's stand closer (a pair to each span, packed toward us, torches in the gaps); every sign at least 0.8 of its size at the corridor's mouth, a farther one raised above a nearer one where they would overlap; at 1280x720 the far signs went from 6-10 px text to 16 px, none overlapping. Layout: the whole window (no 1100px cap), the picture fitted to its column at its own shape (1280x720: 542x402 -> 849x628), caption and buttons in one row under it, title atop the controls, a draggable divider (remembered; double-click = best fit; arrow keys), Save as a menu. Checked at 1280x720, 1920x1080, 1024x768 and a phone, and in the live game; sweep: only the hallway and dining pictures changed. ⏱ opened 2026-10-15 16:07, closed 16:19 (13 min) · 10.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~560 chars (~140 tokens) · ~0 min together | Doors closer, signs bigger, and the screen used to the full. Matthew and M, after 0.0.43: (1) the doors in the hallway and the dining room are too "set back", and the labels above them too small, especially when not full screen. (2) Optimize the viewport: use the screen to the full (the scene, or the controls) with no wasted margins around either. (3) The save options go into a submenu of their own, to save space on the left. (4) Above all the left side, the picture: offer a way to see it as large as possible at all times. (5) Ideally a draggable boundary between the left (the picture) and the right (the controls). from Matthew and M, 2026-10-15 |
^ticket(115) | dashboard | done 2026-10-15 | The 🪷 blocked on Matthew tag (his pick of the lotus, "pur-fect"): a teal chip on the ticket, in the queue and the outline, and a category of its own at the very end of the queue, after the 🌙 long runs; QUEUE_GROUPS in update.py orders the categories. Tagged: ^ticket(102). Under VERSIONS.md Unreleased; the practice is in agent-instructions.md.⏱ opened 2026-10-15 15:58, closed 16:00 (2 min) · 4.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~199 chars (~49 tokens) · ~1.0 min together | A 🪷 blocked on Matthew tag, apart from 🌙 long run. Matthew: another tag, "blocked on Matthew's response", with a peaceful emoji (🪷 a lotus; 🕊️ or 🍃 the alternatives offered). Tagged tickets get a 🪷 chip, and sit in a category of their own at the very end of the queue, after the long runs. First tagged: ^ticket(102). Under VERSIONS.md Unreleased. Matthew on 🪷: "Pur-fect."from Matthew, 2026-10-15 |
^ticket(114) | dashboard | done 2026-10-15 | The 🌙 long run tag, on the dashboard (Unreleased in VERSIONS.md): data.json tickets carry "tags"; "long run" is a chip on the ticket's row, the current ticket's box and the queue, a 🌙 in the outline, and the long runs at the queue's end sit under a "Long runs" line. Tagged: ^ticket(99), ^ticket(106), ^ticket(104). The practice is in agent-instructions.md.⏱ opened 2026-10-15 15:52, closed 15:55 (3 min) · 3.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~140 chars (~35 tokens) · ~0 min together | A 🌙 long run tag for tickets to do while Matthew is away. Matthew: "Maybe the dashboard could have a 'long run' tag where I tag tickets to run when I am AFK." Tickets get a tags list in data.json; "long run" shows as a chip on the ticket and in the outline, and the queue shows the long-run ones together at its end (the work to pick up when he steps away: archive renders, music renders, big sweeps). First tagged: ^ticket(106), ^ticket(104), ^ticket(99) (his "tonight" list, 2026-10-15). Under VERSIONS.md Unreleased.from Matthew, 2026-10-15 |
^ticket(113) | dress up | done 2026-10-15 | Released 0.0.43 (fcba6f4): a hallway can have up to 4 doors a side (8; Matthew's "maybe even 4"), the dining room 3 a side (6, M's request), and the dining room a door in its far wall left of the fireplace (nowhere by default; the designer can use it). With far and near walls: 10 ways out of a hallway, 8 out of the dining room. Each room's slider stops at its own most (king's room 4). 82 of 82 default pictures unchanged; checked in the live game, no errors. ⏱ opened 2026-10-15 15:48, closed 15:57 (10 min) · 6.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~290 chars (~72 tokens) · ~7.0 min together | Eight ways out of the hallway and the dining room (M's request). M: the hallway should be able to have 3 doors in each side wall, and the dining room the same: 8 ways in all in those rooms, counting the far wall and the near wall (the one we can't see). So: up to 6 side doors (3 a side, was 4 in all), plus the far wall (the hallway's stairs; the dining room gets a door in its far wall, by the fireplace) and the near wall (the hallway's ⬇️ door behind you, ^ticket(112); the dining room's way out toward us). Defaults stay 3 doors, so no picture changes until a slider moves. Matthew: mint and do asap; it goes before 92's entrance hall. Matthew, 15:55: "Or maybe even 4 doors on the L and R sides of the hallway?" Yes: the hallway's corridor has four stretches, a pair of doors to each, so the hallway takes up to 8 side doors (10 ways in all); the dining room 3 a side as M asked (8 ways).from M via Matthew, 2026-10-15 |
^ticket(112) | dress up | done 2026-10-15 | Released 0.0.42 (20dee4f): ⬇️ a door behind you. In a castle hallway, the trap room and the greenhouse, an optional way at the bottom of the picture (castleBack, nowhere by default), a sign toward us when set; the living room and each tree house room too, to any place. The castle designer uses it: castle N's one-way greenhouse archway into the trap room, which had no fix in 0.0.41, is fixed by a door back from the trap room. 82 of 82 default pictures unchanged. The laptop crashed at 15:33 mid-ticket; the edits survived on disk and were finished and checked after the restart. Work time reconstructed: about 2 min before the crash, 5 after. ⏱ opened 2026-10-15 15:32, closed 15:43 (12 min) · 7.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~440 chars (~110 tokens) · ~0.0 min together | ⬇️ A door behind you: an optional exit at the bottom of each room. Matthew: the bottom of each room (the living room, maybe the tree house's rooms, and all the castle's rooms) should imply a door we can't see, especially hallways; offer an optional connection there to another room. It helps the castle designer solve its constraints (a way back where a room has no other free exit). Then carry on with the other rooms (92: the entrance hall). from Matthew, 2026-10-15 |
^ticket(111) | dress up | done 2026-10-15 | Released 0.0.39 (ad81493): the greenhouse's back wall holds 6 archways, its new stone side walls (70% high, coursed in perspective, glass above) hold 4 doors, and any more are signs at the bottom. The sweep differs only in the greenhouse; live checked. ⏱ opened 2026-10-15 15:04, closed 15:08 (5 min) · 5.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~140 chars (~35 tokens) · ~1.0 min together | More places for doors (the greenhouse). Matthew, with a screenshot of a 13-room castle's greenhouse, its archways piled up along its back wall: we need more places for doors. Then: add walls on the left and right, and a few possible doors (signs) at the bottom, if need be. from Matthew, 2026-10-15 |
^ticket(110) | dress up | done 2026-10-15 | Released 0.0.38 (b6ea439, tag dress-up-0.0.38): 🍽️ the dining room, the first of 'M's rooms'. A long hall drawn down its length like the hallway. A table set for 4 to 18 ('🍽️ places at table'; one at each end, up to 8 a side) with cushioned chairs, plates, gold cups and cutlery, candles down the runner, candelabras hanging from the vault, torches and a fireplace. Flames on the quick LFO, glows on the slow. The sweep adds only its 2 pictures. Live: draws with 0 errors. ⏱ opened 2026-10-15 14:43, closed 14:58 (16 min) · 7.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~280 chars (~70 tokens) · ~1.0 min together | 🍽️ A dining room, a new castle room (M's idea). A long table down the length of the room, drawn in perspective like the hallway, with "places at table" as a setting: 4 to 18 (up to 8 down each side, and one at each end). Detailed: torches on the walls, candles on the table, and candelabras (chandeliers of candles) hanging down over it. from M, through Matthew, 2026-10-15 |
^ticket(109) | dress up | done 2026-10-15 | Released 0.0.37 (9e51b1c, tag dress-up-0.0.37). Every non-hallway room has its own way out ('↩️ its way out leads to', default 'the usual way': out only from room 1). Castles go up to 13 rooms. The 🗺️ castle designer uses Cytoscape.js 3.30.4 (cdnjs, SRI, loaded on first open): a map of rooms and ways, tap to change where a way leads, two-way doors by default, problems listed with fixes that say what they do, and 🔧 fix everything (17 → 1 on a 13-room castle; what remains is the greenhouse's fixed arch into the trap room). Live check: designer loads, 0 errors. ⏱ opened 2026-10-15 14:37, closed 14:50 (13 min) · 13.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~640 chars (~160 tokens) · ~0 min together | Castle designer, per-room ways out, 13 rooms. Matthew: (1) every castle room with a way "out" (the king's room's sign at the bottom, the dungeons' stairs, and so on) gets a per-room setting for where it leads; by default only room 1 leads outside. (2) An MVP "castle designer" panel that shows how the rooms connect, as nodes and edges, with a stable, simple JS graph package (I chose Cytoscape.js). (3) The designer offers to correct topological anomalies, and doors are two-way (symmetrical) by default. (4) Up to 13 castle rooms. Complete ASAP and release. from Matthew, 2026-10-15 |
^ticket(108) | dress up | done 2026-10-15 | Released 0.0.36 (0b0948e, tag dress-up-0.0.36). With the stairs set to '🧱 nowhere', the hallway ends in a torchlit stone wall whose courses meet the side walls' at the corners, with no arch or stairs. The sweep matches 0.0.35 exactly (74 pictures, since the default still leads to the stairs). The live page checked cache-free: wall present, 0 errors. (Times are rebuilt from the screenshot's file time and the commit time: I had guessed them at first.) ⏱ opened 2026-10-15 14:27, closed 14:31 (4 min) · 4.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~170 chars (~42 tokens) · ~0.0 min together | A hallway whose stairs lead nowhere ends in a wall. Matthew: when "🌀 the stairs at its end lead to" is "🧱 nowhere", remove the stairs and put an actual wall there. Released at once, for M. from Matthew, 2026-10-15 |
^ticket(107) | dress up | done 2026-10-15 | Released 0.0.35 (23e994a, tag dress-up-0.0.35). The Pages run succeeded, and the live page (http) loads cache-free with 7 LFOs, the moving tree-house windows, the five funked waltzes and 0 page errors. The agent instructions went public with it, on Matthew's OK. ⏱ opened 2026-10-15 14:12, closed 14:21 (9 min) · 8.0 min of work, timed · 2 messages from Matthew, 1 questions to him · ~560 chars (~140 tokens) · ~2.0 min together | Release Dress Up 0.0.35, now. Matthew: every new Dress Up feature gets pushed and made public with a version bump, as soon as possible, ahead of the archive work. M is home and wants to see today's progress. from Matthew, 2026-10-15 |
^ticket(106) | dress up | done 2026-10-15 | Done 2026-10-15: repos matthewhammer/dress-up-pictures and dress-up-music (public, Pages), by day, unchanged files as symlinks to the earlier day's; tools in each repo (archive-today.sh makes a day, with thumbs and days.json since 119); browsable (index pages + the /hazel/dress-up/ music and pictures pages). Music day 2026/10/15 (28 tunes, 0.0.42) published 16:06; tonight the pictures day was re-made on 0.0.55 (20:36-20:46, 563 pictures planned, 323 distinct, 0 page errors, 306 MB) and pushed (048e549), live 20:49. ⏱ opened 2026-10-15 14:12, closed 20:50 (398 min) · 16.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~570 chars (~142 tokens) · ~0 min together | Archive repos for Dress Up's pictures and music. Two new repos just for the archives, apart from the game's: one for the dated screenshot archive (every background, with and without the friend, and every chip menu, as PNG + SVG + settings JSON), one for the dated music archive (every tune, one loop at defaults, as MP3). They will hit storage limits before anything else does, and can move to other storage later. Move the existing data in (2026/10/15), make each browsable, and point the tools at them. Matthew: a way to look back at all prior versions, in pictures and sound, is very important to him. 2026-10-15T15:52: Matthew: to the bottom of the queue; do it tonight, while M and N aren't giving us suggestions. 2026-10-15T16:06: the music day 2026/10/15 is complete and published (e0d5474): all 28 tunes, at http://matthewhammer.org/dress-up-music/2026/10/15/index.html. The 15:33 crash had left vibes.mp3 empty but listed as done; it was moved aside (~/du-shots/crash-2026-10-15/) and re-rendered. Left for tonight: today's pictures day is still 0.0.36's (made 14:36), so rerun it on the latest game; and the rest of this ticket. from Matthew, 2026-10-15 |
^ticket(105) | dress up | done 2026-10-15 | Committed locally as ea85125, not pushed. Inside the tree house: the limbs are bare and still and the sign hangs still. Each window shows a limb with branches and leaf pads, swaying with E while its leaves flutter with D. The sweep now covers all 6 tree rooms (74 pictures): only those 6, plus the imagination room's picture of a tree room, changed. A VERSIONS entry and the 'wind is outdoors' design rule are added to the agent instructions. ⏱ opened 2026-10-15 13:55, closed 14:04 (10 min) · 9.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~330 chars (~82 tokens) · ~0 min together | Inside the tree house, the wind is outside. Matthew: there shouldn't be many small limbs inside, since they need sunlight and it's not outside, and the wind is outside, not inside. So: fewer limbs in the rooms, and still: no fluttering leaves indoors (and the hanging sign no longer swings). Add limbs seen through the windows, with branches and the tree house's leaf pads, the limbs swaying with the slow LFO and their leaves fluttering with the quick one. from Matthew, 2026-10-15 |
^ticket(104) | dress up | superseded 2026-10-15 | Covered by ^ticket(106) and ^ticket(119): every tune, the five funked waltzes included, at its default settings, one loop each, as an MP3, listed by its in-game name, is in https://matthewhammer.org/dress-up-music/2026/10/15/ (28 tunes) and plays in https://matthewhammer.org/hazel/dress-up/music.html. Nothing was added to the game's repo. Reopen if the MP3s should also live in kids-choice-games.⏱ opened 2026-10-15 13:51, closed 20:50 (419 min) · 2.0 min of work, timed · 2 messages from Matthew, 0 questions to him · ~380 chars (~95 tokens) · ~0 min together | Every tune as an MP3, in the repo. Every Dress Up tune, the five funked waltzes included, rendered at its default settings for one loop each, as MP3s in dress-up/music/ with a page that lists them by their in-game names. Matthew: 'Let's release them as MP3s somewhere, perhaps in the repo. Let's do the same for the other tracks, at default settings --- one loop length each.' Size is checked before committing (a public repo; a push deploys). Nothing is pushed until Matthew says. 2026-10-15T15:52: Matthew: to the bottom of the queue; do it tonight, while M and N aren't giving us suggestions. from Matthew, 2026-10-15 |
^ticket(103) | dress up | done 2026-10-15 | Today's archive is at ~/dress-up-screenshots/2026/10/15/ (local, 321 MB), browsable at localhost:8133/screenshots/2026/10/15/index.html. It holds 306 distinct drawings, each a .png (1920 wide), an .svg (animations and all) and a .json (its settings, in the game's save format: Load opens it). They are named ^ticket(99)'s safety net; both are local, pending the question of a tools/ folder in the repo. REVISED after Matthew's review (13:00–13:15), still at the same address:
⏱ opened 2026-10-15 12:16, closed 12:30 (14 min) · 10.0 min of work, timed · 5 messages from Matthew, 1 questions to him · ~2,070 chars (~517 tokens) · ~11.0 min together | An archive of Dress Up screenshots, by year/month/day. Each run makes a dated folder holding, with every other setting at its default:
^ticket(99)'s safety net (the sweep that hashes 69 pictures), and before-and-after pictures for the refactor and the drawing-fault fixes.from Matthew, 2026-10-15 |
^ticket(101) | dress up | done 2026-10-15 | Keep all five funked waltzes (Matthew, 14:00: 'all of these variations of the funked waltz are each good. Let's keep them all.'). B was 'pur-fect' and C 'really really' liked. They stay in the game as A-E (83019d0); the MP3s of every tune are ^ticket(104).⏱ opened 2026-10-15 11:47, closed 13:51 (125 min) · 15.0 min of work, timed · 8 messages from Matthew, 1 questions to him · ~1,715 chars (~428 tokens) · ~16.0 min together | A new tune: the spooky waltz, funked. Based on the spooky waltz at 205 beats a minute, moved down 4 half steps, keeping all its elements: the theremin melody, the vamping and the rest. To it, add drums like the bongo breakdown's, and a 4/4 funk beat with more percussion under the waltz's melody and vamping. Open question for Matthew: does the waltz stay in 3 against the funk's 4 (four waltz bars to three funk bars, meeting every 12 beats), or is the melody re-set in 4/4 over the beat? It goes under Unreleased with its own entry, its levels checked by an offline render at 22 kHz or more, and an MP3 preview. Nothing pushed. Also (Matthew, a minute later): a very wah-wah, buzzy, plucky bassline pair. Plan, unless Matthew says otherwise: the waltz stays in 3 over the funk's 4. Four waltz bars take as long as three funk bars, so each 8-bar section of the waltz holds exactly 6 funk bars and they line up again at every section. The funk plays half-time at 205, the snare once a bar and the hats on eighths. REOPENED 12:00: Matthew finds the 4/4 groove does not line up with the waltz: the big beats should land together. He asks for up to five variations, A to E, to try. MATTHEW, 13:31: 'funked waltz B is Pur-fect. A was hard to dance to, but I don't want to discard any option yet. Give me 24 hours or more to adjust to them, and play with them.' So all five stay in the game, unchanged, and nobody asks before 2026-10-16 13:30 at the earliest. Then he picks. MATTHEW, 13:36: 'I really really like funked waltz C.' (So far: B pur-fect, C really really liked, A hard to dance to. All five stay.) from Matthew, 2026-10-15 |
^ticket(100) | dress up | done 2026-10-15 | Committed locally as 57f0b9a (with d593d3b's share), not pushed. There are now seven LFOs, A to G. D (quick, 1 beat) and E (slow, 4 bars) are the scenes' own; F (1/4 beat) and G (1/16 beat, a blur) are for wings. Three scene choices pick which LFO quick things, slow things and wings follow, or their own pace. Every scene and castle room has at least one quick and one slow thing, each its own way into the wave, all 33 variants checked by count. Mostly via shared drawings: clouds, the sun's rays, stars, torches, sunbeams. Plus sceneLfo, lfoTimed, swaying, and butterflies (six places, wings on F, bobbing on D). New: the living room's wall clock. Each part's key carries its LFO, so a change on the mixer redraws only its scene. SCENE_LFO_THINGS names them on the mixing board. Also fixed a released bug: the fairy house ignored its LFO's changes. ⏱ opened 2026-10-15 11:42, closed 12:15 (33 min) · 20.0 min of work, timed · 6 messages from Matthew, 0 questions to him · ~1,805 chars (~451 tokens) · ~5.0 min together | Five LFOs, and every scene moving with two of them. Add LFOs D (a faster one, 1 beat to start with) and E (a slower one, 4 bars), reserved for scene animations only as defaults: anyone can set them to anything, and anything can follow any LFO. The cat room's tails default to D and the stable's nods to E, each animal still with its own offset. Then go through the scenes and give each at least two things to move with them: something fast on D (flapping, pecking, flickering, wagging) and something slow on E (swaying, breathing, drifting, glowing). Things side by side get their own offsets, never in lockstep. One list says what in each scene follows which LFO. It drives the mixing board's 'what follows it' and the redraw when an LFO changes; this is the first piece of ^ticket(99)'s LFO-followers registry. Under Unreleased; nothing pushed. ADDED (Matthew, 12:10): LFOs F and G, under a beat, for wings: F a normal-looking wingbeat, G a faster one; an odd number of LFOs keeps the mixing board balanced, with the main strip first.from Matthew, 2026-10-15 |
^ticket(99) | dress up | done 2026-10-15 | Released as Dress Up 0.0.46 (18:03, live). (a) The friend is drawn by depth during and after a drag: every standing thing in every place is a depth object, sorted when shown, and the friend goes behind whatever stands nearer than their feet; pictures and window views paint in the same order. (b) The formal list: dress-up/OBJECTS.md, 39 places and 371 objects, made from the depth sheets; each place lists its objects in its background tab as compact 👁️/🙈 chips with 🧍 where the friend stands, saved per place. The light and dark of a place are front objects, so the friend is always under them (the camp at night is a pool of firelight). (c) Elbow and knee sliders; arms and legs drawn through their joints, sleeves, pants, stockings and skirts following; identical at 0 (costume sweep unchanged). Added on the way: the friend smaller farther back, a factor on top of the size slider and the place's own size. Drags: no scene rebuilt in any place, all as fast as 0.0.45 or faster. Done with six parallel helper agents (about 147 agent-minutes) plus 54 minutes of wall clock, 17:09 to 18:03; it was tagged as an overnight long run. Left for later: each limb posed on its own (121), NPCs sized by depth, tree house rooms' side doors still painted after the furniture. ⏱ opened 2026-10-15 11:33, closed 18:06 (393 min) · 54.0 min of work, timed · 9 messages from Matthew, 1 questions to him · ~5,575 chars (~1,393 tokens) · ~12.0 min together | Recovery: make Dress Up's routines modular and composable, then write down the recipes. Matthew offered this on 2026-10-15: a ticket, and a chance within it, to refactor, and some space to recover after the creative run. It is self-directed. My plan, in order: (1) A safety net first. Extend the byte-identical check, which hashes scene markup in the committed copy and the working copy, to sweep every place, every castle room style, every costume and the imagination map, under fixed settings. Also time drags in three heavy scenes before starting. No step lands unless the sweep comes out identical. (2) Room space. One shared helper (at(X, Y, z), heights on the wall, shelves on the back and side walls, things on the floor, side doors) used by the hallway, the king's room, the cat room and the stable, in place of their copies. (3) Residents. One declaration per kind: the cat room's cats, the stable's horses, and next the kitchen's workers and the smith. From it come the settings keys, the defaults, the castleOf entry, the controls group, the show-when rules and the LFO listing. This is where ^ticket(92)'s cast meets it. (4) LFO followers. One registry of what follows which LFO and where it shows, driving mixChanged, the mixer's 'what follows it' list, and the scenes' LFO reads (sceneOpts). A new follower then cannot miss one of them, which is how the fairy house's bug happened. (5) Room styles. One entry per style: its label, drawing, exits, tone, theme and own settings. (6) The recipes, in agent-instructions.md: how to add a place, a castle room, a resident and an LFO follower, each naming its registry. Notes on single functions stay in the code. Out of scope: changing anything a player sees; renaming settings keys (saved friends must still load); the music engine. Each step is its own local commit, noted under Unreleased as a refactor rather than a feature, and timed. Nothing is pushed. ESTIMATE 45-90 min: refactoring with verification runs slower than drawing. It starts after ^ticket(93)'s stable; ^ticket(93)'s kitchen and smithy then build on step (3). Matthew will read it afterwards. ADDED 2026-10-15 (Matthew and M): a pass over drawing faults: lines past their shapes, lines ignoring the perspective, things hanging from nothing. A code scan found about 30, listed in ~/du-shots/drawing-faults-2026-10-15.md (local). The top ones: the castle rooms' and greenhouse's floor lines run up the walls; the tent's mesh window (Matthew's 'vent') ignores the perspective; the cat room's window leads cross its arch; the fairy wings' veins leave the wings; the tree room's lantern hangs from nothing. Check each on screen, using the routine now in the Dress Up agent instructions, and fix them; M's lantern catch is fixed already (3616b95). 2026-10-15T15:52: Matthew: another one for tonight (bottom of the queue). 2026-10-15T17:09: Matthew: new priority, now (not tonight). And at the same time: (a) during a drag, respect the scene's depth order: the layer order that stands for depth into the pseudo-3D scene, so the friend (or any NPC) dragged behind a thing is drawn behind it; (b) a formal list of every scene object that should be drawn on top of the main character, or any other NPC, when they are placed at a given position on the standable area. 2026-10-15T17:12: Matthew: one more constraint on this ticket: (c) elbows and knees for the main character, like the other (non-main) characters that have elbows. No knees anywhere yet; they are wanted for animating more human motions: walking, running, climbing, crawling, swimming, dancing. 2026-10-15T17:14: Matthew: to make (b) reviewable, each background provides its list of objects in the game, each with a visible (👁️) toggle to turn it on and off; compact chips, since the lists could be long. Added 17:59: Matthew: "let's add a depth constraint to the size as shown -- indep of the setting chosen by the user (as another factor on top)." and "each scene already should have a setting for the 'default main char size'" (SCENE_FIG). from Matthew, 2026-10-15 |
^ticket(98) | dress up | done 2026-10-15 | DONE 2026-10-15: D's castle starts as D chose it: 5 levels, stone #7c8b6f, 16 trees, moat 72, flags #6a2c91 and #22c55e in stripes, purple roofs #7c3aed; each castle now has its own default roof and flag style. Local commits 66be20b, a9f497c, not pushed. ⏱ opened 2026-10-15 10:48, closed 10:57 (9 min) · 1.0 min of work, timed · 3 messages from Matthew, 1 questions to him · ~695 chars (~173 tokens) · ~2.0 min together | D's castle's defaults, as D chose them (Matthew's screenshots, 2026-10-15): stone #7c8b6f, 16 trees, moat 72, flags #6a2c91 and #22c55e in stripes, roofs purple #7c3aed (his correction). Roofs and the flags' style were one default for every castle; now each castle has its own. Levels 5 (his answer, 2026-10-15). Done, local commit with ^ticket(97).from Matthew, 2026-10-15 |
^ticket(97) | dress up | done 2026-10-15 | DONE 2026-10-15: gems sparkling over the cave's walls (0-40) and on its crystals, with their own star settings; the waterfall's speed and time of day (day, sunset, night), its own, so the jungle waterfall and the cave's view out agree; its width shown in the cave. Checked in a muted headless browser (the cave and the falls at night). Local commit 66be20b, under Unreleased, not pushed. ⏱ opened 2026-10-15 10:46, closed 10:57 (11 min) · 2.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~1,230 chars (~307 tokens) · ~3.0 min together | The cave behind the falls: gem sparkles; the waterfall's width, speed and time of day. Matthew, 2026-10-15. BUILT, timed 10:46:45-10:49 (with ^ticket(98) inside it): gems over the cave's walls (0-40) each with a sparkle drawn by starsSvg, and sparkles on the crystals, with their own shape, size, brightness and twinkle; the waterfall's speed (fallRate) and time of day (fallTime: day, sunset, night), its own settings, so the jungle waterfall (a tint, the moon at night) and the cave's view out (its light by the time, the moon behind the water) agree; its width shown in the cave too. Local commit in ~/kids-choice-games, under Unreleased. Waits for Matthew to try.from Matthew, 2026-10-15 |
^ticket(96) | dress up | done 2026-10-15 | DONE 2026-10-15: 💭 our imagination, first after none: a white room, its far wall a map of every place (a column to each kind, the places as framed pictures joined by a dotted path), each picture a way there. Fixed with it: a picture's own signposts and doors were live (the camp site's picture went to the trail); now the whole picture is the way in, the living room's too. First draw ~270 ms, again ~5 ms. Local commit d3e753b, not pushed. ⏱ opened 2026-10-15 10:39, closed 10:57 (18 min) · 2.2 min of work, timed · 2 messages from Matthew, 0 questions to him · ~1,640 chars (~410 tokens) · ~2.0 min together | 💭 Our imagination: a room with a map of every place. Matthew, 2026-10-15: a background like a big 2D map on the back wall, each place a picture frame on it, in a very simple room (the blank white of the Matrix's construct); go there, and choose any other place with the game's own controls. First after "no background", named "our imagination". BUILT, timed 10:39:24-10:41:37 (2.2 min): a white room, its far wall a map, a column to each kind of place (PLACE_GROUPS) with its name, its places down it as framed pictures (framedSvg, detail 45) joined by a dotted path; a picture of the room holds only frames. First draw 254-278 ms, again 5-6 ms. Fixed on the way: a picture's own signposts and doors were live, so a click on the camp site's picture went to the mountain trail; now a picture's inner doorways are paint (the living room's pictures too). Local commit in ~/kids-choice-games, under Unreleased. Waits for Matthew to try it. from Matthew, 2026-10-15 |
^ticket(95) | dress up | done 2026-10-15 | Drafted as kids-choice-games/agent-instructions.md, committed locally (c80f9c3), NOT pushed: the repo is public, so it waits for Matthew's read. It covers who decides (M and N through Matthew), push = deploy and the release steps, his design calls, a map of the one-file game (TABS control kinds, the scene registries, part() caching, the castle model, music), testing without sound or load, and working with him. It ends with three open questions for him: public or not, a tools/ folder for the test drivers, and a README link. At 11:03 Matthew added that keeping the file in sync with the code and the design process is part of it: now its opening rule (2d827b1). ⏱ opened 2026-10-15 10:29, closed 11:02 (34 min) · 4.3 min of work, timed · 3 messages from Matthew, 1 questions to him · ~1,950 chars (~487 tokens) · ~7.0 min together | An agent-instructions.md for the Dress Up repo (matthewhammer/kids-choice-games), drafted once Matthew is happy with the castle designer (^ticket(94)), for him to read before it is pushed: the repo is public. To gather what lives now only in the work notes, our conversations and code comments: his rules (connected views draw from the place's own settings; his bar for a scene's detail, the tree house's and the witch's woods'; shared organic helpers, never lollipops; the kids only as M and N), the release cadence (changes under Unreleased in VERSIONS.md, no bump or push until he says, tags dress-up-<version>, node --check before a push, checking the live page cold after), testing (headless Chrome with --mute-audio, contact sheets, never leaving browsers running), the castle's design (rooms by number, each with its own exits), the LFO rules (fast ones blur), timing work and keeping ticket metrics, and how the code is organized (SCENES, SCENE_FIG, PLACES, TABS, SHOWN_WHEN, DEFAULTS, parts and their keys).from Matthew, 2026-10-15 |
^ticket(94) | dress up | done 2026-10-15 | DONE 2026-10-15 (Matthew: "the new castle editing UX is better"): a castle is rooms by number, each hallway and king's room with its own exits (0-4 doors, each to the outside or a room by number, its own too; a hallway's stairs); the king's room's doors in its side walls, its way out a sign at the bottom; go-to chips; the castle's settings in dashed groups, only the current room's inside it, marked; room choices only for rooms the castle has, each with its room's emoji; two color themes per castle (A, and B a swapped copy), each room in its own. Local commits 31896c0..9146c61 in ~/kids-choice-games, under Unreleased, not pushed. ⏱ opened 2026-10-15 10:27, closed 10:57 (30 min) · 11.6 min of work, timed · 9 messages from Matthew, 1 questions to him · ~9,180 chars (~2,295 tokens) · ~17.0 min together | Castle rooms by number, each with its own exits. Matthew, 2026-10-15: a castle is numbered rooms; each room has a type and its own exits, each to a specific room by number (so two hallways can connect yet have distinct doors to distinct rooms), the outside, or nowhere. For a hallway: its number of doors (0-4) and each one's destination, and where its stairs lead. Chips to go straight into each room by number (for testing a castle), and dotted boxes round the groups of controls. Defaults reproduce today's links; other room types keep linking to every room for now; doors are one-way (the back chip always returns). Timed from 10:26:44. BUILT 2026-10-15, timed 10:26:44-10:29:54 (3.2 min; estimated 45-60): local commit 31896c0 in ~/kids-choice-games, under Unreleased. Checked in a muted headless browser: two hallways joined by their stairs, each with its own doors (room 1: out, 2, 3, stairs to 2; room 2: 4, 5, stairs to 1); the king's room still links to all; the dashed groups show only what applies; the go-to chips jump straight in. Known rough edge: a door's choices list rooms 1-6 even when the castle has fewer (a door to a missing room is not drawn). Open until Matthew is happy with the castle designer; then ^ticket(95). REVISED 2026-10-15, timed 10:32:19-10:36: door choices follow the room count; the king's room has its own doors (0-4, set like a hallway's) in its side walls in perspective, two to a side, the nearer door's sign above the farther's, clear of the throne, the banners and the suits of armor (moved against the back wall); its way out a sign at the bottom, as the clearing's path. Default doors 3 (out, and the next two rooms). Local commit after 31896c0. ALSO 2026-10-15, timed 10:36:46-10:38: inside a castle room only its settings group shows, marked (at the castle, all); the caption says where the friend is, in each place's own word (in, at, on, near, outside of; a castle room by name and number). Local commit after c3020a5. Matthew 2026-10-15: "the new castle editing UX is better". Then, timed 10:51:44-10:52: each room choice shows its room's own emoji, following its type (choices whose text follows the settings).from Matthew, 2026-10-15 |
^ticket(93) | dress up | done 2026-10-15 | Released 0.0.40 (cfe1d8b): 🍲 the kitchen (hearth, a cauldron on its tripod stirred by a cook in a frilled cap, a bread oven, plates and crocks, copper pots on a rack, flour and barrels, a worktable where a cook in a toque chops carrots) and ⚒️ the smithy (dark, sooty, the forge's breathing coals, hood and bellows, the smith hammering on the anvil with sparks at each strike, quench barrel, tools, swords, a shield). helperSvg draws the helpers, with poses on the quick LFO. The stable and cat room came earlier (0.0.35). The sweep adds only their 4 pictures; live checked, 0 errors. The helpers' own settings come with 92's cast. ⏱ opened 2026-10-15 10:15, closed 15:17 (303 min) · 13.0 min of work, timed · 7 messages from Matthew, 0 questions to him · ~1,690 chars (~422 tokens) · ~6.0 min together | More castle rooms: a kitchen, a blacksmith's, a stable, a cat room. Each with lots of detail (Matthew's bar: the tree house's and the witch's woods'). (1) A kitchen with two workers, a man and a woman, who look like castle helpers by default but can be set up like any friend. (2) A blacksmith's: a smith at a hearth, dark and very rustic. (3) A stable with horses, their heads animated somehow. (4) A cat room: a big, cozy wooden room with a fireplace, cat trees, and shelves and walkways joining all over the walls, with up to 8 cats, each with its own settings. The kitchen's workers and the smith are more of the cast in ^ticket(92) (figureSvg and a settings set per character), so they come after it, or start as their own drawings and move over. The cat room's cats can share the healer's cat's drawing. Each room is a castleRoomN choice, like ^ticket(92)'s hallway. Under Unreleased; no bump or push until Matthew says. ESTIMATE 2026-10-15, from ^ticket(92)'s timed hallway (11.5 min): each room ~15-25 min, the cat room's 8 cats' settings ~20-30 min more; ~1.5-2 h in all. Its kitchen workers and smith want ^ticket(92)'s cast first; the stable and the cat room do not. 2026-10-15T14:42: Matthew: once the castle designer MVP (109) is settled, expand the room types (this ticket) next.from Matthew, 2026-10-15 |
^ticket(92) | dress up | done 2026-10-15 | Closed on Matthew's word (2026-10-15 ~15:55, "92 is good. done with 92."). Shipped: (1) the long hallway, torches, carpet, banners, side doors, its far end's spiral stairs (0.0.35, with fixes in 0.0.36); (2) the spiral staircase, doors on its steps at four heights (0.0.41, 10b55e7). Not built: (3) the entrance hall with the king's statue, and the cast of friends (figureSvg, the people chip); only code reading had been done for them. Mint new tickets if they come back. ⏱ opened 2026-10-15 10:04, closed 15:50 (346 min) · 12 messages from Matthew, 1 questions to him · ~7,494 chars (~1,873 tokens) · ~19.0 min together | New castle rooms, and a cast of friends. Three castle room options (for castleRoomN, beside the ones the rooms have now): (1) a long hallway, dark and atmospheric (the fairy forest's night as the model), lit by glowing torches; no fireflies inside the castle (only outside); a carpet down the hall with a color setting; banners on the walls in the castle's banner colors (^ticket(72)'s flag colors and pattern). (2) a spiral staircase, with doors coming out at different levels. (3) the entrance hall, the first room: styled and decorated like the hallway, but square, with two large winding staircases facing the visitor and doors everywhere, and a statue of the king in the middle, between the stairs. The statue is another friend, with its own settings. Matthew's question: how hard is a set of friends instead of one main character (the king, the healer, the witch, more later), their geometry from one common set of routines and parameters, with a top-level chip to choose which character the tabs edit, each starting from its own defaults? Found 2026-10-15: draw() builds the friend straight from state (state.face, body, shirt, pants, hair, shoes, hands, undies, costume) mixed with the scene's composition; the controls read and write state[part][key] (38 places, one set()); the healer in her hut is drawn by her own separate code, not the friend's. Plan, in steps that each ship on their own: (a) figureSvg(person): the friend's drawing as a function of one settings object, draw() calling it with state, checked by drawing many presets before and after and comparing the markup, identical; (b) a cast: state.people with the king, the healer and the witch as overrides of the friend's defaults, and a top-level chip choosing whose settings the tabs edit (the background, music and other tabs stay shared); (c) using them: the king's statue (his figure in stone), the healer and the witch drawn by figureSvg (changes how they look now: Matthew to check); (d) the three rooms; the hallway and the staircase need nothing from (a)-(c). Under Unreleased; no bump or push until he says. Started 2026-10-15 10:05:01, timed: the hallway first (Matthew: "if you can do it in under an hour"; the whole ticket is about a day). Log times before 10:05 reconstructed from when the dashboard first published the ticket. HALLWAY DONE 2026-10-15, timed: 10:05:01-10:18:03 less a 1.5 min pause = 11.5 min of work, through three rounds of Matthew's notes (doors in the side walls in perspective; a fixed spiral-stairs archway at the far end; a setting for where it leads). Local commit e097fd5 in ~/kids-choice-games, under Unreleased (also fixes torch flames drifting off their torches). REMAINING, re-estimated from that (the hour per room was ~5x high): the spiral staircase room ~15 min; figureSvg(person), the refactor checked by identical markup, ~30-45 min; the cast and its chip ~20-30 min; the statue, healer and witch drawn by figureSvg ~20-30 min; the entrance hall ~15-20 min: ~1.5-2.5 h in all, before review rounds. Fixed after review: the far end set to lead to the hallway itself was not a doorway; now it is (a click flies the friend up the stairs and back), local commit after e097fd5. 2026-10-15T14:42: Matthew: this one too, after the castle designer MVP (109), alongside 93. 2026-10-15T15:22: part (2) done, released as 0.0.41 (10b55e7): the 🌀 spiral staircase, its doors on its steps at four heights; the hallway's default stairs now lead to it. (1) the hallway was done earlier (0.0.35). Left: (3) the entrance hall with its king statue, and the cast of friends (which the kitchen's cooks and the smith, 0.0.40, will join).from Matthew, 2026-10-15 |
^ticket(90) | performance | answered 2026-10-15 | Answered: views in the worker re-measured on #2635's head 032fe3aca6 (main thread about 540 ms less per change in UX mode and 220 ms with the whole program; the redraw no sooner; Kids' Choice about 45 ms later; a 1.5 s drag still stalls 2-3 s). Its numbers feed ^ticket(18)'s decision, and ^ticket(91)'s interleaved A/B tonight measures it again, drag by drag.⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Before parking ^ticket(18): re-measure views in the worker on #2635's head (032fe3aca6). Matthew's middle option, 2026-10-15. The prototype's numbers (main thread 540 ms less per change in UX mode, 220 ms with the whole program; the redraw no sooner; Kids' Choice +45 ms) were taken on 9fb2bdf067, before the autosave fix (^ticket(84)). Steps: (1) rebase the prototype (61d031fd93, a7fbe23788, 9c7808c51e, ede920b07e) onto 032fe3aca6; (2) A/B on Full Body, UX mode (B) and the whole program (C): release to redraw, main-thread work per change; (3) NEW: how long the page stalls during a real drag (frames, in the page, from the pointer going down to the redraw), the cost the release-to-redraw number cannot see; (4) Kids' Choice release, the small-livelit cost; (5) a recommendation: park ^ticket(18) or not. Local only; nothing pushed. First finding, 2026-10-15: on #2635's head, whole program (C), a real 1.5 s drag of Full Body's arm length takes 5.8-7.0 s from the pointer going down to the redraw, the page stalled (frame gaps over 50 ms) for 4.1-5.1 s of it, its longest single stall 0.8-1.1 s; release to redraw 1.3 s. The prototype is rebased onto 032fe3aca6 (local exp/2635-views-in-worker-032fe), measured next. Head measured (032fe3aca6, headless Firefox): C release 1.39 s, a 1.5 s drag stalls 4.1 s (longest 0.9 s); B release 1.0 s, drag stalls 2.6 s (longest 0.8 s); profiled main-thread JS per change C 1.0 s, B 1.25 s; Kids' Choice release 0.21 s. Prototype built on it (4c9331d13c), being measured. MEASURED 2026-10-15 (headless Firefox, one session per build, head then prototype, not alternated; data in ~/.cache/hazel-diag/r90): WHOLE PROGRAM (C): a 1.5 s drag stalls the page 4.1 s (3.7-5.9) on the head, 2.6 s (2.1-3.2) with the prototype, the ranges apart; longest single stall 0.91 -> 0.61 s; release to redraw 1.39 -> 1.11 s (apart). Profiled main-thread JS per change 1,010 -> 914 ms: the editor's update (perform_update) 860 -> 570 ms and the view functions in it 450 -> 240 ms, but ~190 ms of that saving reappears outside the update (probably the worker's larger replies; not traced). UX MODE (B): drag stall 2.6 -> 2.45 s (ranges overlap: no clear change); release 1.00 -> 1.15 s; main-thread JS 1,251 -> 925 ms. KIDS' CHOICE release 212 -> 271 ms (+28%, as last night). Reading: the prototype is a real, visible win with the whole program on screen, none in UX mode, a loss on small livelits; and even with it a 1.5 s drag stalls 2-3 s, so most of the stall is elsewhere (each processed change still costs ~0.9 s of main thread). Decision is Matthew's. 2026-10-15T18:52: tagged 🪷 blocked on Matthew: its next step is his call (see above).from Matthew, 2026-10-15 |
^ticket(89) | performance | done 2026-10-15 | DONE 2026-10-15: #2635 had Andrew's compaction alone; 5 runs at 653ebc4adf: edits 1.2 s, every redo 3.6-4.2 s, most undos 3.5-5.1 s. Ported the newest-entry-whole change (as f8ae6f2b9c on #2597) and PUSHED it on Matthew's word: #2635 at 032fe3aca6. 5 runs: every undo and redo within 0.3 s (the check's poll), edits 1.1 s, every step restores; memory over 12 edits the same within about 1 MB. Undo tests pass. Written into ^report(14)'s update (^ticket(87)). | Undo on #2635: is it slow, and does keeping the newest entry whole fix it? From ^ticket(86) and ^report(14)'s update ("What next, now"). #2635 has Andrew's undo compaction (6010128b7d, d93e482166) but not #2597's f8ae6f2b9c, which keeps the newest undo and redo entries whole. On #2597, compacting every entry made a redo, and half the undos, cost about an edit (~6 s on Full Body, against 2.6 s), and keeping the newest whole brought them back (median 2.6 s, 5 runs). Steps: (1) the same undo check (undo_check.mjs: edit, undo, redo, a second edit, undo; 5 runs) on #2635's head 653ebc4adf, port 8341: running 2026-10-15; (2) if undo or redo take about an edit's time, port f8ae6f2b9c to #2635's History.re (the same three places: edit, undo, redo; #2635 also realizes and carries views on undo), check the Undo tests and the 5 runs again, and push on Matthew's word; (3) write the result into ^report(14)'s update before it is published (^ticket(87)). dev: left to #2469's merge (Matthew, 2026-10-15). RESULT 2026-10-15, #2635 at 653ebc4adf, 5 runs: edits median 1.2 s (1.05-1.44); every redo 3.6-4.2 s, about three edits; undos 3.5-5.1 s but for three at 1.1-1.5 s. Slow, so (Matthew's call) the port: local branch perf/2635-undo-keep-newest, Undo tests pass; measuring its 5 runs and its memory, then push.from Matthew, 2026-10-15 |
^ticket(88) | dress up | done 2026-10-15 | DONE 2026-10-15: released as Messed Up Dress Up 0.0.34 (tag dress-up-0.0.34, 47872b6), live at http://matthewhammer.org/kids-choice-games/dress-up/ (checked in a cold profile). LFOs from 1/32 beat to 8 bars, every whole number and every 1/n, 1 beat the knob's middle (63 positions); bongo and conga bend through each hit when the LFO is faster than a beat; wings faster than about four frames a cycle blur, the blur's 8 copies flapping at the LFO's own speed so it keeps moving; fades that fast hold at their average. | LFOs faster than a beat: down to 1/64 of a beat. Today an LFO's shortest full cycle (the "⏱️ round in" knob, LFO_BEATS = 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 beats) is one beat. Add positions below 1 beat, labelled as fractions: 1/2, 1/3, 1/4, 1/5, and so on up to 1/64. Notes for the work: (1) the bend is sampled once per note, at its step (steps are sixteenths, 4 to a beat), so an LFO of a step or less aliases to nothing (1/4 beat lands every note on the same phase); fast LFOs need continuous modulation in Web Audio (an oscillator into the voice's detune or gain), not a per-note sample. (2) At the fast end it is audio rate: at 116 bpm, 1/64 beat is about 8 ms, about 124 Hz, so it becomes FM or tremolo more than wobble: worth hearing before settling the range. (3) What else follows an LFO (the fairy wings' flap, the fades through lfoNow) runs at the screen's frame rate: above about 1/8 beat it would strobe or alias, so the visuals may need a cap. (4) "And so on" read as every denominator from 2 to 64 is 63 more knob positions; check with Matthew whether he wants all of them or a set (1/2, 1/3, 1/4, 1/6, 1/8, 1/12, 1/16, 1/24, 1/32, 1/48, 1/64). Goes under Unreleased in VERSIONS.md; no bump or push until he says. BUILT 2026-10-15, local commit c1ed1bf in ~/kids-choice-games (not pushed, no bump; under Unreleased): the knob has 73 positions (1/64 ... 1/2 beat, then 1 beat ... 8 bars); a bongo or conga on an LFO faster than a beat gets a pitch curve through each hit (checked offline: a 1/16-beat sine at depth 3 swings a 220 Hz hit 187-260 Hz; slower LFOs unchanged, still sampled per hit); wings on an LFO with a cycle under 1/15 s draw as a blur of 8 see-through copies a side across the sweep (Matthew preferred this to letting the screen alias them); a fade that fast holds at its average. Waits for Matthew to try and listen: http://localhost:8133/dress-up.html REVISED 2026-10-15 (Matthew: down to 1/32 to match 8 bars, with 1 beat the middle): the knob mirrors itself, 1/32, 1/24, 1/16, 1/12, 1/8, 1/6, 1/4, 1/3, 1/2 beat | 1 beat | 2, 3, 4, 6, 8, 12, 16, 24, 32 beats (19 positions, 1 beat the 10th). Saved lengths it no longer has show at the nearest by ratio. Local commit 2 on top of c1ed1bf, not pushed. REVISED AGAIN 2026-10-15: on Matthew's yes, every whole number on both sides (1/32, 1/31 ... 1/2 beat | 1 beat | 2, 3, 4, 5 ... 32 beats; 63 positions, 1 beat the 32nd), local 6d49838. And, his idea, the blur keeps moving: its 8 copies each flap at the LFO's own speed, an eighth of a cycle apart, so every frame catches the full sweep in a new place (checked: frames 40 ms apart differ), local commit after 6d49838. Not pushed. from Matthew, 2026-10-15 |
^ticket(87) | performance | done 2026-10-15 | PUBLISHED 2026-10-15 on Matthew's word: http://matthewhammer.org/hazel/performance-analysis/014/#update-box. Series repo aae0feb (report 14 alone; ^report(8)'s draft stays local, now fb8aa41 on top), homepage e3a35af (the page, its update data, and the card's "Updated later on 2026-10-14"). Checked live over HTTP and in a cold browser: five figures, the update, the data, no errors. | Review ^report(14)'s draft update, then publish. The answer to its open question: the undo history keeps the versions (^ticket(58)). A dated update box under the short answers, a dated note after each claim it supersedes (the "undo capped at 2" build, the open holder, the snapshot lead, What next), and an Update section: a figure and table of four builds over 12 edits, how the holder was found, Andrew's fix (6010128b7d, d93e482166) and its port, the undo checks and the likely cost; data in 014/data/update-2026-10-14/ and in the page's JSON. Local commits 0ddcef7, 6aabc40, 8ba0119 (undo timed over 5 runs per build; the keep-newest experiment) in ~/hazel-perf-report (beside ^report(8)'s draft f7828a6), not pushed. Preview: http://localhost:8131/series/014/#update-box. It calls the port "a local commit": if bc66ae5616 lands on #2597 first, link it. UPDATED 2026-10-15: both undo commits are on #2597 (d41c7a0298, f8ae6f2b9c) and the draft cites them (local commit b569651, not pushed); the keep-newest change is no longer called an experiment. Waits only for Matthew's review. 2026-10-15: Matthew reviewed it ("looks good"); Figure 4's title now gives its unit (MB); dev is left to #2469 (his call, written in); waiting on ^ticket(89)'s #2635 undo result before publishing. READY 2026-10-15: #2635's undo result written in (032fe3aca6); local commits up to 9e4d3ca in ~/hazel-perf-report. Waits for Matthew's word to publish (series repo push + homepage copy).from ^ticket(58), 2026-10-14 |
^ticket(86) | performance | done 2026-10-15 | DONE 2026-10-15: pushed on Matthew's word to #2597: d41c7a0298 (Andrew's undo compaction from 6010128b7d, ported; the same History.re and Test_Undo.re as the measured bc66ae5616) and f8ae6f2b9c (the newest undo and redo entries kept whole; was 3041fd0483, comment reworded). Full Body keeps 208 MB after 12 edits instead of 1,048, and undo and redo stay at ~2.6 s. Follow-ups: measure undo on #2635 (it has Andrew's compaction without the newest entry whole) and bring f8ae6f2b9c there if slow; dev has the same uncapped undo history, left to #2469's merge (Andrew's code). | Push #2597's undo compaction (bc66ae5616), on Matthew's word. From ^ticket(58): #2597 kept a whole page model per edit in its uncapped undo stack (about 87 MB per edit of Full Body). Local branch perf/2597-compact-undo in ~/hazel-2597-probe, one commit on #2597's head c4e411f9c2: Andrew's compaction from 6010128b7d (on #2469/#2635), adapted to #2597's scratchpad list, and the cap of 250 always on. 159 MB after 12 edits instead of 1,048; undo, redo and undo after a second edit restore the same picture; the 6 Undo tests pass. Cost, measured (5 runs per build, alternating): every redo now takes about an edit's time on Full Body (6.0-6.5 s, against 2.7-3.1 s as built), and half the undos do (5 of 10 at 5.8-6.5 s; the rest 2.0-3.0 s, as built). Experiment, also local: 3041fd0483 on perf/2597-compact-undo-keep-newest keeps the newest undo and redo entries whole, so the latest undo and redo are as fast as as built (median 2.6 s, 5 runs), for 208 MB after 12 edits instead of 159; deeper undos still rebuild. Which to push is his call. Questions for Matthew: push it to #2597? And dev has the same uncapped stack: bring Andrew's fix to dev ahead of #2469, or leave it to #2469?from ^ticket(58), 2026-10-14 |
^ticket(84) | performance | done 2026-10-15 | DONE 2026-10-15: pushed on Matthew's word. #2635: ec9f125acd (cherry-pick of the local cbc585933b, the same tree). #2597: a8426b1893 (#2597 had the same quadratic slicing in Triggers.re). Measured on #2635 before pushing: save_current about 600 -> 80 ms per change; UX-mode release 2.05 -> ~1.4 s. Affected test groups pass on both branches (DocSlides.ReparseBackuptext, Parser.Segmented, FastParse, Unproject, Undo; DividedLaws on #2635). dev: not checked yet whether it has the same printing path. | The autosave prints the whole program in quadratic time when it has probes: about 0.6–1 s of the main thread per change. Found under ^ticket(29), profiling the Full Body arm-length slider on #2635's head (9fb2bdf067): the save alarm (about 1 s after a change) prints the program to text (PersistentZipper.to_string / persist_divided → MarkerParse.to_text → Segment.to_string with the zipper's manual refractors), and Triggers.refractor_seg_to_seg_with walks the long top-level let chain slicing with ListUtil.sublist from the list's head and recomputing Skel.range down the Pre chain at every node: O(n²) in the top-level items. Profiled: save_current about 600 ms per change in C (just after the redraw, so it delays the next interaction) and about 1 s in B (UX mode, inside the change: most of why B is 0.6–0.7 s slower than C today). Fix: slice an array, and take a skeleton's first or last index without computing both ends recursively; measure before and after; on #2635 (and #2597) once Matthew says. Fixed locally, 2026-10-14 (not pushed; waits for Matthew): branch perf/2635-autosave-print, commit on #2635's head , in ~/hazel-livelits-modular (served on 8341 meanwhile). save_current per change, profiled: about 600 → 80 ms; UX-mode release to redraw 2.05/2.04 s → 1.26–1.56 s; the whole program 1.23–1.76 s, unchanged within noise. Tests DocSlides.ReparseBackuptext (120), DividedLaws, Parser.Segmented, FastParse pass. To do with Matthew's word: push to #2635, cherry-pick to #2597 (and a dev PR if the code is there).from ^ticket(29) (profiles 2026-10-14) |
^ticket(60) | homepage, publishing | done 2026-10-15 | HTTPS live and enforced, 2026-10-15 19:30. On Matthew's OK, removed and re-added the custom domain via the API (19:17-19:19, ~2 min offline); the 2016 CNAME file had two lines (matthewhammer.org + www), GitHub's re-add wrote one. Certificate approved 19:22 (matthewhammer.org + www, expires 2027-01-13). Mixed content: the only absolute http:// load was the hover cards' data.json, switched to https in the dashboard's ref_cards.html, the 15 published reports + series index, and the series repo (ACAO * so the cross-origin fetch works). Enforce HTTPS on matthewhammer.github.io, hazel-work-notes, kids-choice-games, dress-up-pictures, dress-up-music: every http:// URL now 301s to https. READMEs and the series' instructions no longer say 'HTTP only'. Not done, not needed: rewriting every old http:// link (they redirect); older Pages repos (fumola-lang, Motoko projects, MirrorGarden, ic-mini-terminal) left as they were. ⏱ 17.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Serve matthewhammer.org over HTTPS. Today https://matthewhammer.org/ fails: it presents GitHub's own certificate (*.github.com, Sectigo), not one for matthewhammer.org, so browsers warn and agents' fetches fail; everything is linked as HTTP. The blocker, checked 2026-10-14: the apex A record still points at 192.30.252.153, GitHub Pages' address from before 2018. GitHub serves the site there over HTTP but will not issue a certificate for a domain pointed at it; the Pages settings have no certificate and https_enforced: false. www is fine: a CNAME to matthewhammer.github.io, on the current addresses. The domain's nameservers are ns-cloud-c1..c4.googledomains.com (Google Domains, now Squarespace Domains), so the DNS change is Matthew's, in his registrar's account. Steps: (1) Matthew replaces the apex A record with GitHub Pages' four, 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 (and may add the four AAAA, 2606:50c0:8000::153 to 8003::153); (2) once that resolves, GitHub provisions a certificate for the custom domain, which can take up to a day (re-saving the custom domain in the homepage repo's Pages settings restarts it if it stalls); (3) turn on Enforce HTTPS for the homepage and the project sites under it (hazel-work-notes, kids-choice-games); (4) check every page for mixed content: anything a page loads by an absolute http://matthewhammer.org/ URL (the series' live hover cards read report pages; the dashboard's cards too) is blocked from an HTTPS page, so make those relative or HTTPS; (5) switch the links we publish to HTTPS: READMEs, the series' agent instructions ("HTTP only"), the dashboard, the reports, #2609's ^report links; old HTTP links keep working, since GitHub redirects them once HTTPS is enforced. Progress, 2026-10-14 afternoon: Matthew changed the apex A record in Squarespace to 185.199.108.153; the authoritative nameservers and public resolvers (8.8.8.8, 1.1.1.1) answer it, while this laptop's resolver still had the old address cached (TTL 1 hour). GitHub has not issued a certificate yet (https_certificate: null). Still to do: the other three A records (.109, .110, .111) for redundancy, and re-saving the custom domain in the homepage repo's Pages settings so GitHub checks DNS again and issues the certificate. Later that afternoon: the new address had reached Google's, Cloudflare's and Quad9's resolvers, so DNS was no longer the wait. On Matthew's OK, Claude re-saved the custom domain through the API (204). GitHub's domain health check then said: pointed at a current Pages address, not the old one; eligible for HTTPS; no CAA problem; www fine. It was waiting only for GitHub to issue the certificate. Watching (Matthew's ask, 2026-10-14): a watcher in Claude's session checks every 5 minutes, for up to 24 hours, whether matthewhammer.org serves a valid certificate. When it does, Claude turns on Enforce HTTPS and finishes the ticket's remaining steps, then closes it. 2026-10-15 14:36: the 24-hour watch ended with no certificate (GitHub's Pages API: https_certificate null). DNS is right: the apex A record and www both point at GitHub Pages IPs. Usual fix: remove and re-add the custom domain in matthewhammer.github.io's Pages settings, which re-triggers provisioning. That takes matthewhammer.org offline for a few minutes, so it waits for Matthew's OK, and not while M is playing. 2026-10-15T18:52: tagged 🪷 blocked on Matthew: its next step is his call (see above). 2026-10-15T19:17 (Matthew's OK): removed and re-added the custom domain through the API (GitHub's commits 3f662f98f7 'Delete CNAME', d3c19d168a 'Create CNAME'); the site was offline about two minutes (19:17:37 to 19:19:40). Found on the way: the old CNAME file (from 2016) held two lines, matthewhammer.org and www.matthewhammer.org; GitHub expects one, and the re-add wrote one. 19:22: certificate approved for matthewhammer.org and www (expires 2027-01-13); https://matthewhammer.org/ serves 200 with a valid certificate, www redirects to the apex. Left: (3) Enforce HTTPS, after (4) checking pages for absolute http:// fetches that HTTPS would block; (5) switch published links to HTTPS.from Matthew, 2026-10-14 |
^ticket(50) | dress up | done 2026-10-15 | Released as Dress Up 0.0.54 at 20:27 (d9a7877, tag dress-up-0.0.54; Pages green, live check 20:28). Seats (Matthew: 'where people can sit -- we want to offer that pose in those places'): SEATS[kind](c, B) per place from the scenes' own expressions; drop the friend with feet between a seat's top and the floor in front to sit (dropOnSeat), drag off to stand; 🪑 chips + 🧍 stand up in the background tab; seated, sorted and sized at the seat's depth. Seats: couch; healer's chairs; castle dining chairs, throne, cat-room window seat, spiral steps, stable hay; tree house beanbag; camp logs (facing the fire), beach chairs, underwater picnic bench, toadstools, path mushrooms, mountain rocks, castle crate + barrels, house step + wagon, tree house swing, witch's root. Built with two helpers in worktrees (indoor 13 min, outdoor 16 min, node-verified; dining layout refactor byte-identical); every seat checked on contact sheets. Dropped: bottom bunk (head through the top bunk). The original 'where feet can stand' half of 50 is moot for water since 0.0.48 (standing in water is allowed now). ⏱ 22.0 min of work, timed · 1 messages from Matthew, 0 questions to him · ~95 chars (~23 tokens) · ~0 min together | Messed Up Dress Up: where feet can stand, per background (later, not now). Split from ^ticket(49). Each background gets a collision shape for the places it makes no sense to stand: rivers, the pool under the falls, the campfire, and the like. Arrivals (and later moves and drags) use it to keep a person's feet on the ground. ^ticket(49) hand-picks ground spots for the camp and the falls in the meantime. 2026-10-15T18:52: note: since 0.0.48 (^ticket(122)) the friend can stand in water (the lake, the sea, the falls' pool, the moat, the stream, the brook), so water is no longer a place where standing makes no sense; WATER in the game holds each place's water shape, which a collision shape could build on. 2026-10-15T19:28, Matthew: add to 50 where people can sit: "we want to offer that pose in those places." So each place also marks its seats (the living room's couch, the dining room's chairs, the logs by the camp fire, benches, a swing, steps, a bed's edge, and the like), and a person put or dragged onto one sits, in the sitting pose from ^ticket(124), at the seat's depth (99's model). Builds on 124's sitting drawing; 122's WATER is the model for a place's shapes.from N (via Matthew), 2026-10-14 |
^ticket(46) | performance, CI | done 2026-10-15 | DONE IN PART 2026-10-15: the segmented parser's count is pushed (#2635 653ebc4adf, #2597 7f62da773a): full-body.hz 76 -> 54 s, eyes.hz 19.6 -> 15.8 s. With ^ticket(85)'s test-startup fix (#2635 c053aec689, #2597 0322b09ca2), each test run starts in 6 s instead of 28. What is left of the parse is the editor's re-molding (remold ~22 s) and unselect's reassembly (~14 s), core editor logic for its owners; the 25 KB gate (^ticket(4)) stays. The get_base memo (0825830190, ~15% on eyes.hz) is still local, on parser-get-base-memo in ~/hazel-ci-heap. | The recovering parser on a Livelit Playground slide: 452 s for full-body.hz. Found under ^ticket(4): the editor's recovering parser (Parser.to_segment, segmented, but a slide is one long let ... in chain) is near-quadratic there. MenhirCorpus per file on a laptop: full-body 452 s, shoes 386, scenes 210, emotion-kids 178, witch 169; 2096 s for the playground, about 5 min of it when CI was green. ReparseDocSlides and TextRoundtrip.DocSlides parse the same slides again. If Matthew takes the gate (^ticket(4), local commit f953119482: over 25 KB, only in the weekly run), this is what brings them back per PR. Known leads from the 2026-09-28 profile: Insert.go per character re-molds the whole sibling run; Form.try_get is 12% (a cheap memo); GC 41%. Tried 2026-10-14 night: local commit 0825830190 (branch parser-get-base-memo in ~/hazel-ci-heap, on ci-heap-4-pick, not pushed) keeps each label's molds in Form.Molds.get_base and runs the atomic regexes once instead of up to three times. MenhirCorpus on eyes.hz, alternating runs: 90.1 and 84.6 s before, 75.4 and 72.5 s after, about 15% faster; the parse still matches Menhir and the quick suite passes. Not enough to drop the 25 KB gate (it would take the playground's 2096 s to about 1800); the per-character re-mold of the whole sibling run is the real cost. MEASURED AGAIN 2026-10-14 (#2635 head 9fb2bdf067, one MenhirCorpus case alone, QCHECK_LONG=1, laptop): eyes.hz 19.6 s, full-body.hz 76 s (the ticket's 452 s and 85-90 s were inside the whole suite's process, or an older build). The profile of eyes.hz: the parse 16.8 s (Insert.go 13.6 s, remold 7.7 s), and 22 s of the run at test startup in Test_Unproject's Str scan (^ticket(85), fixed locally). FIXED IN PART 2026-10-14 (local, not pushed): the segmented parser took List.length of the whole rest of the text per insertion; now it counts along the run (perf/2635-parser-count, 3663ef22e6): eyes.hz 19.6 → 15.8 s, full-body.hz 76.3 → 53.6 s, MenhirCorpus passes. What is left is re-molding the sibling run per insertion (22 s of full-body's 41 s parse) and reassembling it in unselect (14 s), core editor logic.from ^ticket(4), 2026-10-13 night |
^ticket(42) | dress up | done 2026-10-15 | Dress Up 0.0.57 (c058d1d, live): the tree house's stream and the cemetery's brook flow (flowRipples: each ripple moves to where the next starts, growing with the water, so the loop is seamless), and a friend on the swing swings with its seat (the same rotation and LFO timing, added to the friend's group). Checked in headless Firefox (ripples move downstream; the sitter's box moves with the seat) and by the sweep against 0.0.56: only the places showing the stream or brook differ. ⏱ 8 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Messed Up Dress Up: scenes that move. Ducks paddling on the lake, the tree house's stream flowing, the living room's lamp switching on and off; small animations kept to their own parts, so a scene's sliders still build only what they change. 2026-10-15T21:29, narrowed (Matthew): most of this is done (the ducks paddle since 0.0.28, the lamp glows with the slow LFO since 0.0.35, every scene moves with the music); what is left: the tree house's stream and the cemetery's brook flowing, and the swing swinging a friend who sits on it (from ^ticket(41)). Tonight, after hours (🌙).from Matthew, 2026-10-13, in chat (ideas from ^ticket(40)'s doorways) |
^ticket(41) | dress up | subsumed 2026-10-15 | Done by ^ticket(50) (Dress Up 0.0.54): drop the friend on the living room's couch or the tree house's swing (and 15 other places' seats) and they sit; drag them off to stand. Left: the swing doesn't swing a sitter, moved to ^ticket(42).⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Messed Up Dress Up: places to sit. Drop the friend on the living room's couch or the tree house's swing, and they sit on it (or swing on it); drag them off to stand again. from Matthew, 2026-10-13, in chat (ideas from ^ticket(40)'s doorways) |
^ticket(29) | series | done 2026-10-15 | PUBLISHED 2026-10-15 21:25 on Matthew's word, after a review together: ^report(8)'s dated update of 2026-10-14 (B and C with the same fixes: UX mode slower again by the autosave's quadratic print; with ec9f125acd, B 1.26-1.56 s vs C 1.23-1.76 s over five sessions each, the same), plus, at his OK, a cross-check from ^report(15) on 032fe3aca6 (C 1.09 s, B 1.20 s). https://matthewhammer.org/hazel/performance-analysis/008/#update-box; series card updated; homepage cfe6814, series repo f8e62fe (the draft squashed into one published commit). Data in 008/data/update-2026-10-14/ and the page's report-data.⏱ 10.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Revise ^report(8) to answer plainly: are conditions C (#2635, whole program) and B (UX mode, only the GUI) similar once the same fixes are applied to each? As written it confuses: the GUI-only view gives no responsiveness benefit, but with the fixes it is no loss either; say so up front. Re-measure B and C on #2635's current head (the reuse check's fix, the stream index, the has-type memo, the per-step changes all in), and add graphs in the report itself, not just the thumbnail, taking ^report(1)'s pictures as the model, especially in the short answers. 008 is published, so per the series' rules: a dated update box under the short answers, notes after superseded claims, and an Update section with the figures. UNBLOCKED 2026-10-15: ^ticket(84)'s fix is on #2635 (ec9f125acd, the same tree as the measured cbc585933b), and the draft now links it (local commit b569651 in ~/hazel-perf-report, not pushed). Waits only for Matthew's review: http://localhost:8131/series/008/#update-box 2026-10-15T18:52: tagged 🪷 blocked on Matthew: its next step is his call (see above).from Matthew, 2026-10-13, in chat |
^ticket(23) | performance | answered 2026-10-15 | Answered by ec9f125acd (on #2635 and #2597): the autosave's print of a slide with probes was quadratic in its top-level items; fixed, it takes about 80 ms, from about 1 s (^report(8)'s update). Saving only the changed cell, or when idle, is no longer needed for responsiveness.⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | The autosave in UX mode (save_current / persist_divided: the divided program printed to text and stored, 0.55 s profiled) landed inside a slider change in ^report(8)'s B. With ^ticket(6)'s fix the change ends first, so it no longer does, but it is still 0.55 s on the main thread about a second after every edit, where the next drag can meet it. Save when idle, or save only the changed cell.from ^report(8) |
^ticket(22) | performance | answered 2026-10-15 | Answered by ^report(8)'s update (published 2026-10-15): with the reuse check's lexemes compared in the same memoized walk, the fresh ids of a re-analyzed cell no longer cost measurable time; UX mode (B) takes the same time as the whole program (C) once the autosave print is fixed.⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | UX mode re-elaborates an open cell's item with fresh ids on every change (the drawn cell in ^report(8)'s B), so its parts never match by hash and fall to the reuse check's slow path. Keep the ids stable when a cell is analyzed again.from ^report(8) |
^ticket(12) | performance | answered 2026-10-15 | Answered: ^report(14) measured the shared-value tables (ShareCodec's two, reset at their 200,000 cap) and the per-object hashes (ValueHash's WeakMap, about 98 MB alive after four edits, as a measure of old state kept); the 10-09 interaction profile measured the worker's messages at 300-480 KB, under 5% of the time.⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | Measure the size of view samples on the wire (not trimmed as the result is) and the memory of the shared-value tables and per-object hashes. from ^report(5) |
^ticket(7) | performance | answered 2026-10-15 | Answered by ^report(13) (published 2026-10-14) and the commits on #2635 (043508921b, 9fb2bdf067): the request's projection now takes under 1 ms, from 1.9-11.9 ms. The one step left, offering the three ports to #2469, is ^ticket(54).⏱ 0.0 min of work, timed · 0 messages from Matthew, 0 questions to him · ~0 chars (~0 tokens) · ~0 min together | The commit path: find which of the request's values are new (about 15 ms of hashing, profiled), and make the commit's redraw cheaper. Part done, 2026-10-14, in node on #2635 at 41897ec3a5 (probe 4f0e261652, branch request-hash-probe in ~/hazel-ci-heap-2635, local only): a release-like edit (one literal, ids kept) through the app's own path, six edits each, steady state. Statics after the edit (DefStatics, per item): 25–37 ms. Only about 5% of the request is new objects: Kids' Choice 2,039 of 40k program nodes and about 4,600 of 87k EvalInfo nodes; Full Body 396 of 196k and about 13,100 of 326k. Building the request: the EvalInfo projection is the largest part, 13–14 ms on Full Body and 2.5–9 ms on Kids' Choice, because project_from walks both whole statics maps in key order on every commit, though about 180 entries change; then hashing the new values about 1–4 ms, and encoding 1.5–5 ms. So the hashing is not the cost. Tried and dropped: keeping an entry that is equal by content (ValueHash) as well as by identity: Full Body's re-added entries 179 to 30 and its EvalInfo new nodes 13,100 to 2,200, but the whole request got slower (median about 20 ms against 17), since comparing by content hashes as much as it saves. Done and pushed to #2635, 2026-10-14: 043508921b (DefStatics hands EvalInfo the keys it touched; the projection updates only those) and 9fb2bdf067 (unchanged spine roots kept): on Full Body the request 15.5–18.8 → 2.5–3.0 ms, statics after the edit 25–31 → 16–20 ms. Carrying it to diverged branches is ^ticket(47) (^report(13)). The commit's redraw, the ticket's second half, is not started.from ^report(1) |
^ticket(85) | performance | done 2026-10-14 | DONE locally 2026-10-14 (not pushed; waits for Matthew): branch test/2635-unproject-startup, commit on #2635's head, a plain substring scan instead of Str. A one-test run 28.0/28.3 s → 6.4/6.4 s; the same 42 slides picked, Unproject's 47 cases pass. Applies to #2597 too (same test). PUSHED 2026-10-15 on Matthew's word: #2635 c053aec689, #2597 0322b09ca2. | Every test run spent about 22 s at startup before any test ran: Test_Unproject's slides() runs at load and picked the livelit slides with Str.search_forward, whose matcher under js_of_ocaml is slow (20.7 s of a 45.5 s CPU profile of one MenhirCorpus case). from ^ticket(46), profiling one MenhirCorpus case, 2026-10-14 |
^ticket(83) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.33). Chips 👗 dress | 👕 shirt + pants. With pants: the tunic as a top in the same five styles and colors, its hem a short frill at the hips in the style's shape, pants a shade deeper. Checked all five styles. | Dress Up: the fairy in a shirt and pants. Offer a non-dress option for the fairy costume, with the same style options as the tunic: two chips, dress | shirt + pants. Changes go under Unreleased; no version bump until Matthew says. from Matthew, 2026-10-14 |
^ticket(82) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.32). The afro was a circle whose lower arc showed beside the cheeks to the jaw (more with big eyes, which stretch the hair). Now its own shape: round over the head above the ear line, a curly edge, tucked in under the ears. Checked at lengths 60, 100, 135 (his screenshot's, with big eyes), 250, 400, and an alien head. | Dress Up bug: the afro becomes a beard. The afro is one big circle; its lower part reaches down beside the cheeks to the jaw, so it frames the face like a beard (again; screenshot at length 135). Give it a shape of its own: a big round puff over the head, curly at its edge, tucking in at the ears, nothing below them. Changes go under Unreleased; no version bump until Matthew says. from Matthew, 2026-10-14 (screenshot) |
^ticket(81) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.32). Three waltzes in the waltz group: 🏰 castle waltz (G, flute over harpsichord oom-pah-pah, strings, bells) for the castle; 👑 ballroom waltz (D, fiddles, harpsichord arpeggios) for inside the castle; 🌿 healer's waltz (F, slow, vibes, pad, music box) for inside the healer's hut. Each builds (every part 8 bars, 48-bar form); rendered offline in silence, levels like the existing tunes (RMS 0.083-0.096 vs 0.085-0.098), no clipping. Not yet heard by a person. | Dress Up: a few peaceful waltzes, new tracks for the castle and the healer's hut (in 3/4). Changes go under Unreleased; no version bump until Matthew says. from Matthew, 2026-10-14 |
^ticket(80) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.32, commit d178835). healerroomScene: the clearing's healer's hut is now a doorway into it (woods group). Warm round room: thatch, daub walls, herbs drying on the beam, a sunny round window, a braided rug; a stone hearth, a big steaming pot of golden soup on its fire; shelves of potions sorted by color, each shelf its own kind of bottle; the cat on the top shelf, its tail swinging (fur, tuxedo, eye colors); a worktable (mortar and pestle, bowls, herbs, scissors, knife, balance); a tea table with two chairs. The healer: friendly, auburn hair with flowers by her ears, green dress, heart apron, swaying, stirring the soup with her spoon; draggable (no jump on a first drag), a size slider. Its music comes with ^ticket(81) (the waltzes). | Dress Up: inside the healer's hut, a new background. It is warm. A fire with a pot full of warm soup. Lots of shelves of neatly organized bottles (potions). A pet cat sitting high up on a shelf, slowly wagging its tail, its color configurable. The healer herself: a friendly character, swaying gently from side to side, smiling; long hair with flowers in it near her ears, a dress, and a spoon in her hand for stirring the big soup pot. Tables with tools, herbs and bowls, and chairs to sit and talk near a table. Changes go under Unreleased; no version bump until Matthew says. from Matthew, 2026-10-14 |
^ticket(79) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.32, commit 92b16ed). A band of vine, leaves and six flowers round her head on her forehead, between the brows and the crown, over her hair and bangs; flowers in every color or one kind (pink roses, daisies, buttercups, forget-me-nots, violets); a toggle, on by default; goes off with the lid when her head explodes. Checked with long, short, curly and afro hair, big eyes, an alien head, no bangs. | Dress Up: a wreath of flowers for the fairy, worn on the crown of her head, above the eyebrows. Changes go under Unreleased; no version bump until Matthew says. from Matthew, 2026-10-14 |
^ticket(78) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.32). Cause, as Matthew guessed, the witch's hat, together with eye size: under the hat hair is tucked below the brim's line, y = 40 in the hair's own units, which the head's stretch moves; the bangs (drawn in the face's units) were tucked at an unstretched 40, so big eyes (a taller head, the hat lifted with it) left bare forehead between the brim and the bangs, and only the sweeps over the eyes showed. Now the bangs' tuck line is the brim's, stretched with the head. Checked: witch with eyes 200 and 50, bangs 100 and 164, long, short and curly hair. | Dress Up bug: bangs and eye size interfere. Seen live in 0.0.31 (screenshot: the witch, long hair at length 237, bangs at 164, big blue eyes): the forehead shows no fringe, and dark wedges of hair cut into the top outer corner of each eye. Find why the bangs (^ticket(74)) and big eyes clash, and fix it. Changes go under Unreleased; no version bump until Matthew says.from Matthew, 2026-10-14 (screenshot) |
^ticket(77) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in Dress Up 0.0.31: Matthew pushed it, Pages deployed, checked on the live site: LFO B flaps the wings and its strip lists them). With an LFO the wings flap once per LFO cycle in its wave's shape, in time with the music (lfoNow, shared with the visibility LFOs), swinging 8 to 36 degrees with depth (15 at the usual depth); mixer changes reach the wings at once (checked: LFO B to 4 beats, saw, full depth turned a 1.43 s sine flap into a 2.86 s saw); still flapping through a drag, at no measurable cost. FIX (Matthew, same day): the mixing board's LFO strip did not list the wings among what it drives; now it lists 🧚 fairy wings while she wears them, they flutter, and wingLfo names that LFO (checked for A, B, no flutter, no costume). | Dress Up: an LFO chip for the fairy's wings (no LFO, A, B or C). from Matthew, 2026-10-14 |
^ticket(76) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (in Dress Up 0.0.30, commit ba3f668; LIVE: Matthew pushed it, Pages deployed, checked on the live site in a fresh browser profile). The wings' flap and the sparkle wings' twinkle are CSS animations now, which the drag's pause of SVG animations leaves running, started in step with the clock at each redraw. Measured (headless Chrome, 60-move drags, on/off alternating): the wing angle sweeps 0-9 degrees while the SVG animations are paused; frames 16.7 ms median either way; redraw medians overlap (2.2-2.8 ms with, 2.1-2.6 without, four runs each in the witch's woods): no measurable cost. | Dress Up: a fairy's wings keep flapping during a drag, so a player can pretend to fly. A special exception to pausing the picture's animations while dragging (^ticket(62)), if it costs no noticeable performance. Changes go under Unreleased; no version bump or push until Matthew says.from Matthew, 2026-10-14 |
^ticket(75) | Dress Up | done 2026-10-14 | DONE 2026-10-14, in Dress Up 0.0.30 (commit 3729675; version 2df87d4, tag dress-up-0.0.30; LIVE: Matthew pushed it, Pages deployed, checked on the live site in a fresh browser profile). Was: 70 white dots, a quarter blinking. Now one helper (starsSvg) for the camp site's sky and the night through the tent's door: shape chips (dots, four, five, six, eight points, or a mix, the default), average size 25-2000%, average brightness (the brightest glow), twinkle 0-100 (the big bright ones turn, scale and glimmer; small ones blink). The moon covers the stars behind it. Measured: idle 60 fps with twinkle 100 or 0; a drag's frames and redraws the same either way. | Dress Up: the camp site's stars at night. New controls: (1) the stars' average size, up to huge, and their shape: today they are plain white dots (70 circles, 0.8 to 2.2 units across, a quarter of them fading in and out); make them many-pointed stars from a palette of nice-looking shapes; (2) their average brightness; (3) twinkling: some of the large, bright stars spin slightly and vary their size and brightness, with a slider for how much they twinkle. (The tent's door shows 14 more dots of night sky; they should match.) Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 |
^ticket(74) | Dress Up | done 2026-10-14 | DONE 2026-10-14, LIVE in Dress Up 0.0.29 (commit 57d965e; version b065648, tag dress-up-0.0.29; Matthew pushed it, Pages deployed, checked on the live site). The front of the hair is drawn again after the face, eyes and brows: the hairdo clipped to the head and a fringe line, locks of its kind (pointed, round, zigzag), outlined only over hair (not across a long hairdo's parting), split along the crack when the head explodes. A bangs slider (0 = none, the old look; 100 = to the brows; up to 300). Cost: about 0.4 ms more per redraw on a woods drag (2.3 to 2.7 ms median, two alternating runs each), still 60 fps with no dropped frames. | Dress Up: the bangs in front. A bug since the first version: the whole hairdo is drawn before the head, so its front (the fringe over the forehead) is hidden behind the face (seen with the head made see-through). The order, back to front, should be: the back of the hair, the face, the eyes, then the front of the hair. Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 (two screenshots: the parting's outline seen through a see-through head) |
^ticket(73) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in Dress Up 0.0.29, commit ca17c47, deployed 2026-10-14): no skeletons or skulls by default; each dungeon room has its own slider, 0 to its drawing's most (2 in the dungeon, 4 in the deep dungeon), least scary first; never rolled by Surprise me. With none, chains hang empty and the candle stands on a stone; the dungeon's sign is a key, not a skull; the trap room's bone became a dropped sword. Checked in a muted headless Chrome: each count drawn, sliders only for dungeon rooms (none in M's castle). | Dress Up: no skeletons unless asked for. The dungeon rooms are too scary for M. By default no skulls or skeletons anywhere in the castles; each room has its own slider to add them, from 0 up to as many as that room's drawing has (the dungeon 2, the deep dungeon 4). Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 |
^ticket(72) | Dress Up | done 2026-10-14 | DONE 2026-10-14 (LIVE in Dress Up 0.0.29, commit ca17c47, deployed 2026-10-14): two flag colors per castle with three chips (in turn, stripes, checkers), used by the keep and tower flags, wall banners, road pennants, the clearing's far view and the king's room (banners and shields); the letter inked for contrast with an outline, on a badge on patterned banners, so M's letter shows on gold; a roof color per castle. | Dress Up: castle colors. (1) Banners and flags in two colors, chosen for each castle by its owner, used together in one of three ways, picked by chips: alternating (each banner and flag solid, the two colors in turn), stripes, or checkers. (2) The owner's letter always readable: its color chosen for contrast with the banner's (today M's gold letter on a gold banner does not show). (3) A roof color control for each castle. Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 (with a screenshot of M's castle up close) |
^ticket(71) | Dress Up (side quest) | done 2026-10-14 | DONE 2026-10-14 (LIVE in 0.0.33). A sheet over the whole friend: domed head, sleeves along the arms (any pose), wavy hem, folds, an 'o' mouth; ragged 0-100 (round waves to torn points and slits); eyes none/one/two/three (third on the forehead) in the friend's eye style, or black holes with 'hide eyes' (off by default); sheet color (off-white default). Its panel carries the Visibility tab's every-part chips and full-body slider with its LFO chip (a new 'of' control kind shows another tab's control); checked: the ghost chip makes sheet, eyes and mouth 50% see-through; the LFO chip fades them all with LFO A. | Dress Up: a ghost costume that looks like the 👻 emoji but full sized: the friend under a rounded sheet with a wavy hem. One costume, with a few controls (Matthew, 2026-10-14): how ragged its hem and edges are; how many eyes: none, one, two or three (the third in the middle of the forehead, like the three-eyed smiley face on the poster for the movie Evolution); the sheet's color, off-white by default; and the eyes drawn in the friend's own eye style (the face tab's eyes, as the person has them). A "hide eyes" toggle (Matthew, 2026-10-14), off by default: when on, the ghost's eyes are plain black circles, the emoji's eye holes, instead of the friend's own eyes. Its see-through-ness (Matthew, 2026-10-14): the ghost's controls include the visibility panel's all-at-once controls (the every-part chips and the full-body slider), the same settings, so the whole ghost can be made see-through from its own panel, with an LFO option too (the full-body slider's LFO chip: every part follows one LFO). Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 |
^ticket(70) | Dress Up | done 2026-10-14 | Shipped as Dress Up 0.0.27 on Matthew's go-ahead, live and checked (0c8c3fe, tag dress-up-0.0.27). All eight: snow caps cut from their peaks' slopes (snowCap; the castle's mountains a proper range); no bushes in the tree house's stream; castle banners from the battlements, sized to the wall; every cloud drifting (cloudsSvg, drifting); the lake's ducks paddling, bobbing and rocking; the sun's rays turning and breathing (lake, castle); parallel sunbeams that dim and brighten as clouds pass (sunbeamsSvg: the woods and the path through them); and organic trees, pines, bushes, grass and rocks in every scene from shared helpers built on the tree house's leaf pads (leafyTree, pineTree, bushSvg, grassTuft, rockSvg; oakDef and the pine defs now draw from them). Measured: every scene drags and idles at 60 fps. 0.0.28 (f5e7d60, live): on Matthew's report that the ducks swam backwards, each now swims out, pauses to turn round and swims back facing its way (checked at every 4% of each duck's cycle). (where) | Dress Up: fixes and life for all backgrounds. (1) Snowy mountain tops: aligned with the rock of the mountains they cap, not cartoony and misaligned. (2) No bushes in the river near the tree house. (3) Castle banners: they dip into the moat on a one-level castle; shorter for shorter castles, longer for bigger ones. (4) Animate the ducks on the lake. (5) Animate the clouds in every scene. (6) Animate the sun's rays at the lake. (7) The woods' shafts of light: they dim and brighten, appearing and disappearing as clouds move past a sun that stays put (so they mostly keep their shapes); more variety among them, and one common origin, at infinity, so they run parallel, as real sunbeams do. (8) Every tree in the tree house's "pads and branches" style, not lollipops or ice-cream-scoop leaf bundles: a more organic style for all trees, bushes and grasses, and organic rocks where they appear, from common helpers, for consistency. Changes go under Unreleased; no version bump or push until Matthew says. from Matthew, 2026-10-14 |
^ticket(69) | Dress Up | done 2026-10-14 | Committed in kids-choice-games (a4e2384, then 1b71ad1), not deployed: it ships with the next Dress Up version, on Matthew's go-ahead. The icon is the game's own drawing: the tree house with two cat houses (a black cat, #1f1f22, and an amber one, #e8862a), rendered in headless Chrome, rasterized at 512 px and halved down to 16, 32 and 48 px, the small ones lightly sharpened, embedded as PNG data URIs (about 14 KB). First it was two crops side by side (Matthew liked it); on his idea, a new control, "🏡 tree house size" (50 to 250), now grows the tree house's houses apart from the tree, with the bridges and ladders keeping to their platforms, and at 250 one crop of the scene reads as well. The game's favicon request no longer 404s. (where) | Dress Up: a favicon, the tree house with cats. The game has none (its console shows the browser's favicon.ico request failing). Draw the tree house, with cats on it, simple enough to read at 16 and 32 pixels in a browser tab, on light and dark tab bars; embed it in the page as an SVG data URI, so the game stays a single HTML file. Changes go under Unreleased; no version bump or push until Matthew says. Reopened, 2026-10-14: the first version (committed as a4e2384) crops each cat house on its own and puts the two side by side. Matthew likes that, but would rather get the same effect without cropping: add a control that grows the tree house's houses independently of the tree, then render the icon again as one crop of the scene. from Matthew, 2026-10-14 |
^ticket(68) | Dress Up | done 2026-10-14 | Committed in kids-choice-games (not deployed; it ships with the next Dress Up version, on Matthew's go-ahead). The Background tab picks a place in two rows, as the Music tab does: the kind of place (⬜ no background, 🌲 woods, 💧 water, 🏕️ camping, 🏠 home, 🧙 the witch's), then that kind's places, each row in a dashed box. PLACES (the flat list, which Surprise me! and sceneName still read) and PLACE_GROUPS (each place in exactly one kind, checked at load). Picking a kind goes to its first place unless the place is already of that kind; a doorway's new place shows its kind. Checked in the browser, and Surprise me! still picks valid places. (where) | Dress Up: backgrounds by category, as the music is. The Background tab's one long row of places (17 or more, and castles to come in ^ticket(67)) becomes two rows, as the Music tab's kinds and tunes do (TUNE_GROUPS): first the kind of place (for example outdoors, indoors, water, the witch's, castles), each kind's chips in a dashed box with its name, then that kind's places. Picking a kind shows its places, and keeps the current place if it belongs to that kind. Changes go under Unreleased; no version bump or push until Matthew says.from Matthew, 2026-10-14 |
^ticket(67) | Dress Up (side quest) | done 2026-10-14 | Shipped as Dress Up 0.0.26 on Matthew's go-ahead, live and checked (b019aed, tag dress-up-0.0.26), with ^ticket(68)'s backgrounds by kind. N's, M's and D's castles, each with levels (1-5), stone, trees nearby, moat and drawbridge (none to very wide), flags and banners in the owner's color and letter; the clearing shows the one picked as a doorway; a 🏰 castle scene up close, drawn at the tree house's and the witch's woods' level of detail (on Matthew's ask): mountains, forest, sun and birds, stonework, tiled roofs, ivy, lit windows, gate torches, reeds and lily pads, a road with pennants, a hay cart, a well, barrels, flowers, framing oaks. Up to six rooms per castle, each in a style chosen in the Background tab; N's styles: 👑 king's room (throne, carpet, banners, stained glass, chandelier, armor, gold, and the hallway through its open door), 🌿 greenhouse (glass, vines, glass parrots, tropical plants, a running fountain), 🕳️ trap room (one doorway, two spiked pits dropping into the dungeons), 💀 dungeon and ⛓️ deep dungeon (skeletons, chains, cage, cell, mushrooms, dripping water, ladders back up). Every doorway tested. M's and D's room styles are still to come, when they make them up. (where) | Dress Up: three castles, N's, M's and D's, with rooms. The castles: each castle has its own settings: how many levels of castle (1 to 5), the stone's colour, how many trees nearby, and the moat and drawbridge's size (none to very wide). The forest clearing has one place for a castle, so for now a chip menu chooses which of the three stands there. The rooms: each castle has a set of rooms, and each room is one of several styles. The styles are organized by their creator (N, M and D, the castle owners), but any castle can use any style. N's rooms (so far): (1) a trap room: two floor traps someone could fall into (pits with spikes inside) and one doorway; (2) a king's room, where we can see the hallway outside, and a door leading into the room; (3) a greenhouse room: vines and climbing plants on all the walls, windows, glass parrots flying near the vines by the ceiling, bushes and other tropical plants everywhere, with lots of detail, even more than the waterfall, and an animated fountain in the middle; (4) two dungeon rooms, each different, with skeletons hanging on the walls: the rooms the traps lead to. M's and D's room styles are still to come. Mind ^ticket(62)'s lesson: many SMIL animations (the fountain, the parrots) cost frames even at rest, so keep animations few, and check a drag in each room. Changes go under Unreleased; no version bump or push until Matthew says.from Matthew, 2026-10-14 |
^ticket(66) | performance | not needed 2026-10-14 | NOT NEEDED 2026-10-14 night: a smaller leaking slide was a way to fit a heap snapshot; a probe build with ValueHash's memo per synchronous run made Full Body's snapshot fit (2.3 GB, 75 s), and it named the holder (^ticket(58)). | Find a smaller slide that grows the same way (Hazel performance analysis, ^report(14), What next, step 2: "Find a smaller slide that grows the same way, so that a snapshot fits even with the memo: one whose slider edits a livelit's model, measured as here."). On #2597, run ^report(14)'s measurement (014/drivers/heap_per_edit.py, the live heap after two forced collections, per slider edit) on the Livelit Playground's smaller slides, picking a slider that edits a livelit's model (Shirt and Pants' width sliders are not the shirt's own, so that earlier try applied no edit; check that each edit changes the picture). A slide that keeps a version per edit at a fraction of Full Body's size may let a heap snapshot finish, and its retainer paths would name the holder (^ticket(58)).from Matthew, 2026-10-14 |
^ticket(65) | performance | done 2026-10-14 | DONE 2026-10-14 (measured; written into the work notes, http://matthewhammer.org/hazel-work-notes/work-notes/2026/10/14/2026-10-14T2120-0600.html#memory, not yet into the series). ^report(14)'s driver (014/drivers/heap_per_edit.py, snap Chromium, Full Body, shirt-width slider, live heap after two forced GCs) on #2635's head 9fb2bdf067, exact build: about +8.2 MB per edit for edits 1-10 (427 → 512 MB), then a drop of about 48 MB at edit 11, then about +6 MB per edit; +70 MB after 15 edits, against #2597's about 92 MB per edit (about 1.4 GB). Two 15-edit runs, identical. A 30-edit run: it falls back twice, at edit 11 (+93.6 → +46.0 MB) and edit 22 (+108.3 → +53.1 MB), a sawtooth; the likely candidate is ShareCodec's tables, which start over at 200,000 entries (unconfirmed: that needs ^report(14)'s table-count probe build on #2635). So the growth is #2597's own. dev has no Livelit Playground slides, so it cannot be measured there. Data: ~/hazel-perf-report/014/data/update-2026-10-14/ (local, not pushed). | Measure the same on #2635 (Hazel performance analysis, ^report(14), What next, step 4: "Measure the same on #2635 (livelits with modular editors) and on dev, to see whether the growth is #2597's own."). Run ^report(14)'s measurement (014/drivers/heap_per_edit.py: Full Body, the shirt-width slider set 15 times, the page's live heap after two forced collections) on #2635's head and on dev, where the slide exists, and compare with #2597's 92 MB per edit. If a branch does not grow, the difference between it and #2597 points at the holder (^ticket(58)).from Matthew, 2026-10-14 |
^ticket(64) | performance, series | done 2026-10-14 | Published on Matthew's say-so and checked live: ^report(1) to ^report(13) carry their data, as ^report(14) does. tools/report_data.py embeds each report's tables (cell by cell, merged cells repeated) and a hand-written description (builds and commits, runtime, runs, measures and units, provenance) as script#report-data, writes the same to NNN/data/ (^report(1)'s to data/, published as 001/data/), and adds a line to the method or measurement section. ^report(9) to ^report(13) have their raw probe and oracle outputs (about 690 KB, spot-checked against the printed numbers; ^report(9)'s were rescued from a build folder a clean would wipe); for ^report(1) to ^report(8) every number exists only as printed, and each description says so. Three agents wrote the descriptions for ^report(1) to ^report(12), checked against the reports. Not kept: Gecko profiles of 50 to 110 MB in ~/ffprof. Commits: hazel-performance-analysis b3ce476; matthewhammer.github.io ec20a64. (where) | Give ^report(1) to ^report(13) their data, as ^report(14) has it. Matthew's rule (2026-10-14, now in the series' agent-instructions.md): every report carries every number behind its figures and tables twice, (1) embedded in the page as JSON (<script type="application/json" id="report-data">), described well enough to read without the prose (build and commit, browser, each measure and its unit, raw readings, the patch or driver), and (2) as files in NNN/data/, linked from the method section (folders on GitHub). For the earlier reports, gather each one's numbers from its tables, figures and whatever raw files survive (the work notes, the scratch drivers' outputs), say where a number exists only as printed, and add a line to each method section. These are additions, not changes to the story, so they need no dated update; they wait for Matthew's review before publishing.from Matthew, 2026-10-14 |
^ticket(63) | homepage | done 2026-10-14 | Published on Matthew's say-so and checked live: http://matthewhammer.org/hazel/ is now "Matthew Hammer and Hazel", in the homepage's style (its outline and Pages menu, with light and dark themes), with text in his voice: Hazel and his part in it, what he works on now, the performance reports (all 13 listed, newest first), games with M and N, how he works (with Claude and the dashboard), and Fumola in Hazel. "The issues" is gone. The list is kept by hazel-performance-analysis's tools/hazel_page.py, now a step in publishing a report (agent-instructions.md). Commits: matthewhammer.github.io aa536a4, hazel-performance-analysis dd977a1. (where) | A new matthewhammer.org/hazel/ page: "Matthew Hammer and Hazel". The page was a link tree, and still called the series "the issues". The new design: in the homepage's own style (its stylesheet and outline, with the Pages menu and an outline of the page), some text in Matthew's voice, and the reports as its focus for outsiders. It says what Hazel is and Matthew's part in it, what he works on now (livelits, and making them fast), lists every report of the performance analysis series (kept up to date from the series index by a tool, run when a report is published), the games with M and N as the test bed, how he works (the Hazel Work Dashboard, with Claude), and Fumola in Hazel. A draft, previewed locally; it waits for Matthew's review before publishing. from Matthew, 2026-10-14 |
^ticket(62) | Dress Up (performance) | done 2026-10-14 | Measured, fixed, committed in kids-choice-games (3a27f9f), not deployed: it ships with the next Dress Up version, on Matthew's go-ahead. Measured in headless Chrome over the DevTools protocol (real mouse events, a 60-move drag of the friend, frames timed in the page). Before: the witch's woods took 8.1 s for the drag, frame gaps median 67 ms and p95 250 ms; inside the hut 4.2 s, p95 183 ms; the forest and the house took 2.0 s at 60 fps. The JavaScript was cheap everywhere (changed() about 2 ms); the cost was rendering. (1) draw() wiped the whole picture with innerHTML and put the cached scenery back, so the scene's 459 SMIL animations were torn out and re-registered on every move; now the scenery stays in place and the rest is replaced around it (woods 8.1 to 3.9 s). (2) The fumes cost even at rest, about 15 fps in the woods with no input; Matthew's idea, pausing the picture's animations while a drag is held, finishes it: both scenes now take 2.0 s at a steady 60 fps, like a scene without fumes. Scene switches and the no-background picture checked. (where) | Dress Up performance fix: dragging the friend where the cauldron's fumes turn (outside the witch's hut and inside it). The double-helix fumes make a drag slow enough to be a real UX problem. Likely why (from the code, not yet measured): every pointermove calls changed(), whose draw() replaces the whole picture (pic.innerHTML = svg); helixSvg draws each strand as short segments, about a hundred in all, each with four SMIL <animate>s on x1, x2, stroke-width and stroke-opacity, so every move re-parses them and restarts every animation, and the geometry they animate is repainted each frame. Matthew's idea: pause animations while dragging (pic.pauseAnimations() on pointerdown, unpauseAnimations() on letting go). Other ideas: draw at most once per animation frame however many pointermoves arrive; on a figure drag, move only the friend (its group's transform) and leave the scene's DOM alone, so the fumes neither restart nor re-parse; keep the scene in a group of its own that is replaced only when its settings change; make the fumes cheaper (fewer segments, or turn a group with a transform the compositor can animate). Measure first (performance before features): a drag's frame times in both scenes, before and after, against a scene without fumes. Changes go under Unreleased; no version bump or push until Matthew says.from Matthew, 2026-10-14 |
^ticket(61) | Dress Up (side quest) | done 2026-10-14 | Built and tested, committed in kids-choice-games (dd86f24), not deployed: it goes out with the next Dress Up version, on Matthew's go-ahead. "🧍 full body: moves every part" sits under the "every part at once" chips and above the seven parts. Matthew chose "same amount": every part moves by the master's change, keeping their differences, each stopping at 0 and 100; the master shows the most visible part. A drag starts from where the parts were, so dragging back restores them (checked: head 54, rest 100; master to 40 gives head 0, rest 40; back to 100 gives head 54 again). Listed under Unreleased in VERSIONS.md. Corrected in 0.0.25 (2026-10-14, live and checked, 62c3699), on Matthew's report that it did not work as he meant: moving the slider now snaps every part to exactly its value, wherever they were (no longer by the same amount); it keeps its own value (visible.all) and dims, showing "mixed", once a part moves on its own; the every-part chips set it too, and now read visible, ghost, invisible. (where) | Dress Up: a "full body" master slider in the 👻 Visibility tab (a follow-up to ^ticket(51), the invisible main character). One more slider, "full body", above the seven parts' sliders (head, eyes, hair, shirt, pants, feet, hands) and below the "every part at once" chips (invisible, visible, ghost): moving it moves the others with it, globally. Open choice, for Matthew: whether every part follows by the same amount, keeping their differences (clamped at 0 and 100), or is set to the master's value, as the chips do. If ^ticket(57) lands first, the master could take one of the three shared LFOs, as each part will. Changes go under Unreleased; no version bump or push until Matthew says.from Matthew, 2026-10-14 (with a screenshot of the Visibility tab, 0.0.23) |
^ticket(59) | dashboard, series, Dress Up | done 2026-10-14 | All three repos' READMEs link their public pages first, with no localhost recipe: the dashboard at http://matthewhammer.org/hazel/work-dashboard/ (new README; the address redirects to the repo's own site, keeping #ticket-N, and closed.html now redirects too), the series at http://matthewhammer.org/hazel/performance-analysis/, and Messed Up Dress Up at http://matthewhammer.org/kids-choice-games/dress-up/. Each app links its repository: the dashboard's outline ("On GitHub", beside "Performance reports"), the series index (beside its link back to the dashboard), and Dress Up's quiet "GitHub ↗" chip after 🎵 Music, released as 0.0.23 (tag dress-up-0.0.23) on Matthew's go-ahead and checked live. (where) | Each repo's README links its public page, and each app links its repo. For the Hazel Work Dashboard (matthewhammer/hazel-work-notes), the performance analysis series (matthewhammer/hazel-performance-analysis) and Messed Up Dress Up (matthewhammer/kids-choice-games): (1) the GitHub README links the public URL on matthewhammer.org, prominently, at the top; (2) the app itself links its GitHub repository: the dashboard, the series index, and Dress Up, where it can be discreet: a last chip after the final top-level menu panel chip. Dress Up's change waits for Matthew's go-ahead to deploy, like any other (no version bump or push until he says). from Matthew, 2026-10-14 |
^ticket(58) | performance | done 2026-10-14 | DONE 2026-10-14 night: the holder is #2597's page-level undo stack. History.Model.undo_stack keeps a whole page model per edit (syntax, statics, layout of that version); its cap applies only when cap_undo_stack is on, and it is off by default, so ^report(14)'s undo test (250 → 2) never ran. Found with a probe build (ValueHash's memo per synchronous run, so snapshots work), two snapshots in one session and a dominator tree: copies of Full Body's derived data at undo_stack[0] and [1]. Confirmed: the cap always on at 2 stops the growth after 3 edits (284 MB after 12, against 1,048 as built). Fix ported from Andrew's 6010128b7d (on #2469/#2635): local branch perf/2597-compact-undo bc66ae5616 in ~/hazel-2597-probe, 159 MB after 12 edits, undo and redo checked in the browser, Undo tests pass. dev has the same uncapped stack. Pushing is ^ticket(86). Work notes: http://matthewhammer.org/hazel-work-notes/work-notes/2026/10/14/2026-10-14T2120-0600.html#holder | FOUND 2026-10-14 night (was parked): what holds about 85 MB per edit of Full Body in a #2597 tab. From ^ticket(45), which measured it and ruled out every cache and table (its outcome, and the work notes of 2026-10-14). The holder is not a table: it keeps whole versions of the editor's state. A heap snapshot would name it, but snapshots kill this page's renderer (snap Chromium and Chrome for Testing 155 alike, with the hang monitor off; a trivial page and a million-deep chain of arrays are fine). Lead: a page with a WeakMap of 2 million live entries did not finish its snapshot in 400 s, and ValueHash's memo is a WeakMap of about that size: try a probe build with ValueHash's memo off (or bounded), and take the snapshots then. Other ways in: a smaller slide that leaks (Shirt and Pants' sliders are not the shirt's width; find one that edits a livelit model), or closures held by the browser (listeners, timers, observers) on a render path. Lead, 2026-10-14 night (^ticket(65)): #2635's head grows only about 8 MB per edit and falls back by about 50 MB at edits 11 and 22 (a sawtooth over 30 edits, likely ShareCodec's tables starting over), where #2597 keeps about 92 MB per edit and never falls back: the holder is something #2597 keeps that #2635 does not. Look at what #2635 changed or dropped (dev and #2469 merged in) before trying the snapshot again; work notes: http://matthewhammer.org/hazel-work-notes/work-notes/2026/10/14/2026-10-14T2120-0600.html#memory FOUND, 2026-10-14 night: the page-level undo stack. #2597's History.Model.undo_stack keeps a whole Page.Model.t per edit, with that version's syntax, statics and layout, and its cap only applies when the setting cap_undo_stack is on, which it is not by default (Settings.re: cap_undo_stack: false). So ^report(14)'s undo test (the size 250 → 2) changed nothing: the cap never ran. How it was found: a probe build with ValueHash's memo off made heap snapshots possible (2.3 GB, 75 s); two snapshots in one session (after edits 3 and 6), a dominator tree (29M nodes), and the copies of the editor's derived data sit at Logged.current.undo_stack[0] and [1]. Confirmed: with the cap always on (at 2), #2597 stops growing after edit 3 (284 MB after 12 edits, two runs, against 1,048 MB as built). #2635 has the fix already: Andrew's 6010128b7d (2026-08-26, on #2469) compacts each undo snapshot (drops syntax, statics, eval state; rebuilt on restore) and always caps. Next: port that compaction to #2597 (its ScratchMode differs), check undo in the browser, measure; local only, push on Matthew's word.from ^ticket(45), 2026-10-14 |
^ticket(57) | dress up | done 2026-10-14 | DONE 2026-10-14. (1) The fairy costume and (3) the three shared LFOs shipped in 0.0.29; (2) the fairy forest is LIVE in 0.0.33 (commit 1627f0d): a glade reached by a sign in the clearing, time-of-day chips (day, sunrise, sunset, night), a ring of toadstools (count slider, glowing at night), a great tree with a fairy house (door, lit windows, fungus steps, balcony, lanterns, chimney) whose see-through slider and LFO chip (A/B/C, in time with the music, listed on the mixing board) work; fireflies at dusk, sparkles by day, the camp site's star settings at night. Measured drags: redraws like the woods'; 0-5 single 33 ms frames per 2 s drag (noisy, no one part in A/B). | Messed Up Dress Up: a fairy costume, a fairy forest, and LFOs for what shows. (1) A fairy costume: wings in 5 styles and a fairy tunic in 5 styles, each style with its own default colours and colour choices. Hair, face and hands look the same as without it; the feet take styles that come from the costume. (2) A fairy forest background: a special forest area with a ring of mushrooms; time-of-day chips (day, night, sunrise, sunset); a fairy house in a tree, which appears with a transparency control and can follow one of the three LFOs (fading in and out with the music's beat). (3) LFOs for visibility, the mixer's own: each body part's transparency on the 👻 Visibility panel (^ticket(51)) can follow an LFO, optionally: a chip per part for none or an LFO. The same LFOs as the mixing board's (^ticket(52)), and only three: A, B, C (Matthew, 2026-10-14), set from either place: one set of three, used by the drums' pitch, the parts' transparency and the fairy house's. Three, not today's four, so the music panel lays out as a quad: the main mix controls and the three LFOs. (Today's tunes use A to C only; D goes.)from Matthew, 2026-10-14 |
^ticket(56) | performance, series | done 2026-10-14 | Published in ^report(12), in place, as if it had been there from the start (no dated update). Figure 1, under the opening: the body drawn from its tree, each part outlined in its branch's colour (body, arms, head, hands) and labelled with what places and shapes it; it is also the report's new thumbnail on the series index. Figure 3: the whole tree in four subtrees (body, arms, head, hands), each node with its transform and the sliders that change it. The figures are numbered 1 to 6 in reading order. Matthew reviewed it ("looks good") and said publish. Commits: hazel-performance-analysis f807c3c, matthewhammer.github.io edf105c. (where) | Pictures of the body as a symbolic tree of transforms and parts, in ^report(12) ("Moving a Group, Not Its Points": Full Body rebuilt as a picture of groups, each with a transform). Draw the figure's tree: each group a node labelled with its transform (a translate, a rotation about a joint, a scale), and the parts (the shapes) at its leaves, as the slide builds them; best beside the drawn figure, so a reader sees which node makes which part, and, for a slider edit, which nodes it changes (the picture diff the report measures). The pictures illustrate what ^report(12) already says, without changing its story, so they go into the report in place, where it describes the tree, not as a dated update (Matthew, 2026-10-14); he reviews before they are published.from Matthew, 2026-10-14 |
^ticket(55) | #2583, review | done 2026-10-14 | DONE 2026-10-14. The answers page, published on Matthew's ask (generated by Claude, labelled so): http://matthewhammer.org/hazel-work-notes/work-notes/2026/10/14/2026-10-14T1903-0600.html (one request type, parsing apart from applying, tests, startup as an action, slide_name off Globals, panel names in Sidebar.re, all 13 comments answered with links, an order for the work). The short reply posted on Matthew's OK: https://github.com/hazelgrove/hazel/pull/2583#issuecomment-6182030558. Nothing on the branch changed yet; Matthew's three calls are open (Share target here or later; copied span; no tutorials). (where) | Respond to the change requests on #2583 ("Deep links: the URL can say which slide, which panel, and where the cursor is", Matthew's PR, branch deep-links). Review by 7h3kk1d, 2026-09-23, changes requested: "Feature works. Most feedback here is surface level but I think it deserves a once over before I approve." The conceptual point: two separate URL encodings now, the scratch pad's and the documentation slides' by name, and neither works in the tutorial or other modes; tests would be nice. The 13 inline comments: in DeepLink.re, a type for the whole deep-link request, with parsing the query separated from applying it to the settings and the editor, so that parsing, the editor actions and making the URL can each be tested without a browser (pass the query param in; the PanelRequest type is the pattern to follow); what key is for; List.find_index (OCaml 5.1); the panel's enumerate/show mapping in one place, or in Sidebar.re; a comment that reads like "a poem or ancient translation"; in Page.re, whether tutorials should be shareable; in Globals.re, handling it as an action in Main.re like the others, and slide_name not belonging on Globals (non-blocking); in CodeEditable.re, why the enclosing term is selected rather than the caret's position shared. Its output (Matthew, 2026-10-14): a work-notes page, published in hazel-work-notes and shared on #2583 in reply to the review (the comment waits for his OK before it is posted). The page says plainly that Matthew asked for it and Claude generated it.from Matthew, 2026-10-14 |
^ticket(53) | performance, series | done 2026-10-14 | Published on Matthew's say-so and checked live: ^report(14), "What the page keeps from one edit to the next". A #2597 tab keeps about 92 MB more after every slider edit on Full Body; not the caches (each tried in its own build), not any long-lived table (each counted after every edit); what stays is whole versions of the editor's state; the holder is open (^ticket(58)), since heap snapshots kill the tab. Four figures, the data as JSON in the page and as files in 014/data/ (the model for Matthew's new rule, and ^ticket(64) for the earlier reports), the experiments' patches in patches/, the drivers in 014/drivers/, a dated note in ^report(5), the card on the series index and the line on the /hazel/ page. Commits: hazel-performance-analysis 9c548bd, 35a5e53, 8be276a; matthewhammer.github.io be12468. (where) | Series ^report(14): what the page keeps from one edit to the next. From ^ticket(45). ^report(5) listed what the page keeps of a view (the view memo's Html, the worker's and the page's tables of shared values, the last virtual DOM tree, the DOM) and said "what memory these take has not been measured". ^ticket(45) measures it: on #2597, a tab keeps about 85 MB per edit of Full Body (+1.4 GB over 15 edits), and none of those representations is the holder. The report: the measurement (live heap after forced GCs, per edit, in headless Chromium driven over the DevTools protocol); the method where heap snapshots crash the tab (V8's sampling heap profiler: what stays alive, by the code that allocated it; a census of the tab's Maps and Sets); what was ruled out and how (the whole-program memos, the undo history, the projector view cache, ShareCodec's tables, each by an experiment); the audit of every cache and weak table (^ticket(45)'s audit); and the holder and its fix, once found. Matthew, 2026-10-14: plan on writing it unless there's a surprise reason not to. Drafts wait for his review before publishing.from Matthew, 2026-10-14 (from ^ticket(45)) |
^ticket(52) | dress up | done 2026-10-14 | Messed Up Dress Up 0.0.22, live (kids-choice-games f87eb70, tag dress-up-0.0.22): four LFOs A to D on the mixing board (once round in 1 beat to 8 bars, triangle / saw / sine, a depth up to an octave), a chip on every bongo and conga for none or A to D, each hit taking the wave at its step; the classic beats' bongos and beach calypso's second conga set their own (the tracks revised once more, as asked). (where) | Messed Up Dress Up: beat-based LFOs for the drums' pitch. A side quest, after ^ticket(49). (1) Four LFOs, A, B, C and D, set by knobs in a row on the mixing board before the parts' own strips: each LFO has a speed, from one beat (the shortest) to 8 bars (the longest), counted in the tune's beats so it stays in time with it, and a shape: triangle, saw or sine. (2) On each drum part with a pitch (today the bongos' and congas' 🎵 pitch knob): a chip next to the pitch that cycles none, LFO A, B, C, D; the chosen LFO moves that drum's pitch up and down as the tune plays. (3) Then revise the tracks once more, in all, for the new setting: some would gain from pitch moving, the bongos most (Matthew, 2026-10-14). To settle while building: how far an LFO moves the pitch (its depth: one more knob, or a fixed amount), and whether the setting is the friend's (saved with it) or the device's.from Matthew, 2026-10-14 |
^ticket(51) | dress up | done 2026-10-14 | Built (kids-choice-games 77da1c0, under Unreleased, not pushed): the 👻 Visibility tab, a slider per part (head, eyes, hair, shirt, pants, feet, hands) and 🫥 invisible / 🙂 visible / 👻 ghost chips. An invisible shirt is an invisible body (neck and arms too), invisible pants are invisible hips and legs; the eyes go on their own; a ghost is evenly see-through where a part is drawn in two layers (masked). Matthew: "looks perfect". With it, his tweak to the music menu (4660a5b): kinds and tunes each in a named dashed box. | Messed Up Dress Up: a visibility panel for each person. A new control panel per person, visibility: a slider for each separately controlled body part, as a group, for how see-through it is: head, eyes, hair, shirt, pants, feet, hands. Above the sliders, three chips that set every part at once: invisible (all parts gone), visible (all parts shown), ghost (every part half visible). Making the shirt invisible makes the torso invisible: this turns body parts ghostly or invisible, it never takes clothes off to show a body underneath. The same holds for pants and the legs, feet and shoes, hands and gloves. from N (via Matthew), 2026-10-14 |
^ticket(49) | dress up | done 2026-10-14 | Messed Up Dress Up 0.0.21, live (kids-choice-games 342fd65, tag dress-up-0.0.21): inside every tent (its own things, its door onto the camp at the camp's time and fire), the cave behind the falls (the falls seen from behind, by their own sliders), the path through the woods (clearing to trail and back, a third arrow on the trail's sign), landing on the ground everywhere (by the fire, on the pool's bank, beside far doorways). With it, at Matthew's word: a usual figure size per place (100 on the slider is the place's own). The collision shapes stay ^ticket(50). (where) | Messed Up Dress Up: new places behind the camp, the falls and the clearing, and landing on the ground. A side quest from N. (1) Inside the tents: at the camp site, every tent (up to the tents slider's most) gets an inside of its own, a background of its own, and a way to go in and out both ways by a new spinning teleport (today's doorways zoom, flyInto). (2) The cave behind the waterfall: not shown from outside; a background for it, and doorways both ways. (3) The clearing's front path: a path toward the front of the forest clearing (the bottom of the picture) leading to the trail system (the mountain trail, whose signpost reaches the falls and the camp), and a way back from the trail. Matthew's correction asked for a background for it, and portals back and forth: read here as a scene of its own for the path (clearing to path to trail, both ways); if a direct doorway was meant, drop the middle scene. (4) Land on the ground: arriving at the camp site, never in the fire; at the waterfall, never in the water. Pick ground spots per place for now; the general version (a shape per background for where feet cannot stand) is ^ticket(50).from N (via Matthew), 2026-10-14 |
^ticket(48) | dress up | done 2026-10-14 | Messed Up Dress Up 0.0.20, live (kids-choice-games 9cfa091, tag dress-up-0.0.20): music by kind (8 kinds, a kind row of chips then its tunes); three classic beats of our own, about 3 min each (bongo breakdown, canyon cruise, skatepark strut), every player in front in turn, six new instruments; ➖ | 🎵 interval | ➕ under the transpose slider (half step to octave, octave first) and on each pitched channel, up to 3 presses. Side quest ended at Matthew's word. (where) | Messed Up Dress Up: music by category, and transposing by interval. (1) Categories: a small number, about 8: reggae, house, groove, waltz, calypso, classic beats, and a few more of our choosing. Every category gets at least one song, a new one where none fits yet. The 17 tunes today could seed most of them: reggae (sunny reggae, bubble reggae, turtle steppers dub), calypso (beach calypso, beach soca jump-up, steel-pan lullaby), waltz (spooky waltz), and chases (tree-house, duck-pond, round-the-house); the rest to sort. The music picker becomes two chips: song category, then track. Classic beats gets three new funk / hip-hop instrumentals in the spirit of "Apache" (Incredible Bongo Band), about 3 minutes each, with sections that feature, in turn: bass guitar, bongo drums, drum kit (funk playing), organ, and rhythm and lead guitars, so each part can be heard on its own (and on the mixing board). Our own tunes in that style (breakbeat kit, bongos, organ, twangy guitar), not Apache's melody or its break. (2) Transpose by interval: next to the transpose slider (today ±24 half steps), three chips: minus | interval | plus. The interval chip cycles through every interval up to the octave: half step, whole step, minor 3rd, major 3rd, 4th, tritone, 5th, minor 6th, major 6th, minor 7th, major 7th, octave; the octave is the default. Minus and plus move the slider by exactly that interval. (3) The same per channel (each part on the mixing board): the three chips only, and in place of a slider an indicator of the channel's setting, none or −/+ X × the interval, where X counts the presses of minus or plus, up to 3. To settle while building: what the indicator shows if the interval chip changes between presses. from Matthew, 2026-10-14 |
^ticket(47) | performance, series | done 2026-10-14 | ^report(13), "Telling the Request What Changed", published (homepage a5c37be, series d6efedb), with a dated note in ^report(1). Building a release's request projection now takes under 1 ms on every Playground slide (from 1.9–11.9 ms); what is left grows with how far down the program the slider sits. Ported to #2469's head locally (branch port-2469-t7 in ~/hazel-port-2469, not pushed): one change per step, quick suite passes whole. dev and #2597 need per-item statics first. Linked 2026-10-14 from #2609 (finding 30), #2635, #2597 and #2469 (a progress note, no @-mention, offering the ports). (where) | ^report(13): carrying ^ticket(7)'s improvement to branches diverged from ours. #2635 9fb2bdf067 and 043508921b (DefStatics tells EvalInfo the keys it touched; unchanged spine roots kept) cut a release's worker request on Full Body from 15.5–18.8 ms to 2.5–3.0 ms. Write how to apply that to a branch that has diverged (#2469's modular-editors, dev, #2597): what it depends on, what to port first, how to check it, with plots of the data. A draft for Matthew to review before it is published.from Matthew, 2026-10-14 |
^ticket(45) | performance | superseded 2026-10-14 | Measured and narrowed, not fixed; the holder hunt is parked as ^ticket(58). On #2597, Full Body, a tab keeps about 85 MB per slider edit, steadily (397 to 1,105 MB over 8 edits). Ruled out by experiment: the whole-program memos, the undo history, both together, the projector view cache, ShareCodec's tables (bounded, reset at 200,000). Ruled out by counting every table keyed by ids after each edit (a probe build, local branch count-45 in ~/hazel-ci-heap): Statics.module_livelits, LivelitProj's seven tables, MakeTerm's maps, HazelDB, AppStore all flat. What stays (V8's sampling heap profiler): whole versions of the editor's state, statics, terms, layout, the worker's replies, and ValueHash's hashes of them. The audit of every cache and weak table, and the method: work notes 2026-10-14 (12:09). Heap snapshots, which would name the holder, kill this page's renderer in the snap Chromium and in Chrome for Testing alike. (where) | A tab's memory grows about 85 MB with each edit of a big slide, on #2597; the holder is not yet found. Full Body, the shirt-width slider: the live heap after forced GCs goes 399, 482, 562, 656, 749, 830, 907 MB over six edits (+1.4 GB after 15). Ruled out by experiment: the whole-program memos (held to one entry), the undo history (capped at 2), both together, the projector view cache (its store off), and ShareCodec's tables (bounded: reset at 200,000 entries while the heap kept growing). What stays (V8's sampling heap profiler; heap snapshots crash this tab): whole versions of the editor's state, statics, terms, layout, the worker's replies, and ValueHash's hashes of them. The audit (work notes, 2026-10-14): every weak table is a JS WeakMap and none is the cause; no store on the ordinary path adds a version per edit by construction, so the holder is a table keyed by ids that change between edits: Statics.module_livelits, LivelitProj's last_good_view / view_memo / computed_shapes, or AppStore (its gc never called). Next: a build that reports each of those tables' sizes after every edit; clear the one that grows, measure; then its fix (an owner and a lifetime, not a weaker reference), the same on #2635, and the series report (^ticket(53)).from ^ticket(4), 2026-10-13 night |
^ticket(10) | performance | superseded 2026-10-14 | Superseded by ^ticket(18). Measured 2026-10-12, remembering the virtual DOM per Html subterm would save at most about 2% of the main thread on a woods figure move; evaluating the view is about 69%, which ^ticket(18) (evaluating each livelit's view in the worker) goes after. Closed on Matthew's word, 2026-10-14. | Option C from ^report(5): remember HazelDOM's virtual DOM per Html subterm, so an unchanged part of a view is not converted again and virtual_dom's diff skips it. PARKED 2026-10-12: measured, it would save at most about 2% of the main thread in the woods. A profiled figure move (2 runs, 4.4 and 4.1 s): Html to virtual DOM 1.1%, diff and patch 0.9%, browser layout and paint 1.4%, GC 21%, evaluating the view about 50% (about 69% with its GC), the app's own update machinery about 25%. On Kids' Choice it was 8 of 104 ms (^report(1)). It would also need a content hash as its key, since the woods' shapes are fresh objects each time, and handlers that find the current state at event time. The lever is evaluating the view: ^ticket(1), ^ticket(2), ^ticket(18).from ^report(5) |
^ticket(5) | performance | superseded 2026-10-14 | Explained in part, by the evaluator. On both slides the figure is one binding that depends on every slider, so every change evaluates 85–95% of the picture again (^report(11)); and the witch's picture is bigger: 133 shapes against 49, 205,665 evaluation steps from scratch against 128,393 (^report(10)). In node that is 754 ms against 499 ms per edit (^report(9)): about a quarter of a second, not the seconds seen in the browser. The rest of the browser's gap (statics, the views, drawing 133 shapes) was never profiled on this slide; ^report(8) profiled one change phase by phase, on another slide. What shrinks it on every slide is reuse below a binding, by names and groups (^report(11) and ^report(12)), and the per-change costs in ^ticket(23), ^ticket(24) and ^ticket(45). Closed on Matthew's suggestion. (where) | The Witch slide's extra cost over Full Body for the same change, from ^report(4) (2026-10-12): a skin press took 6.5 s against 4.1 s before that report's three fixes, and 2.2–4.5 s against 1.5–1.8 s after them, one run each. Written before ^report(9)–^report(12) measured what a small change costs; revised 2026-10-14.from ^report(4) |
^ticket(4) | performance | done 2026-10-14 | #2597's CI is green again (red since 2026-10-11): quick tests 8.5 min, full tests 53 min (limits 20 and 100). Two causes, both from the Dress Up slides: the whole-program memos kept every test's programs (out of heap), and the recovering parser on the big slides (out of time, ^ticket(46)). Pushed 2026-10-14: #2597 5d576cc479 (Statics.mk keeps 16 programs, MakeTerm's memos 8, as in #2469), 8798d64213 (programs over 25 KB get the recovering-parser checks in the weekly run only), c4e411f9c2 (the big slides' Unproject cases in the full tests only); #2635 the last two (its memos were already bounded): its quick tests 585 s and full 41 min, no longer timing out, red only for its 8 known failures. Found on the way: ^ticket(45) (the editor's memos keep about 82 MB per edit on #2597, fixed there by the same bounds) and ^ticket(46) (the parser). (where) | #2597 CI red since 2026-10-11 afternoon: full tests time out at 100 min, quick tests run out of heap. Locally, the whole suite in one process runs out of memory after about 96 min. from CI runs on #2597 |
^ticket(44) | performance, research | done 2026-10-13 | Done (2026-10-13): ^report(12) published on Matthew's go-ahead at matthewhammer.org/hazel/performance-analysis/012/ (homepage 076ec93, series cfdb7c6), checked live over HTTP, with dated notes in ^report(11) (its What next, step 1, and its representation section). | ^report(12): write up ^ticket(38)'s results, a picture of groups with transforms (^report(11), What next, step 1). An experimental copy of Full Body whose picture is a tree of groups with transforms (placement, arms, hands, eyes), flattened to today's picture exactly; its picture diff on the same 27 slider edits as 011 (at most 14 nodes, was up to 39 shapes); and ^report(11)'s trace distance and update cost on it, with the tree flattened at the end and not (as a renderer that draws groups would take it). Draft locally, with figures, for Matthew's review; publish only when he says.from Matthew, 2026-10-13, in chat: write up the 012 results when the second run finishes |
^ticket(40) | dress up | done 2026-10-13 | Side quest done and closed on Matthew's word, after the kids tested. Released Messed Up Dress Up 0.0.12 to 0.0.19 (2026-10-13), each on his go-ahead: the asked-for tunes (four chase tunes, two new synths) and backgrounds (lake; house and yard; tree house with animal houses, bonsai pads, a braided trunk, a stream, a swing and a hidden crown; rooms inside the tree houses, numbered, with ways between them that match the bridges and ladders outside, checked for all 252 layouts; a living room with a window and pictures of chosen scenes), then by his asks while the kids played: doorways between scenes with a spin-in and a back chip, feet like hands (toes and talons 1-100), per-house colors, the witch's hut inside, a forest clearing, a mountain trail, a jungle waterfall and a camp site, far views drawn from each place's own settings. Scenes are built from parts kept between moves (named by their inputs, or by the options they read), and the page keeps each piece while its text is unchanged. Ideas for later: tickets ^ticket(41) (places to sit), ^ticket(42) (scenes that move), ^ticket(43) (postcards). | Messed Up Dress Up: chase tunes and new backgrounds (a side quest, timeboxed to 2 hours). Tunes: more like bat-wing boogie, fast high-energy dance or chase music, good at 250%+ tempo, synth parts layered, drums in and out. Backgrounds: outside a tree house system (several houses, ladders, bridges; after LEGO Ideas 21318 Tree House); inside each house, as a real place; a living room (couch, pillows, pictures of the other backgrounds on the wall, their detail on a 0-100 slider); a lake (ducks, cattails, a small beach, sparse grass at the shore, sun, puffy clouds); a brown two-level house with an attached garage on the right, driveway, a tree each side, a stone front walk, toys on the lawn, bushes, flowers. from Matthew, 2026-10-13, in chat |
^ticket(39) | performance, research | not needed 2026-10-13 | By design, in Matthew's words: for a small face or body, the head's geometry and everything connected to it (the hair, and so on) is affected by eye size; the head shape is meant to adjust to the eyes, so it is very dependent. The large trace distance of the eye sliders is that dependence, not waste. | Look at what the eye sliders' work computes (^report(11), What next, step 5), and whether its answers could be cut off sooner.from Matthew, 2026-10-13: ^report(11), What next, step 5 |
^ticket(38) | performance, research | done 2026-10-13 | Done for Full Body (2026-10-13), closed on Matthew's word, published as ^report(12), "Moving a group, not its points". The tree, flattened, is today's picture exactly; every value edit changes at most 14 nodes (was up to 39 shapes). In steps against today's from-scratch run (121,973): the tree alone takes 110,260; not flattened, its trace distance follows the picture diff (arm angle 20,206 to 5,104 steps); flattened at the end, the gain is gone; Hazel's update today does not change. Follow-ups are 012's What next steps (draw the groups, find the floor, flatten only what changed, Witch and Scenes, a scalable outline), each mintable as a ticket. | A picture of groups with transforms in the Drawing library, and shared symbols for repeated shapes (^report(11), What next, step 1). Today a picture is a flat list of polygons in absolute coordinates, and the library places a part by moving every one of its points, so a slider that moves a part changes every shape in it (up to 113 shapes for one edit). ^report(11) estimated, at best, at most 14 changed nodes per slider edit with groups and transforms, and one for the red eyes with a shared symbol. Then measure the picture diff, the trace distance and the update cost on the same 63 slider edits. Progress, 2026-10-13 (paused for ^ticket(40)): an experimental copy of Full Body (full-body-groups.hz, local only), placement as groups (Stage A) and arms and hands in their own coordinates (Stage B); flattened, it draws today's picture exactly (28 pictures, to 6e-14). Tree diff against the flat one: arm angle 30 shapes to 8 nodes, arm length 30 to 8, hand angle 24 to 2, shirt width 39 to 13, neck 9 to 4, smile 8 to 5. 2026-10-13 evening: hand size as a scale group: 24 shapes to 14 nodes (each hand's fill grows with its group; a fingered hand's outline is a band of fixed width, 2.5, so its 12 shapes are made at the hand's own size); still exact (28 pictures, to 5.7e-14). Then the eyes as groups in the face's own units (each eye placed and grown in one group, turned and narrowed inside): eye size 7 shapes to 5 nodes, eye gap 4 to 2, eye angle and shape 2 to 2; still exact (to 5.7e-14), counted with a 1e-9 tolerance in both pictures (a shine turned and turned back moves 9e-16). Now no value edit changes more than 14 nodes (hand size), and none more nodes than the flat picture changes shapes. Next: the trace distance and update cost with 011's harness, the tree flattened at the end and not.from Matthew, 2026-10-13: ^report(11), What next, step 1 |
^ticket(37) | performance, research | done 2026-10-13 | ^report(11), "Naming what a slider leaves alone", published 2026-10-13 on Matthew's go-ahead, with dated notes in 001, 005, 009 and 010 on the naming each reuse claim assumed. All 63 slider edits; terms set with Matthew: picture diff, trace distance, update cost. Names for calls after Fumola's merge sort: on value edits all agree; on structural edits naming by operand comes closest to content. Reads add at most 0.8 points, list cells up to 5.1. The eye sliders' cost is the mouth-clears-eyes check. Representation: with groups and transforms every slider edit would change at most 14 nodes (vs up to 113 shapes). Next candidates (not ticketed): a Drawing picture of groups, transforms and shared symbols; edits with intermediate diffs; timing a memo keyed by operand. | ^report(11): the reuse ladder, how close Hazel could get to the ideal. Extend ^report(10)'s oracle: (1) sweep every slider on the Full Body, Witch and Scenes slides, and a few on other Playground slides, and show the spread of shapes changed and work left; (2) list-cell names: name a call from its argument's name, not its position, so a walk over a list does not run its spine again; (3) reads instead of mentions: record the variables a call actually reads, not every one its body mentions. The ladder: today's run, a memo by position, a memo with names and reads, and the floor, the changed shapes' own work. Then stage 2, timing a memo, on the best design.from Matthew, 2026-10-13, in chat: publish 010, then the sweep and the depth work, as ^report(11) |
^ticket(36) | dashboard | done 2026-10-13 | current_waiting in data.json (true, or a line saying what for, shown as the badge's tooltip): ✋❔ in a pill with a 3px CanvasText border, next to the current ticket's heading, on its row in the Tickets table and in the outline; and at the front of the page's title, so a browser tab shows it unopened. Dashboard 'Unreleased'. | Show when the current ticket waits on Matthew: a raised hand and a question mark, ✋❔, next to the current ticket's heading, in a bold, high-contrast rounded box. from Matthew, 2026-10-13, in chat |
^ticket(35) | dashboard, series, homepage | done 2026-10-13 | An Auto / Light / Dark button, top right, on every page: the dashboard (both pages, local and published), /hazel/, and the performance analysis series (index, 001-009), remembered per site in localStorage. Pages that already had data-theme hooks got the button (tools/add_theme.py in the series repo); the dashboard got the hooks too (dashboard 'Unreleased'). Checked: each mode, persistence across reloads and pages, live over HTTP. The homepage's root page has no dark mode, so it has no switch. | A theme toggle on every page (Auto / Light / Dark): pages follow the system's prefers-color-scheme, dark on Matthew's work laptop (Ubuntu, dark high contrast), light on his personal one. The choice kept in localStorage per origin (one for all of matthewhammer.org: homepage, /hazel, the series, the published dashboard; localhost:8000 its own), Auto by default. The series and /hazel already have data-theme hooks in their CSS; the dashboard needs them. from Matthew, 2026-10-13, in chat |
^ticket(34) | dashboard | done 2026-10-13 | Each page carries a version tag (a hash without the Updated line); every 90 s in view, and on coming back to the tab, it fetches itself and reloads on a change, keeping scroll and #ticket-N. Tested: a real change reloaded at the same scroll. The elapsed time is now browser-only, so the page no longer changes every 15 minutes while a ticket is current. | The dashboard refreshes itself, locally and published: today nothing reloads (the one timer only updates the current ticket's elapsed time). Every minute or two, fetch the page in the background and reload only if it changed (compare its body without the Updated line), keeping the scroll position and any #ticket-N. from Matthew, 2026-10-13, in chat |
^ticket(33) | dashboard | done 2026-10-13 | hazel-work-notes is public; its Pages site serves a copy of the page and the closed tickets (noindex, a note, history links on GitHub), written by log_notes.py on each change. matthewhammer.org/hazel/work-dashboard/ redirects there, and the Hazel menu has a work-dashboard chip. (where) | Publish the dashboard publicly, on every change, so Matthew can see where things stand when AFK and off the local network (for him, perhaps Cyrus; not secret, just too detailed for most; no reason to hide it, none to point people at it). A "work-dashboard" chip in the matthewhammer.org/hazel menu, and a similar URL (matthewhammer.org/hazel/work-dashboard/) that goes there; it shows what localhost:8000 shows, at all times. Proposal: make hazel-work-notes public and serve it with its own Pages (log_notes.py also writes the live page and closed.html to the repo root on each change; cron pushes every 15 minutes); a redirect page and the chip in the homepage repo; on the public copy, a line saying the localhost links work only on Matthew's machine, and a noindex meta. Audit 2026-10-13: no tokens, keys or email in its 172 commits; kids only as M and N. Making it public needs Matthew's go-ahead (it publishes the whole history). from Matthew, 2026-10-13, in chat |
^ticket(32) | series | done 2026-10-13 | Thumbnails for 001 and 005-008 are now transparent SVGs with a light and a dark palette, split by tools/theme_variants.py into -light and -dark copies that the index shows by the page's theme (so the switch decides, not only the system); 009's too. 002-004 stay PNGs on a light ground, pictures of the app. The series' publishing steps say how a thumbnail is made. Live over HTTP. | Thumbnails with transparent backgrounds, readable in dark and light themes (Matthew uses a dark, high-contrast theme, where the white blocks look out of place): for every report's card on the series index. Likely SVG thumbnails with their own prefers-color-scheme colours, or PNGs with transparency and colours that hold on both. Also: revise ^report(9)'s thumbnail once its results are final (^ticket(31)). Matthew, 2026-10-13: 008, 007, 006, 005 and 001 need it (white blocks in dark mode); 002, 003 and 004 keep their light backgrounds, since they show how the app looks.from Matthew, 2026-10-13, in chat |
^ticket(31) | performance, research | done 2026-10-13 | Matthew's hunch was right: the hashing was linear, but the side table it was kept in (ValueHash's WeakMap) made each evaluation slower than the last (2.3, 5.5, 6.3 s for the same work). Merkle names kept on the values (a hidden property, computed as a value is built) hold still (2.2, 2.5, 2.2 s) and cost 12 to 28% over fresh ids, not 23 times. ^report(9) updated: a section on it, two corrections, a theme-aware thumbnail. Experiment local: ~/.cache/hazel-diag/merkle_on_object.patch. (where) | Merkle names at construction, then remeasure ^report(9)'s derived-names experiment. 009: content-derived names took the woods' evaluation from 1.9 s to 44.2 s. Today Util.ValueHash is a LAZY Merkle hash: a walk on first use, remembered per object in a WeakMap; TypHash (#2661) remembers types' hashes per object; nothing is hashed as it is built, and environments and closures only as generic values. Fix: compute each node's content name when it is built, from its children's names (already known), an environment frame's from its parent's and its binding, a closure's from its environment and body: a few operations per allocation, never a walk (the paper's constant cost). A content name is separate from the provenance id a step's result keeps for probes, or two values from one piece of syntax would share a name. Steps: (1) profile the derived mode first, to find where the 23x goes (walks, the WeakMap and GC, fallbacks); (2) the construction-time names; (3) remeasure gensyms, names kept and time; (4) then finish ^report(9)'s edits, including a CORRECTION: its claim that a hash that gives up is not remembered is wrong (ValueHash marks it unhashable, and a too-deep walk leaves objects marked busy for good). FOUND 2026-10-13: (a) the hashing is linear within an evaluation (each object walked once: 1,033,746 walked, 932,980 reused, the same every run); (b) but time per evaluation grew across a session (2.3 -> 5.5 -> 6.3 s; 44.2 s after a long run) with the same work and a growing heap: the side table (ValueHash's WeakMap). (c) Names kept ON each value (a hidden property, ~/.cache/hazel-diag/merkle_on_object.patch): flat, 2.2 / 2.5 / 2.2 s, about 1.4x fresh ids (1.6 s), names still kept (33,846 of 33,847). Full remeasure running.from Matthew, 2026-10-13, reading ^report(9) (#derived) |
^ticket(30) | performance, research | done 2026-10-13 | ^report(10), "What a slider change touches", published 2026-10-13 on Matthew's go-ahead: stage 1, an oracle naming calls by position. A slider edit changes 5-16% of a picture's shapes; 75-84% of the steps Hazel evaluates after it are in calls whose inputs did not change (steps, checks free). The rest of the ladder (sweep, list-cell names, reads) is ^ticket(37); timing a memo (stage 2) after that. | ^report(10): names inside evaluation, for the reuse that would pay (^report(9), What next, step 3). Apply Incremental Computation with Names further to Hazel: a demanded computation graph at the right granularity (units below the top level: calls and right-hand sides, named from the call site, the enclosing unit and the argument's name), the reads each unit makes recorded with what it saw, an early cutoff where a result keeps its name, and inputs (a slider's literal) as named references. An experiment on a local branch, then ^report(10) with its preliminary results. Groundwork in ^report(9): ^ticket(2) per-step costs, the derived-names experiment (content names cost 23x; names from a parent's name are the paper's way).from Matthew, 2026-10-13: ^report(9), What next, step 3 |
^ticket(28) | performance, research | done 2026-10-13 | ^report(9) published, then updated the same day with the paper's main ideas, Hazel's caching in its terms, the derived-names experiment and what following the paper would take. Its step 3 became ^ticket(30) (^report(10)). (where) | ^report(9) of the series: fresh ids and lack of reuse. Hazel mints ids two ways: Id.mk, a gensym (counter + random bytes), and names derived from a parent (Id.derive, Id.next, IdTagged.fresh_deterministic, v5 UUIDs), the distinction of Incremental Computation with Names (Hammer et al., OOPSLA 2015). Measure what Hazel can tell us today: (1) elaboration: analyzing the same program twice, which nodes come back renamed, and how many enclosing parts then fail the reuse check's hash (^report(8)'s 122); (2) experiment: name those nodes deterministically, and what reuse it recovers; (3) evaluation: ids minted per evaluation, and whether re-evaluated values ever share ids (why the call memo erases them). MEASURED 2026-10-13 (node, #2635 c4e64060f1; harness ~/.cache/hazel-diag/Test_Names.re, experiments issue009_experiments.patch, uncommitted): (1) the same term analyzed twice (Statics.mk_unmemoized; the memoized mk returns the same object): Full Body 667 of 111,462 nodes renamed, all minted by statics, 692 expressions then differ by hash; Witch 798/844, Scenes 210/237, Polygons 14/23. Sources: UserLivelit.mk_expand_dot_def (each use's dispatch on def.expand, FreshGrammar), StaticsBase.fresh_ascription, and 85 nodes from the Drawing library. (2) naming the first two by their use / what they ascribe (Id.derive by path): 667 -> 85 renamed, 692 -> 247 hash-differing. After a slider edit, incremental reuse: names held 943 -> 1,002, renamed 105 -> 46, reused 336 either way (the structural fallback recovers it): names change the check's cost, not what is reused. (3) evaluation mints 461,422 ids for the woods picture (33,847 nodes); evaluated twice, equal in structure, not by hash; the only shared ids are the program's own. (4) THE WORKER'S RESPONSE DIFF (ShareCodec, keyed by ValueHash with ids; instrumented build on 8342, ~/.cache/hazel-diag/sharecodec_stats.patch + share_moves.py): 8 woods figure moves, slots sent vs if structure-equal parts were references: worker->page 387,625 vs 347,920 (1.11x), page->worker 43,248 vs 21,728 (1.99x, small: ~5,400 a move vs 749,480 at load). The diff works because incremental reuse returns the same objects (~2,900 refs a move): identity survives through reuse, not names. (5) TIMED, 5 runs: after a slider edit, incremental vs scratch (node, medians): Full Body 502 vs 451 ms, Witch 857 vs 791, Scenes 1,547 vs 1,571; named about the same. DRAFT ^report(9) written 2026-10-13 (local commit in ~/hazel-perf-report, not pushed; generator ~/.cache/hazel-diag/gen009.py + body009.html). OPEN: identity memos with no structural fallback; Polygons' 13 hash-differing with nothing renamed.from Matthew, 2026-10-13, in chat |
^ticket(27) | performance | done 2026-10-13 | Done on #2635 (2026-10-13), closed on Matthew's word: 8717564a5f stops the stream collector's walk at patterns and types (204 ms to 115 ms profiled; the clock change not detectable, ^report(8)). What remains (map_term rebuilding every node, Id.Map inserts) would be a new ticket. | The page's incremental stream collector (StreamCollector.Inc.fresh) indexes the whole elaboration again for every new evaluation, once per slider change: 204 ms profiled per change in C (Full Body, dev mode). DONE ON #2635 2026-10-13: 8717564a5f stops the walk at patterns and types: 115 ms profiled, clock not detectable (^report(8)). REMAINING: map_term still rebuilds every expression node, plus Id.Map inserts. Further: a hashtable, or the worker tags each entry with its enclosing entry so the page needs no index.from ^report(8) |
^ticket(26) | correctness | not needed 2026-10-13 | Its premise was wrong: #2597 has no lexemes (it lacks 500c466b44) and makes an unknown operator a hole of its operands, so @@ and @@@ give the same term. ^report(8) got dated corrections. (where) | #2597's IncrEval.reuse_check never compares lexemes: it lacks 7de3875cd8 ("Invalidate incremental-eval reuse on lexeme-only edits", in dev and #2635), so editing @@ to @@@ in place can show the stale operator's result. Port it in the one-walk form from perf/2635-reuse-one-walk (^ticket(6)), so #2597 doesn't pick up the quadratic cost with it.from ^report(8) |
^ticket(6) | performance | done 2026-10-13 | cbdab791f5 on #2635: the reuse check compares lexemes in the same memoized walk as structure (^report(8): the UX-mode slider 3.5 s to 1.26 s). On dev as cea8f0a98f, a second commit on #2673. Nothing to port to #2597 (^ticket(26)). (where) | IncrEval.reuse_check compared lexemes by trace, quadratic when elaboration ids churn. DONE ON #2635 2026-10-13: cbdab791f5 (one memoized, hash-shortcut walk for structure and lexemes), ^report(8): UX-mode slider 3.5 s -> 1.26 s. NOT NEEDED ON #2597 (checked 2026-10-13, retired ^ticket(26)): it has no lexemes (lacks 500c466b44), an unknown operator is a hole of its operands, and its check is already hash + memoized structure. REMAINING: dev, together with the hash shortcut (dev uses full fast_equal plus two traces): a dev PR, with Matthew's OK.from #2609 finding 24, ^report(8) |
^ticket(2) | performance | done 2026-10-13 | Done (2026-10-13), closed on Matthew's word. A value known to have a type is not walked again for it (#2635 237e5aae74, #2597 e6d1cc500f); per-step tuning pushed: bind(Done(x), f) = f(x) (d0c56fb090) and a step with no effects only counts (c4e64060f1) on #2635, a4b856c543 + 684a75cb2f on #2597. What is left per step is structural (trampoline nodes, continuations, fresh ids): a design question, not this ticket. | Generic evaluator overhead in views: the woods' picture view, ~500 ms per figure move after ^ticket(1). DONE 2026-10-13: a value known to have a type is not walked again for it (#2635 237e5aae74, #2597 e6d1cc500f, comment a8fe28953d / 30f6835b49; dev #2681): Firefox view 563.5 -> 497.5 ms. PROFILED AGAIN 2026-10-13 (node, through evaluate_open as the app runs it; harness ~/.cache/hazel-diag/Test_PicView.timing.re): the view allocates ~1,064 MiB per run, and GC is about as large as its own work. What's left is spread over the evaluator's machinery: the trampoline (Bind/Next nodes, continuations; ~240 MiB), EvaluatorState.update copying the state record every step (~66 MiB), Transition.transition (~129 MiB), string building that is the view's own work (coords, ~76 MiB), fresh ids. Ascription pushing fell 143 -> 61 ms. TRIED, not kept yet: bind(Done(x), f) = f(x): view 407 -> 375 ms in node, ranges overlapping; a stack-depth risk to check (patch ~/.cache/hazel-diag/trampoline_fast_bind.patch). FOUND, load-time only: the first consistent_by_hash miss on the Html types costs tens of ms (memoized for the session after). PER-STEP TUNING 2026-10-13 (his option 1), PUSHED: bind(Done(x), f) = f(x) (d0c56fb090, with 2 deep-recursion tests) and a step with no effects only counts (c4e64060f1, EvaluatorState.tick), on #2635; #2597 a4b856c543 + 684a75cb2f. Woods allocation -8% (program 4,334 -> 3,980 MiB, view 1,064 -> 980); Firefox view eval 579 -> 558 ms (-3.6%, 20 moves per build, overlapping). Tests 987 on #2635, 974 on #2597. What is left per step is structural (trampoline nodes, continuations, fresh ids): a design question.from #2609 findings 28, 29 |
^ticket(21) | series | done 2026-10-12 | github.com/matthewhammer/hazel-performance-analysis, public, with the series' history and its own agent-instructions.md. Snapshots moved to dashboard-snapshots/YYYY/MM/DD/. | After ^report(7): make a new PUBLIC repo, hazel-performance-analysis, for the series, with its git history (~/hazel-perf-report) and its agent instructions (audience: outside readers; the work notes are our working record). hazel-work-notes stays as it is. Also: snapshots in dashboard-snapshots/YYYY/MM/DD/.from Matthew, 2026-10-12, in chat |
^ticket(20) | performance | done 2026-10-12 | Opened as #2679 (copy-on-change ascriptions, against dev). | A dev PR for copy-on-change ascriptions (#2597 1fa1384445): on dev, pushing an ascription through a list or tuple rebuilds it whatever happens inside, the same as before the fix. It is general: any typed boundary a big value crosses.from ^ticket(1), part 3 |
^ticket(11) | performance | done 2026-10-12 | Measured in ^report(5)'s update: in a woods figure move, evaluating the view is about 50% of the main thread (about 69% with its GC); Html to virtual DOM 1.1%, diff and patch 0.9%, layout and paint 1.4%. That parked ^ticket(10). (where) | Split the woods' redraw of a new picture into evaluating the view, Html to virtual DOM, diff and patch, and the browser's layout, for one figure move. from ^report(5) |
^ticket(9) | performance, CI | done 2026-10-12 | ^report(6) published, with its PR, #2678 (evaluator-step budgets in CI). (where) | ^report(6) of the series, before Wednesday (from the meeting with Cyrus): performance-regression testing in CI, with #2161 weighed against starting fresh in a table. The PR is open: #2678. DRAFT written 2026-10-12: Matthew to review, then publish to the series.from Matthew, 2026-10-12 |
^ticket(8) | series | done 2026-10-12 | ^report(5) published: the worker, the page, and diffing the view. (where) | ^report(5) of the series: the worker versus the main thread, and diffing the view (Html to virtual DOM, on which thread). Drafted, then published 2026-10-12.from Matthew, 2026-10-12 |
^ticket(3) | performance | done 2026-10-12 | ^report(7) answered why: the page redrew the whole code view and redid statics for each of 2,520 worker messages. c07bdacead (from #2469) went to #2597 as a15c6f0b65: Scenes 287.5 s to about 16.5 s to its first picture. (where) | #2597: the Scenes slide takes about 5 minutes to its first picture (287.5 s cold on 2026-10-12), the Witch slide 173.1 s; #2635 about 12 s. WHY (^report(7), published): the page redraws the whole code view and redoes statics for each of 2,520 worker stream messages; the worker idles. from #2469 (stream posts every 100 ms, lazy info-map projection) on a local copy of #2597 (~/hazel-2597-stream, served on 8312): Scenes 16.6–17.6 s, Witch 18.6–19.4 s, 86 messages. Next: bring it to #2597 itself (his call), then the adaptive cadence from .from ^report(7); #2609 findings 25, 28 |
^ticket(1) | performance | done 2026-10-12 | Three fixes, on #2597 and #2635: a Drawing.frame sets the view box, a cheaper coords, and an ascription pushed through a value gives the value back when it already has the type. A woods figure move's redraw went from a median of 2.20 s to 1.41 s. Written up in #2609 finding 29 and ^report(4)'s update. What was left of the view became ^ticket(2). | The ^picture view in the woods: parts 1–3 DONE 2026-10-12, a woods figure move's redraw from a median of 2.20 s to 1.41 s. Part 1: a Drawing.frame sets the view box (#2597 86e3ea9a9f, b5edde9da3; #2635 6f1d7fdc5e, 44d8ab2225). Part 2: a cheaper coords (#2597 44928f5fdd; #2635 ba72773d46), view steps −26% but time −6%. Part 3: an ascription pushed through a value gives the value back when it already has the type, instead of copying it (#2597 1fa1384445; #2635 57c928cef4). The picture's view on a move to a new size went from about 725–830 ms to 520–650; revisits stay about 210 ms; Kids' Choice is unchanged. That gave the memo cheap keys and kept its hits. Measured and not kept: a key budget (it lost the revisits) and a cache of pushes (nothing over copy-on-change). What is left of the view is the evaluator's own work, about 500 ms: see ^ticket(2). Close ^ticket(1)?from #2609 findings 27–28 |