/* #p8v2_ring_viz_goal_20260817 @commentary_gen5_e64 @viz_standard @viz_surfaces @um_viewer @p8v2_gen3_viz_goal_20260810 @root GOAL: bring the umr.html ring view into the e64 instrument, as its main view Written 2026-08-17 in answer to #commentary_gen5_e64, MJC's review of the generation-5 instrument. That block is one screen and it is the source; read it too, but nothing below depends on having read it. This block is a spec, not a build, and it is the whole of what this push is. FOR A FRESH SESSION: everything you need is here. ## The one-line version docs/pprog/p8v2-gen5-e64/index.html currently draws settling as a GRID of positions. MJC wants the main view to become the UM-NATIVE RING VIEW already prototyped at docs/umr.html: rings, faint lines, mouseover annotations on everything. The grid stays. This is a port, not a reuse -- see "Why it is a port" below. ## What MJC asked for, in his words "For gen6 I want most of the changes in the viewer. First we're going to move to a different UM-native perspective that has been prototyped elsewhere. Specifically, at cmpr.ai/hutter/umr.html we have a viewer that uses rings, faint lines, and mouseover annotations on everything. This is the main view that I want. However, it would be more natural here if it goes from left to right, as this the memory chain structure as described everywhere I've written about it. The point of a ring is that it shows the full picture of an LPP such as the k=2 one which is captured as 65536 values. The settling will make more sense visually in this kind of context." "A lot of things about the UM viewer are actually quite nice such as the bpc, surprise, replay capability, and so on. The top-right SN button that swaps the view with a straight textual representation of the model is also quite useful. Literal values are on the right and derived on the left; let's keep this." "Ideally, the patterns and where they are learned from in the data can all be displayed together, with the patterns taking inspiration from umr.html and the grid staying as it is; it is natural that the patterns will take up more space than the grid, so they'll have to be displayed separately and not one-to-one with the grid positions (visually it just wouldn't fit)." "I'm spending a lot of time on getting this right because it's going to only become more important as we scale up (both DSS and P)." THAT LAST LINE IS THE BRIEF, NOT A PLEASANTRY. This viewer is an instrument for MAKING generation 6's choices, not a report on generation 5 -- "then by looking at it I'll make the choices that lead to gen6". Build it to be driven. ## IN PLACE. THERE IS NO gen6 PAGE AND NO FREEZE MJC, 2026-08-17: "there's no point in keeping the gen5 version of e64 frozen, we can just improve it in place ... most of the work between here and gen6 will be done on the viewer, but the end result will just be that the gen5 viewer gets a lot better, and then by looking at it I'll make the choices that lead to gen6." So: EDIT docs/pprog/p8v2-gen5-e64.tpl.html AND build-p8v2-gen5-e64. Do NOT fork a p8v2-gen6-e64/. #viz_standard's "Versioning a viewer" was amended the same day and now says what supersedes is RUNS, not work: a live generation's page is improved without limit, and the freeze fires only when a new generation has run something. Generation 5's runs are still the current ones. ## The source: what docs/umr.html is, and where it lives block #um_viewer (hutter.c), marker Hutter:UMViewer. build hutter-make umr -> extract 'Hutter:UMViewer.' docs/umr.html served https://cmpr.ai/hutter/umr.html size ~1746 lines, self-contained HTML + inline JS It is the browser counterpart to the C runner (umr) and a CONSUMER OF .sn FILES. Its own block warns: read #umr_spec for the math, #umr_core for the C forward pass this JS must match exactly, #umr_sn_io for the SN format. Its stated features, verbatim from #um_viewer: - Model picker: dropdown of available .sn models on the server - Data picker: dropdown of data chunks (1K, 4K, 16K enwik9) - Local file picker: load .sn model + raw data from disk - 3D ring visualization: one ring per ES, neurons glow by support - LPP connections: bezier arcs from source to target events - Byte ribbon: context window with current position highlighted - Side panel tabs: State (ES activity), Dist (output distribution), SN (live state) - Playback: step forward/backward, play/pause, speed control - Keyboard: arrows, space, home/end, pgup/pgdn THE MECHANISMS TO READ BEFORE PORTING, all in docs/umr.html: ringPositions(esIdx) one ring per event space. n events at angle (i/n)*2pi on a circle of radius RING_RADIUS = 140, shrunk for small ES: r = n <= 64 ? RING_RADIUS * (0.3 + 0.7*n/64) : RING_RADIUS. Rings are stacked on the Y axis by ringYs[], spread evenly over totalHeight. THIS IS THE AXIS THAT HAS TO BECOME LEFT-TO-RIGHT. project(x, y, z) the 3D projection. Canvas 2D (getContext('2d')), not WebGL, not SVG -- so it is plain arithmetic and portable, and it is why mouse drag and wheel zoom are cheap (canvas mousedown/wheel handlers). esColor(idx) per-ES colour. #ribbon / #ribbon-bar the byte ribbon; ribbonEls[] one element per position. #side-panel FIXED ON THE RIGHT, 300px, tabs state / dist / sn. #controls fixed BOTTOM LEFT: speed, threshold, prev/next/play/reset. #btn-sn-view the SN button in the header; swaps in #sn-fullscreen, a straight textual rendering of the model (#sn-full-body). #hover-tooltip the mouseover annotation layer MJC means by "annotations on everything". surprise / avg-bpc predictions[] carries {dist, surprise, actualByte, activeEntries}; surprise = -log2(p) per position, avg-bpc the running mean. LITERAL RIGHT, DERIVED LEFT is MJC's instruction and the arrangement above is what it describes as built: the right panel carries the model's literal state (ES activity, the output distribution, the SN text) and the left carries the controls and the driving view. Keep the convention; do not infer more from it than that. ## Why it is a PORT and not a reuse -- read this before estimating umr.html reads .sn UM models, fetched at run time through docs/model-registry.js and docs/models.json. THE p8v2 INSTRUMENT IS A DIFFERENT WORLD ON BOTH COUNTS: 1. FORMAT. A p8v2 model is a binary file (P8V2 magic, 48-byte header, k=1 argmax table at offset 48, backward LPP at 304, 4 bytes per k=2 rule, then gap and trace sections). It is read by docs/pprog/build-p8v2-gen5-e64 in Python, NOT by any JS. There is no .sn for it. Nothing in umr.html's loader is reusable. 2. NOTHING IS FETCHED. The instrument BAKES its data: the builder writes one JSON payload into the template at /*DATA*/null, and the page's own footer promises "Self-contained: no external stylesheet, no script, no font, no image, nothing fetched." KEEP THAT PROMISE. umr.html's model picker, data picker, local file picker and registry are all OUT OF SCOPE for this reason -- the instrument is one sample and one set of variants by construction, and its "which model" control is the D/E/F axis buttons, which select among BAKED variants. WHAT IS REUSABLE IS THE DRAWING AND THE CHROME: ringPositions, project, esColor, the bezier arc rendering, the ribbon, the tooltip layer, the SN swap, the playback transport, and the surprise/bpc readouts. Take those as a reading, not as a copy-paste: umr.html is a different page with its own CSS variables and its own state. ## The mapping onto p8v2, which is the part that has to be got right AN EVENT SPACE IS A POSITION. In settling, each of the W positions in the window holds a 256-entry activation vector, and that vector IS an event space -- the page already calls it that ("the log support values of the settled event space"). So: ONE RING PER POSITION, 256 events on it, each event's glow its log support. At e64, W = M = 64, so 64 rings of 256. LEFT TO RIGHT IS THE MEMORY CHAIN. umr.html stacks rings on Y. Here they go along X, in position order, because that is the memory chain structure. This is the single largest change to the ported drawing code and it is the thing MJC asked for by name. THE FAINT LINES ARE PATTERN APPLICATIONS. In umr.html the arcs are LPP connections source event -> target event. Here the three applications that reach a position each step are exactly that: the forward k=1 from the left neighbour, the backward k=1 from the right neighbour, the k=2 token from two back. Drawing them between adjacent rings is what makes settling legible -- and it should show the BIDIRECTIONALITY, since that is the whole point of settling and the thing a left-to-right layout could otherwise hide (see "the causal pass" in #viz_standard). THE RING IS WHY THE k=2 LPP CAN BE SEEN AT ALL. MJC: "the point of a ring is that it shows the full picture of an LPP such as the k=2 one which is captured as 65536 values." A 16x16 grid cannot show 65536; a ring of 65536 points can, as a dense band. This is a distinct surface from the per-position rings and is worth its own ring. USE THE UM'S WORD (#viz_standard). One settling iteration is a TIME STEP IN f, never a "sweep"; what a time step is built from is a PATTERN APPLICATION, never a "message". umr.html's own vocabulary is fine where it is a UM word (event space, support, LPP). ## What is already on the page -- do NOT redo any of it A short push landed 2026-08-17 answering four items of #commentary_gen5_e64. All four are live and all four must survive this one: - THE CONCORDANCE CONCORDS. The cause was padding: table.kwic td carried padding:0 .15rem, td.kx another .5rem, and the th carried the same, which forced a one-character column wider than its content. All zeroed across the four text columns (kl / kn / kx / kr); only td.kp keeps a right gutter. IF YOU TOUCH THAT CSS, THE CONCORDANCE STOPS CONCORDING. - THE CONTROL-TO-AXIS PANEL. details.axp / #axpb, filled on hover of any [data-set] button by axPanel(kind, v) from axes.json; axpOpen and axpLast live outside render0 so the rig can be rebuilt while stepping without blanking it. This is MJC's own fix for "the controls don't directly connect to the axes" and it is why the axis list keeps the vector's digit order rather than being reordered. - AXIS A IS TAGGED RETIRED AND SPLIT in the axes list (RETIRED map in renderAxisDefs). - THE CHAIN: activations -> patterns -> input positions. pinBlock(pkey) opens a pattern's succDist + concordance IN PLACE under the application that fired it, and [data-gopat] jumps to the same pattern in the browser. Wired in wireDetail(), NOT in wire(), because wire() runs before #pane-f is filled. Pattern keys are "k1:" and "tok:,"; the backward direction deliberately has NO key, because under B1 it is reconstructed rather than stored and the page will not mint an atom for it. Also live and load-bearing: four panes as tabs under the grid (f / patterns / chain / ladder), the f panel that IS f, the LATD proof, the "Is any of this probability?" section with its four gaps, and the ladder pane reporting unrecorded positions because settled_ok is partly circular. ## The push, as items 1. THE RING VIEW, AS A PANE FIRST. Add it as a fifth pane alongside f / patterns / chain / ladder before making it the main view. Rings left to right, one per position, 256 events each glowing by support, faint arcs for the pattern applications of the current time step, mouseover annotation on everything. It must be driven by the SAME state the grid is: the existing st{}, sel, step and cur (runF's frames), so stepping f moves both. DO NOT FORK THE ENGINE. runF is the page's f and is conformance-checked; the ring view is a second rendering of its frames and nothing more. 2. THEN PROMOTE IT. Once it reads well, it becomes the main view and the grid moves to where the panes are. THE GRID STAYS AS IT IS -- MJC said so explicitly. Do not redesign it while moving it. 3. THE CHROME MJC NAMED: the transport (step forward/backward, play/pause, speed), the SN swap button top-right (the p8v2 equivalent is the query layer's SN atoms, which the page already carries in D.sn -- event names verbatim from --patterns), and the hover-tooltip layer. Keep literal values right, derived left. 4. PER-POSITION bpc, WITH ITS ANNOTATION. Softmax the settled vector, take -log2 of the true byte's share. THE ANNOTATION IS NOT OPTIONAL and MJC wrote it: "here bpc doesn't relate to the compression because we are storing a memory trace and not entropy coding. However, the bpc still measures how far away a position is from truth, and when the argmax is wrong, that's interesting. It also gives a sense of what the model is doing 'under' the argmax, and everything that the argmax throws away." Say that where the number is, or it will be read as a compression rate -- which #hutter_metrics forbids and #viz_standard is emphatic about. 5. PATTERNS BESIDE THE DATA, NOT ONE-TO-ONE WITH IT. "the patterns and where they are learned from in the data can all be displayed together, with the patterns taking inspiration from umr.html and the grid staying as it is; it is natural that the patterns will take up more space than the grid, so they'll have to be displayed separately and not one-to-one with the grid positions." So: a pattern surface in the ring idiom, beside the grid, linked to it -- not an overlay on the grid cells. NOT IN SCOPE, and each for a reason: the model/data/file pickers and the registry (the instrument bakes, nothing is fetched); any change to runF or to the conformance check; any new variant or run (that is generation 6 and it is MJC's call); WebGL (umr.html is canvas 2D and that is enough). ## What must still be true when you are done - Every combination of the controls renders without throwing. There are 168 of them (H1..H7 x G in {1, pass, att} x B1..B2 x four panes) and they were all checked after the last push; check them again. - H2 still conforms EXACTLY -- 0 of 64 endpoints, conv 41 against 41. It is the one decay rule that draws no entropy, so it is the only row where a mismatch is a real disagreement about f rather than the stochastic stream the page cannot replay. If it stops conforming you have changed f, which this push must not do. - The page is still self-contained and still built by build-p8v2-gen5-e64, never hand edited. Rebuild and check the footer's promise is still true. - Nothing on the page is presented as a result, and there is no chart point. - #viz_surfaces is updated. It is the map, it already carries a REVIEWED line pointing at #commentary_gen5_e64, and it is the block an agent reads before touching any page. ## Numbers you will want, so you are not hunting for them e64: M = W = 64, one window. 256 events per position. smax = 2. 303 patterns (256 k=1 + 47 kept k=2). 59 unrecorded positions. e10k: 1330 patterns (256 + 1074), 5349 unrecorded. The instrument is e64 only; the ladder pane carries e1k and e10k as counts from the dumps. The stated k=1 strength is 8 and is read from axes.json's constants, never held in the page. The k=2 LPP is 65536 entries; only v025 serializes it, and the builder reads it from there for every variant because they share one learned model byte for byte. ## Open, and these are the programmer's "Feedback inline." 255. A. THE RING GEOMETRY AT 64 RINGS. umr.html draws a handful of ESs; here there are 64 positions at e64, and 128 at the W of a real window. Sixty-four rings of 256 events side by side is 16384 points. Does the ring view show ALL positions at once (a band of rings, each necessarily small), or a NEIGHBOURHOOD around the selected position at readable size, with the rest as the existing grid? Proposal: neighbourhood, with the grid remaining as the whole-window view -- but this is a design question about what the instrument is for and it is yours. Neighborhood; this is what I was getting at with the grid vs rings not being one-to-one in the comments I made on the notes on gen5 viz originally. -MJC B. WHAT THE 65536-ENTRY k=2 RING IS FOR. It shows the full picture of the LPP, which no other surface can. Is it a view of the MODEL (one ring, always the same, a property of what was learned) or a view of the STATE (which entries are live at this position and time step)? The first is a page to look at once; the second is part of settling. It's three rings because it connects three memory cell positions. Maybe four because we also need to visualize the AND of the first two. It should be a view of the model, in one mode, showing the recorded strength bytes and which events they connect (e.g. as visual attributes of the lines. In another mode it is restricted to 255 activation on one event in a particular ring (e.g. this can be a mouseover mode) and the projection onto the other rings is shown. A click or something could freeze this to allow fixing the event on more than one ring at a time. In the forward pass and the settling stage, these lines become active, with pulses of some kind as we experiment with the frequency-based settling ideas. This is worth building and shipping before anything else. -MJC C. WHETHER THE RING VIEW REPLACES THE GRID OR JOINS IT. MJC says the grid stays and the ring is "the main view I want". Item 2 above reads that as: ring promoted to main, grid kept and moved. Confirm, or say which is primary. Neither is primary, one must be above the other but both are equally important. If they are to be merged somehow, I can't quite visualize it and I don't think that's a good idea. -MJC D. bpc AGAINST WHAT DISTRIBUTION. Softmax of the settled vector is the obvious reading and #f-p8 licenses softmax only under conditions the settled vector does not meet (assignment, absolute antecedent, before f runs -- see gap 4 of the page's own probability section). Report it as a distance from truth as MJC describes, and say the derivation is not there? Or wait for the document gap 4 asks for? Softmax of the log support of each event in an ES is the probability distribution by definition. I'm not sure what #f-p8 is referring to, but something has been misread somewhere. It's worth including this if nothing else because it eliminates any misunderstandings about what an ES is, which apparently is an ongoing source of confusion. -MJC ## Sources #commentary_gen5_e64 MJC's review; the source for this push #um_viewer what umr.html is, and the warnings that go with it #umr_spec, #umr_core the UM math and the C forward pass umr.html matches #umr_sn_io the SN format (relevant only as background: p8v2 has none) #viz_standard binding on everything published; the versioning amendment, the UM's-word rulings, and the "causal = left-context" gloss that must not be carried onto f #viz_surfaces the map of every page we own; update it #f-p8, #f-p8-forward, f itself, and the two axes generation 5 split it into #f-p8-decay #hutter_metrics why no per-byte rate over a model file, ever #variant_protocol why the picks are not ours docs/pprog/build-p8v2-gen5-e64 the builder; run it to rebuild docs/pprog/p8v2-gen5-e64.tpl.html the template; edit it, never the output docs/umr.html the prototype being ported */