From 682e0da9954354f5d49ceeae76d030b5d117ef03 Mon Sep 17 00:00:00 2001
From: onatozmenn
Date: Fri, 31 Jul 2026 18:05:53 +0300
Subject: [PATCH] Walk through it instead of filming it
The last box on #14 asked for a recording that works with the sound off. A
recording of this page would be a copy of what the compiler said on the day
it was filmed, and this page's whole claim is that it prints what the
compiler prints. The two would part company and the recording would win,
because it is the thing that looks authoritative.
So the walkthrough moves through the page that is already here. Five steps,
a caption saying what to look at, four and a half seconds each. No sound to
turn off and nothing that only works if you can see colour: the caption
carries it and the section is scrolled to.
Auto-advance is off entirely under prefers-reduced-motion, and anything that
means "I am reading this myself", a wheel, a touch, a key, ends the tour
rather than fighting it.
The two playground links still carried 0.2.1, which the playground would have
warned about for no reason since the programs did not change. tools/check.mjs
now reads the version out of every share link on the site and compares it to
the pin, verified by writing 0.1.9 into one and watching it complain.
Closes #14
Co-authored-by: GitHub Copilot
---
assets/style.css | 27 ++++++++++++++++++
assets/walk.js | 66 +++++++++++++++++++++++++++++++++++++++++++
one-clause/index.html | 20 ++++++++-----
tools/check.mjs | 12 ++++++++
4 files changed, 118 insertions(+), 7 deletions(-)
create mode 100644 assets/walk.js
diff --git a/assets/style.css b/assets/style.css
index 4e830f6..78e5c53 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -160,6 +160,33 @@ pre.code b {
max-width: 68ch;
}
+.walk {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 0.75rem;
+ margin: 1.5rem 0 0;
+}
+
+.walk .summary {
+ margin: 0;
+}
+
+[data-step] {
+ scroll-margin-top: 2rem;
+ transition: color 0.2s;
+}
+
+[data-step].here {
+ color: var(--accent);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ [data-step] {
+ transition: none;
+ }
+}
+
.next {
display: grid;
grid-template-columns: repeat(3, 1fr);
diff --git a/assets/walk.js b/assets/walk.js
new file mode 100644
index 0000000..03148dc
--- /dev/null
+++ b/assets/walk.js
@@ -0,0 +1,66 @@
+// The thirty seconds a recording would have shown, without the recording.
+//
+// A video of this page would go stale the moment the compiler said anything
+// differently, and this page's whole claim is that it prints what the compiler
+// prints. So the walkthrough moves through the page that is already here
+// rather than a copy of it filmed in the past.
+//
+// No sound to turn off, and nothing that only works if you can see colour: the
+// caption says what to look at and the section is scrolled to.
+
+const BUTTON = document.getElementById("walk");
+const CAPTION = document.getElementById("caption");
+const STEPS = Array.from(document.querySelectorAll("[data-step]"));
+
+const STILL = matchMedia("(prefers-reduced-motion: reduce)");
+const PAUSE = 4500;
+
+let at = -1;
+let timer = null;
+
+function show(index) {
+ at = index;
+ const step = STEPS[at];
+ for (const other of STEPS) other.classList.toggle("here", other === step);
+ CAPTION.textContent = `${at + 1} of ${STEPS.length}. ${step.dataset.step}`;
+ step.scrollIntoView({
+ block: "center",
+ behavior: STILL.matches ? "auto" : "smooth",
+ });
+}
+
+function stop() {
+ clearTimeout(timer);
+ timer = null;
+ BUTTON.textContent = at + 1 < STEPS.length ? "Keep going" : "Start again";
+}
+
+function next() {
+ if (at + 1 >= STEPS.length) {
+ stop();
+ return;
+ }
+ show(at + 1);
+ // Someone who wants to read a step should be able to. Auto-advance is off
+ // entirely when the reader has asked for less movement.
+ if (!STILL.matches) timer = setTimeout(next, PAUSE);
+ else stop();
+}
+
+BUTTON.addEventListener("click", () => {
+ if (timer) {
+ stop();
+ return;
+ }
+ if (at + 1 >= STEPS.length) at = -1;
+ BUTTON.textContent = "Pause";
+ next();
+});
+
+// Anything that means "I am reading this myself" ends the tour rather than
+// fighting it.
+for (const event of ["wheel", "touchstart", "keydown"]) {
+ addEventListener(event, () => {
+ if (timer) stop();
+ }, { passive: true });
+}
diff --git a/one-clause/index.html b/one-clause/index.html
index b05673a..797cf41 100644
--- a/one-clause/index.html
+++ b/one-clause/index.html
@@ -41,7 +41,12 @@
One clause, two worlds.
wrong, and why.
-->
-
Two functions
+
+
+
+
+
+
Two functions
Identical, except for one line. They are
demo/read_only.deed and
@@ -82,7 +87,7 @@
read_write
-
Two worlds
+
Two worlds
Compiled to WebAssembly components, the import section of each module
is its world. It lists what the module asks a host for, and
@@ -108,7 +113,7 @@
read_write.wasm imports
code is the manifest.
-
What the host does with it
+
What the host does with it
A host that offers Io.read and not Io.save
runs the first module and refuses the second. The refusal is not a
@@ -134,7 +139,7 @@
What the host does with it
process executes.
-
None of this is checked by eye
+
None of this is checked by eye
The import sections, the host's refusal and the listing printed
further up this page are all pinned in the compiler's repository, and
@@ -156,7 +161,7 @@
None of this is checked by eye
import section with nothing to notice the prose no longer matching.
-
Try them
+
Try them
Both programs open in the playground, running the same compiler this
page is describing. Add Io.save to the first one's
@@ -166,14 +171,14 @@