/* #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
*/