Oct 2026 · Grasphouse

Programming in a Glass House

Drag a House, Rewrite a Program

Contents

Load a program that draws five houses in a loop. Grab the third house and pull it to the right. The house follows your pointer, and in the editor a 55 becomes a 65 — while a chip near the cursor offers the alternative: press Tab, and instead the 20 becomes a 50, shifting the whole row rather than spreading it out.

Two honest answers, both correct inversions of the same arithmetic, and you choose by watching each one happen.

That interaction is why Glasshouse exists, and nearly everything in its architecture is downstream of making it trustworthy: a small language called Chalk, a browser studio around it, and the verification machinery this report describes, shipped together as v0.1.0.

The central claim this project tested is that an environment's promises about a program are only as strong as the language underneath them — that observability you can trust is a property of semantics, not of tooling. Every capability described below was purchased with a constraint accepted in the language design months earlier.

Glasshouse studio — house-row.chalk live demo
paper(color: #FAF9F5)

to house(at: base, height: h):
  rect(left: base.x, top: base.y - h, …)
  triangle(a: …, fill: #8A3324)
end

repeat 5 as i:
  house(at: (20 + i * 55, 220),
        height: 40 + random(…))
Drag the third house. Press Tab mid-drag, or click a card, to switch between the two honest answers — the code updates either way. Before either edit is shown, the demo re-computes every house from the edited literal: the dragged house lands under your pointer because the arithmetic was actually inverted, not animated.

What learnable programming asks for #

The term comes from Bret Victor's 2012 essay, and the vision it carries is larger than any feature in it: a programming system — language and environment designed together — built around how people think and learn, and answerable at every point to one question: does this help a person see, and does something true end up in their head? His argument for why such a system is needed: programming as normally taught forces people to work blind: the learner holds invisible state in working memory, mentally unrolls loops, guesses what a line will do to a world she cannot see, then types into a void and hopes. People don't fail at programming because the concepts are hard. They fail because the activity, as constructed, is unobservable.

The essay stands, by its author's own account, on Seymour Papert's Mindstorms (1980): Victor's reference note says that designing a learning system without a solid understanding of that book's principles is like designing a mechanical system without understanding “the lever.” Papert's levers surface throughout what follows, and the roadmap's closing section audits this project against them directly.

We distilled the essay into five demands at the start of the project, and they became its phase structure.

The essay does not leave these abstract. It shows each one working.

Back to contents


What Victor demonstrated, and what he left open #

The essay was written in 2012, prompted by an online environment for learning to program that had been built on Processing.js. That gives it an unusual concreteness: it takes a real system and shows, interaction by interaction, what it should have done instead.

Vocabulary. He takes a Processing call — four bare numbers inside ellipse(...) — and points out that the learner has no way to know which is the width. The mockup: hovering a name reveals an explanation in place, with a labeled diagram tying each argument to the thing it controls, attached to the call rather than filed in a manual.

Flow. The centerpiece. Code on one side, picture on the other, and a slider that moves through execution: as you drag, the drawing assembles one call at a time and the responsible line highlights. A second demo unrolls a loop so that every iteration is visible at once, rather than collapsed into one blurred repetition.

State. Values shown beside the code and updating as you scrub. He singles out Processing's hidden mode state — a fill() call setting a color that persists invisibly across later statements — as a case where the environment cannot explain a picture, because the language refuses to keep a shape's determinants local to it.

Reacting. Dragging a number in the source and watching the picture respond continuously (an interaction he had shown the previous year in Inventing on Principle), plus autocompletion that previews what a call will draw before you commit to it.

Abstracting. The most ambitious sequence: start from a drawing made of literal coordinates, replace magic numbers with named variables, fold repeated statements into a loop, lift a block into a reusable procedure. Concrete first, general afterwards — climbing the ladder of abstraction by editing what is already on the screen.

The demos are interfaces, not mechanisms #

These demos are interactive, but each one animates a specific, hard-coded example. That was the right form for the argument — it makes an interaction legible in seconds, and the essay is a critique rather than a release announcement. But it means the demos establish the interface while leaving the mechanism open, and the mechanism is where the difficulty lives.

A mockup of a scrubber does not have to say what happens when the learner edits the code mid-scrub. A mockup of an unrolled loop does not have to decide what to show at ten thousand iterations. A mockup of "fold these three shapes into a loop" does not have to prove that the picture is unchanged afterwards — the illustration simply shows it unchanged. Victor is explicit that these interactions are means rather than ends, and that a language and its environment have to be designed together; what the essay does not do, and does not claim to do, is supply the machinery that would make any of it true for programs the reader writes rather than the ones the demo ships with.

What making them real requires #

Promote each demo from illustration to feature and it turns into a specific engineering demand:

Read as a list, that is the structural claim the essay makes and most readers skim past: these are not editor features. Four of those six demands are constraints on semantics, and the last is a demand for a canonical definition of program output. A language either guarantees them or leaves them to tooling — and tooling can recover a great deal about a particular run, but it cannot promise anything about every program the language admits.

Which is why this project's first phase was a language, and its second was a display-list serializer, before a single hover card existed. The rest of this report is what happened when we took that list literally.

Back to contents


Why a new language, and what existing families make expensive #

The previous section ended with a list of demands on semantics. The natural next question is whether some existing language already meets them. Asked honestly, that is not a question about possibility — with enough machinery, more than one language family can be made to meet most of them — but about cost: what each family makes expensive, and what a learning environment would have to build to pay for it. These are engineering obligations, not proofs of impossibility. The useful way to weigh them is by family rather than by picking on one library.

Imperative languages destroy the record #

The imperative family — C, Java, Python, JavaScript, Processing — has one real advantage for this project: execution is a sequence, so "which line ran, and when" has an obvious answer, and Victor's scrubber fits the model naturally. Everything else works against us.

Assignment erases derivation. After x = x + 1, the previous x is gone. Answering "why is this pixel here?" requires the entire history of the computation, not its current state — which is why provenance in this project had to be reconstructed from a recorded trace rather than read off live values. Time-travel debuggers exist and do recover history, at considerable engineering cost, but they buy replay, not inversion: knowing that x was 55 at step 4,000 does not tell you which source literal to edit so that a shape lands under a pointer.

State is ambient, and set at a distance. Compare the same drawing operation in a conventional API and in Chalk:

// JavaScript / Canvas
ctx.arc(160, 120, 40, 0, Math.PI * 2);
ctx.fillStyle = "#2A76C6";
ctx.fill();
// Which number is the radius? Nothing in the syntax says.
// The color was set on a shared object, two statements away.
// Chalk
circle(center: (160, 120), radius: 40, fill: #2A76C6)
// Every argument is named, and everything that determines
// this circle's appearance is inside this call.

The Canvas context is a mode machine, and so are OpenGL, Logo's turtle, and every global-configuration API. Whenever the determinants of an operation live outside it, "explain this in context" degrades from a lookup into an interprocedural analysis.

The state is a heap, not a list. Closures capture variables, references alias, graphs contain cycles, and handles to sockets and files have no visual form at all. Victor's "see the state" panel presumes the state is enumerable; in this family it is unbounded and, in general, undecidable before the program runs.

Nondeterminism is ambient. Wall-clock time, unseeded randomness, I/O, thread scheduling, and hash iteration order all leak into ordinary programs. Reproducible replay means capturing all of it, and reproducibility is not optional here — a scrubber that shows you a plausible frame 42 rather than the frame 42 is teaching a falsehood.

Dynamism makes call sites undecidable. With eval, reflection, and open dispatch, no static account of "what this call does" exists, so explanation must fall back on runtime observation of one particular run.

Functional languages preserve the record in a shape that resists inversion #

The functional family fixes several of those problems outright, which is why it is the more interesting case. Immutability means derivations are never overwritten. Purity means determinism is close to free. Without aliasing, what a function depends on is what was passed to it.

And yet the fit is poor — not impossible, as the counterexample below shows, but expensive in ways that are structural rather than incidental.

There is often no state to show. Victor's state panel assumes a persistent thing that evolves and can be given a history and a sparkline. In a pure program, there is no such object: what an imperative language calls state is a value threaded through calls and rebuilt at each step. The panel has nothing to display, or everything on the stack, and neither is a panel.

The exception is instructive. Elm, and the architecture pattern it popularized, makes application state a single explicit value updated by a pure function — which restores enumerability, gives replay from a message log, and produced one of the best time-travel debuggers anyone has shipped. That is three of our demands met by a functional language, and it is the strongest existing candidate I know of. What it does not provide out of the box is inversion or in-place vocabulary, for reasons that follow.

Evaluation order is not reading order. In a lazy language, work happens when a result is demanded, so "which line ran when" has no answer a beginner can use; even in strict functional code, evaluation is a tree traversal rather than a march down the page. Victor's scrubber assumes execution has a linear narrative that maps onto source lines. A reduction sequence does not, and scrubbing a graph-rewriting process is a much harder thing to make legible.

Derivations are trees, not affine forms. Immutability preserves the history of a value, which sounds like exactly what a drag needs, but the preserved form is a general expression graph. Running such a graph backwards can be done — but it takes far heavier machinery than solving a sum of scaled literals, and the candidate answers multiply. Our inversion is cheap because a coordinate is a sum of scaled literals: a shape strong enough to run backwards, and simple enough that the learner can follow why the edit came out as it did. Rich derivations are more expressive, and their inverses are harder both to compute and to explain.

Higher-order functions break locality of explanation. map f xs cannot be explained without explaining f, which arrived as a value from elsewhere, possibly as a partial application composed at a third site. The explanation the learner needs is no longer attached to the call in front of them.

The abstraction ladder loses its bottom rung. Victor's pedagogy is concrete first, general afterwards: write the clumsy repeated version, then fold it into a loop. Functional idiom pushes generality from the first line, and the clumsy concrete draft is both discouraged and often awkward to express. The rung the learner is supposed to start on is the one the language would rather they skipped.

The counterexample: Sketch-n-Sketch #

It would be easy to read the last few paragraphs as saying that a functional derivation is something you can display but not solve. Sketch-n-Sketch, from Ravi Chugh's group at the University of Chicago, shows otherwise. Its bidirectional evaluation reconciles edits made to a program's output with edits to the program itself, for a full-featured functional language with closures, lists, and higher-order functions (Mayer, Kunčak & Chugh, OOPSLA 2018). Its output-directed programming work goes further, constructing programs through manipulation of the drawing rather than text edits — including, for the first time in that setting, a recursive function (Hempel, Lubin & Chugh, UIST 2019).

Those results rule out the strong form of the argument this section could be mistaken for: that a functional language cannot support the interaction Glasshouse wants. They also make the costs concrete. The authors are candid that each transformation is hand-coded, that the system grows sluggish on larger examples, and that in a general-purpose language ambiguity is inherent, so the user chooses among candidate repairs. That last point is the conclusion this project reached about the dragged house, arrived at independently — which suggests it belongs to the problem rather than to either system.

What separates the two designs, then, is not possibility but where the trade is struck. Sketch-n-Sketch spends machinery to keep a general-purpose language. Glasshouse spends expressiveness to keep the machinery small and its explanations short. That is a choice made for a learning environment, not a verdict on the alternative.

Two honest near-misses #

Dataflow and spreadsheets. Excel, Observable, and patcher environments like Max do better on several demands than either family above: every cell's value is visible, recalculation is deterministic, and the dependency graph is a genuine provenance record — you can ask a cell what it came from and get a real answer. Victor has written admiringly about spreadsheets, and the admiration is deserved. What they lose is flow and abstraction. Recalculation is a topological sort with no narrative order to scrub through, so "watch it happen step by step" has nothing to show; and turning a working formula into a reusable, parameterized piece is famously the weakest part of the spreadsheet experience — precisely the ladder Victor's fifth demand is about.

The teaching lineage. Logo and Smalltalk were designed for learners and got something important right that this project inherited: liveness, the sense that the system is running and responsive under your hands. But Logo's turtle carries ambient state — heading, position, pen — which is the same mode-at-a-distance problem in miniature, forgivable only because it is small enough to draw on screen. Smalltalk's live image is an unbounded mutable object graph, wonderful for exploration and hostile to any promise of the form "here is all the state." Both optimized for immediacy rather than for explanations a machine can verify.

Why not a subset #

An obvious alternative is a restricted subset of an existing language: teach a small, well-behaved corner of Python or JavaScript and let the environment rely on the restrictions. That is a viable design, but only if the boundary is enforced. Asking learners to stay in the well-behaved corner establishes nothing: the heap, the ambient state, the nondeterministic clock, and the dynamic dispatch all remain in the runtime underneath, and the fence is crossed by the first library call, the first stack trace, the first error message written in the host language's vocabulary rather than the learner's. Enforcing the boundary means deciding which constructs are admitted, which libraries are reachable, which effects are controlled, and how every diagnostic is phrased — and a fence the system enforces at every one of those points is, for practical purposes, a language. Chalk takes that ownership explicitly instead of inheriting it in pieces. That does not make it the only defensible design; it makes plain what the project gets in exchange for maintaining a language of its own.

The synthesis #

Both major families were designed to be written and composed, not observed and inverted. Their objective functions — expressiveness, abstraction power, performance, safety, ecosystem reach — are orthogonal to observability and frequently in tension with it. That is not an error anyone made; it is what those languages are for. Victor's five demands amount to a different objective function, and optimizing a different objective produces a different language.

Optimizing for observability is what produced Chalk's four load-bearing guarantees:

The price is real and worth stating plainly: Chalk has no recursion, no collections, and no functions as values. It is not a general-purpose language, and it cannot become one without either handing back some of the guarantees that make the environment honest or buying much heavier machinery to keep them — the trade Sketch-n-Sketch strikes from the other side. That trade is the subject of the closing sections of this report.

Back to contents

What Chalk is #

Chalk is deliberately small: roughly twenty-five grammar productions. Not because small is elegant, but because every guarantee above is easier to keep in a small house. What follows is the shape of the language; the full grammar, static and dynamic semantics, and standard vocabulary are stated formally in the companion appendix.

paper(color: #FAF9F5)

state y = 40
state speed = 3

to house(at: base, height: h):
  rect(left: base.x, top: base.y - h, width: 30, height: h, fill: #C46A3D)
  triangle(a: (base.x, base.y - h), b: ..., c: ..., fill: #8A3324)
end

repeat 5 as i:
  house(at: (20 + i * 55, 220), height: 40 + random(from: 0, to: 30))

every frame:
  y = y + speed
  circle(center: (160, y), radius: 20, fill: #2A76C6)

Beyond the four guarantees, the surface worth noticing:

That last decision is the most consequential in the project.

Back to contents


Design principles #

The display list is the truth. We rejected pixel comparison early; anti-aliasing and platform rendering make it unusable as ground truth. Instead, two runs produce "the same picture" if and only if their serialized display lists are byte-identical. This became the project's universal oracle: determinism is a byte comparison, regression is a byte comparison, and — the payoff we did not fully anticipate — the correctness of the most ambitious features became byte comparisons too.

One evaluator, two modes. The interpreter has exactly one execution path; instrumentation is a switch, not a second implementation. If the fast run and the traced run could ever disagree, every visualization built on the trace would be a confident lie. A standing test forces them to agree byte for byte.

One source of truth per kind of thing. All vocabulary lives in one registry, and a test refuses to let a builtin exist without a plain-language card. All generated code comes from one synthesizer. All screen-reader speech comes from one template set. Even parsing follows the pattern: Chalk is parsed twice — once by the semantic authority, once by an error-tolerant grammar that drives highlighting — with a drift test forcing agreement.

Honesty over cleverness. When the edit-matcher cannot tell whether two pieces of code are the same, it resets identity rather than guessing. When provenance meets two variables multiplied together, it declares the value opaque rather than pretending. When a drag has two solutions, the environment never averages them into one plausible-looking lie.

Trace once, decode forever. The engine records everything on the first pass: every expression's value, every state write, every drawn shape, each tagged with the source that caused it. Entire later phases then needed no new machinery — the state panel, the typed timeline, and the provenance chains behind direct manipulation are pure functions decoding data that was already there. We budgeted performance headroom for provenance recording and never spent it.

Back to contents


Architecture #

Glasshouse architecture: three concentric rings — the dependency-free engine at the center with the display list as canonical output, a pure observation and transformation layer around it, and the editor and studio as the only browser-facing ring; the trace flows outward, edits flow inward, and an oracle loop verifies both.

The engine is dependency-free: lexer, hand-written parser, tree-walking interpreter, trace store, display list. It knows nothing about browsers.

The pure layer turns traces into things worth seeing — scrubber positions, timeline rows, sparklines, provenance chains, drag candidates, the abstraction gestures. These compute what to show and never touch a pixel, which is why they could be tested with exact numerical assertions.

The editor and studio is the only ring that knows the DOM. A test greps the source to keep browser-coupled libraries from leaking inward.

Two structural details earned their keep. Identity has two layers: the engine numbers syntax nodes per parse, while the editor maintains stable identities across keystrokes via a tree-matching algorithm — and the engine never sees those stable ids, keeping its world hermetic. And because layering was enforced from the first commit, the final phase split the engine into a publishable library by writing a one-line exports map. The boundary did not need to be created, only named.

Back to contents


The hard parts #

Identity across edits. Every keystroke produces a fresh parse with fresh nodes, while the timeline and scrubber attach to code. Which new node is the old node? The failure mode is insidious: a matcher clever enough to be usually right will occasionally hand your circle's identity to an unrelated rectangle, and the timeline will lie with confidence. The solution was a two-pass matcher — structural fingerprints anchor confident matches, positional recovery handles renames — governed by the honesty rule: when in doubt, reset. Live use later exposed exactly the predicted flaw, and the fix was to make the matcher less clever.

Running the math backwards. Direct manipulation is inversion: a pixel moved, so which literal changes? Provenance chains express each coordinate as an affine combination of source literals, reconstructed after the fact from the existing trace. The genuinely difficult part was invisible in the demo — values crossing function-call boundaries. A house's rectangle is drawn inside a definition using a parameter, while the interesting arithmetic lives at the call site. Chains must chase through the recorded call stack from parameter back to argument, or the feature dies at the first abstraction a learner creates.

Ambiguity as an interface problem. 20 + i * 55 gives a dragged house two honest inversions. We pinned a policy — solve per candidate, rank by gentlest edit, apply one live, cycle with Tab, never average — and backed it with an oracle that runs on every candidate: apply the edit, re-execute, verify the shape lands under the pointer. That oracle earned its keep dramatically. An early rotated-shape drag solved each axis independently, passed its unit tests, and put a spinning blade forty-four pixels from the finger. The oracle failed loudly and forced the correct joint solve of both axes as a small system of equations.

That guarantee is worth stating exactly, because it is narrower than it sounds. The oracle checks one thing: in the execution being examined, the dragged attribute lands at the target. It does not establish that the edit is the one the learner meant — showing both candidates is how that question is put to the learner — nor that the rest of the program's behavior is unchanged, nor that the edit is right for frames, inputs, or seeds it was never run against. Respacing the row to move the third house moves the other four as well; the oracle passes, correctly, because moving them is what that edit means. A promise whose edges are visible is one a learner, or a reviewer, can actually rely on.

Preserving the picture through abstraction. The five abstraction gestures are program transformations under one law: output identical. But the oracle itself needed refining. Full serialization records which source node drew each shape, so any gesture that inserts a line necessarily shifts those tags, making byte-equality unsatisfiable by even a perfect transform. The law split into two co-required clauses: a visual serialization (geometry and color) must be byte-identical, and untouched code must keep its stable identities. We then added a test proving the relaxed oracle still catches a one-unit coordinate change and a reordered draw. When you loosen a correctness check, you owe a proof that it still checks.

The law has edges too. "The same picture" here means the same serialized drawing — the ordered operations with their geometry and color — not the same pixels. Two programs that draw a square as one rectangle and as two abutting ones look identical and still fail the comparison, and that strictness is deliberate. The law also holds only for the executions actually checked — setup plus a pinned run of frames at a fixed seed — not for every input the program might ever receive. That is a strong guarantee about a precisely stated thing, which is the only kind worth offering.

A process failure worth recording. Midway through, a scaffold silently overwrote a file containing more than a hundred lines of shipped, working code — behind a verification step that counted plan comments rather than actual code, so the damage looked like a harmless net-positive diff. It was caught, audited to a single file, restored exactly, and converted into a permanent rule: scaffolds add new files only; changes to existing files arrive as explicit instructions, never as overwrites.

Back to contents


What we would tell someone building one of these #

Back to contents


Where this sits relative to the vision #

Victor's essay is a research agenda wearing the clothes of a critique. Glasshouse is one traversal of that agenda, in one domain, at small scale. It is worth being precise about which parts are done, which are partial, and which we did not attempt — because the gaps are more interesting than the checkmarks.

Demand Status What is missing
Read the vocabulary Substantially met Cards explain the language, not the domain being modeled
Follow the flow Met within a frame and across frames Deep call trees are unvisualized; the iteration grid caps at 64; Chalk's control flow is too simple to stress the ideas
See the state Met for scalars, points, colors No collections — so no list, tree, or graph visualization, which is where the hard problems live
Create by reacting Met Values arriving through state are not draggable; products of two variables are opaque
Create by abstracting Met for the five gestures No recursion, no collections to abstract over, and no abstraction over behavior

Five gaps stand between this and the essay's full ambition.

The domain is one domain. Everything here draws pictures. Victor's examples were graphical too, but his claim is general, and the specific thing that made this project tractable does not obviously generalize: our universal oracle exists because output is a picture, and a picture has a canonical serialization. What is the display list of a sorting algorithm, a parser, a bank ledger? Until there is an answer, the verification strategy that underwrote every hard feature here is confined to graphics. Finding canonical-output definitions for non-graphical domains is, in my view, the first hard research question — not a port but a genuine open problem.

Time is only half tamed. You can scrub time; you cannot abstract over it. Provenance understands arithmetic within a frame and treats values carried across frames as opaque, so you can grab a bouncing ball's shape but not its motion. The difficulty is structural rather than incidental: a state variable at frame 200 is the composition of 200 applications of an update rule, so inverting a drag through it means solving for an initial condition or a rate against a recurrence, where the clean affine story collapses. "Make this bounce" as a gesture is the most interesting unbuilt feature in the system.

Abstraction stops where dynamism starts. There are no collections, no recursion, no functions as values. Each would buy real expressive power and cost a guarantee, which means the design question is not "which features next" but how much dynamism can a learnable language absorb before observability degrades — an unanswered question in language design. There is a promising pattern already in the codebase: provenance marks values it cannot explain as opaque rather than guessing, and the environment tells the learner so. Generalizing that into graded dynamism with honest degradation — richer constructs whose loss of explainability is visible and localized rather than global — looks more tractable than either banning or admitting dynamism wholesale.

We measured use, not learning. This is the gap that matters most and the one easiest to gloss over. Nine human gates measured whether people could accomplish things inside Glasshouse. Victor's claim, and ours by inheritance, is about learning to program — which implies transfer, and which is the essay's own standard: something must end up in the learner's head that survives beyond the environment that put it there. Nobody in this project has evidence that a Chalk learner subsequently reads Python better, and the study that would settle it is straightforward to describe and expensive to run: teach one cohort with Chalk and one with a conventional language and standard tools, then test both in a third environment neither has seen. That study has not been run for any learnable-programming system, which is much of why the field remains contested rather than settled.

Programming as a literate activity. The essay puts weight on reading and remixing other people's programs — comprehension as a first-class activity, not just authoring. Glasshouse ships five examples and eight lessons; there is no corpus, no sharing, no remix path. The gestures are actually good raw material here, since "open this up" is a comprehension tool disguised as an edit, but the social and library layers are simply absent.

There is also an uncomfortable question we should state rather than dodge. The essay is partly a warning against toy environments that teach themselves instead of teaching thinking. Chalk is a small purpose-built language with twenty-five productions and no escape hatch, which makes it fair to ask whether we built what the essay asked for or an unusually well-instrumented instance of what it warned about. Our defense is that the guarantees would have cost far more to obtain any other way, and the guarantees are what make the explanations honest. But that is a defense, not a proof, and only a transfer study decides it.

Finally, scale is untested in the way that matters. Performance was verified against a 500-line synthetic program and traces of roughly 50,000 steps, and it is comfortable there. But a timeline of 10,000 statements is not a timeline, and an iteration grid capped at 64 is a confession. Whether these representations survive contact with programs of real size — or whether learnable programming is inherently a small-program technique, valuable for teaching and irrelevant for production — is unresolved here and largely unresolved in the literature.

Is a better language the key? #

The intuitive next move is to grow Chalk — add recursion, collections, functions as values — and assume the vision follows. Sorting the gaps by what kind of problem each actually is suggests otherwise.

A transfer study is pure empirics and needs no language work at all. A corpus with a remix path is social and infrastructural. Representations that survive scale are an interface problem: a timeline of ten thousand rows fails for reasons that have nothing to do with semantics. Oracle generalization is conceptual — it asks what "the output" means for a program that does not draw. Cross-frame provenance touches state semantics but is mostly an algorithms problem, since the difficulty is inverting a recurrence rather than expressing one. Only collections sit squarely in language design, and even there the harder half is visual: nobody knows what a learnable view of a changing tree looks like, and that question outlives whatever syntax introduces one.

There is also a trap in "improve the language" as a strategy, and it is this report's own thesis pointed forward. Every capability here was purchased with a restriction. Adding recursion, collections, or higher-order functions does not improve the system monotonically — each one either spends a guarantee or demands heavier machinery to keep it, and either way the explanations get harder to give. That is why the productive framing is graded dynamism with honest degradation, described above: grow expressive power only where the resulting loss of explainability can be made visible and local, the way provenance already marks what it cannot explain as opaque rather than guessing. Language work is necessary eventually. It is not the binding constraint now.

Should this build on Elm? #

Since Elm was named the closest existing fit, the obvious question is whether to merge rather than continue separately. I think not, for the reason the subset argument gives: a cut-down Elm is only as trustworthy as the fence around it. Building that fence — which expressions to admit, which to invert and how, what to do about positional application whose arguments carry no names to explain — is most of the work of a new language, done without the freedom to choose the rules, and it gives up the ecosystem that made Elm attractive in the first place. Going the other way — importing an architecture of messages and update functions into Chalk — adds machinery to solve a problem Chalk avoided outright by banning nondeterminism, since replay from a seed needs no message log.

What is worth taking from Elm is narrower and more useful than a merger. Its output is a tree structure rather than a stream of effects, which is an existence proof that "the result of a program is a comparable value" holds for a class of programs wider than drawings — precisely the question the oracle-generalization gap poses. Its treatment of immutable collections is also the right reference if Chalk ever grows them.

The more promising relationship is layered rather than merged: a small language for learning, and a designed path to a conventional one for graduating. That directly attacks the transfer gap instead of routing around it, and a pure, deterministic, picture-producing target language is an unusually good second rung.

That is the landscape. The final question is what to actually do, and in what order.

Back to contents


What comes next #

The gaps sort into a program. This section makes each step concrete enough to argue with.

The experiment that should come first #

Before more engineering, the field needs the study that no learnable-programming system has run: two cohorts matched on prior exposure, one taught with Glasshouse's curriculum, one taught the same concepts — values, loops, procedures, decomposition — in a conventional beginner stack with matched time on task. Then both are assessed in a third environment neither has seen: read a short program and predict its output, locate a planted fault, make a specified modification. Measures pre-registered, so the result counts whichever way it falls.

Two refinements make the study worth more than a verdict. First, an ablation arm — Glasshouse with hover, provenance, and the scrubber disabled — separates "liveness helps" from "explanations help," which are different claims with different design consequences. Second, in-environment measures (error rates, time to unaided completion) alongside transfer measures, so that if transfer fails, the data says whether the environment taught the wrong thing or taught the right thing non-transferably.

This comes first because it is inexpensive relative to everything below it and it is the one result capable of invalidating the approach. Better to learn that before the next two years of building, not after.

Making behavior graspable #

Cross-frame provenance is the most self-contained hard problem, and it has a tractable core. A state variable at frame n is the n-fold composition of its update rule — but when the update stays affine in state and literals, the composition has a closed form. y = y + speed gives yₙ = y₀ + n·speed, which is affine in exactly two source literals. A drag on the ball mid-flight can then offer the same two-honest-answers pattern this report opened with, lifted into time: change where it starts, or change how fast it moves.

Conditionals break the closed form — a bounce flips the sign — but they break it piecewise: segment the timeline at branch flips, solve within the current segment, and present candidates scoped honestly ("since the last bounce"). Updates that are not affine degrade to opaque, marked as such, exactly as within-frame provenance already does. The gesture target is dragging a trajectory rather than a position; the harder inverse problem — synthesizing a new update rule from a demonstrated path, a true "make this bounce" — should wait until editing existing motion works, because it is synthesis rather than inversion and needs a different kind of machinery.

A second domain #

The oracle strategy must be tested outside graphics, and the right first candidate is data transformation: programs that filter, sort, and reshape tables. The canonical output is the final value plus an operation list — each produced row tagged with the input rows that fed it — which is a display list whose operations happen to be data operations rather than drawing ones. Databases have long had a name for the provenance half of this (lineage); what they have never had is the interaction layer: scrub a sort and watch rows move, grab an output cell and be shown its inputs, fold three repeated transformations into one.

If the method transfers there, the central claim stops being about pictures. If it does not — if "the output" of a data program resists canonical definition in some way drawings do not — that failure would be as informative as success, because it would locate the boundary of the whole approach.

Growing the language without lying #

Collections and richer expressions come after the above, and they should arrive under an explicit mechanism rather than one feature at a time: every admitted construct declares its explainability class, and the environment renders the loss visibly — a marked region meaning values inside here can be shown but not solved. That generalizes the pattern already in the codebase, where provenance marks what it cannot explain as opaque instead of guessing.

Concretely: fixed-size lists of points first, because they are drawable and their diffs fit the existing frame-to-frame diff machinery; growable lists next, displayed as strips with per-frame change marks; higher-order functions deferred indefinitely, because they detach explanations from call sites and nothing in the current design pays that cost back. Each admission is a trade, and the trade should be legible to the learner, not only to the language designer.

Reading as a first-class activity #

The essay treats comprehension of other people's programs as half of programming; Glasshouse currently supports authoring only. The missing layer is mundane and important: any program shareable as a link carrying source and seed, so that what you open replays exactly what the author saw; a public corpus grown from lesson solutions and remixes; and the existing gestures repurposed for reading — "open this up" is already a comprehension tool disguised as an edit, and a curriculum of reading exercises (predict, then scrub to check) would exercise the same machinery in the opposite direction.

What has changed since the essay #

The essay predates modern language models, and it is worth stating plainly what they do and do not change for this program.

They lower the cost of the content layers. The vocabulary cards, the eight lessons, and the announcement templates were hand-written under test constraints — jargon-banned, coverage-checked, length-limited. A model can draft all of that, and the one-source-of-truth architecture is precisely what makes model-drafted content safe to admit: the acceptance tests already exist, and content either passes them or does not.

They also open an interaction the essay could not have proposed: a learner asking, in words, "why is this circle here?" and receiving an answer. But the grounding is the entire question. An unconstrained model explains plausibly; a model constrained to cite the recorded trace and the provenance chain explains truthfully — and any edit it proposes can be run through the same oracle that vets a drag before the edit is offered. The guarantees-first architecture is not made obsolete by AI assistance. It is what makes AI assistance checkable, which may turn out to be its most durable contribution.

The lever: auditing the roadmap against Mindstorms #

Because the essay rests on Papert, the roadmap should answer to Mindstorms directly, not only to the essay built on it. Read against the shipped system, the book yields three confirmations, one reframing, and two gaps — and the gaps reorder nothing above, but they deepen the reasons for steps already listed and change how two of them should be designed.

The confirmations. Papert's epistemology of the bug — the question to ask about a program is “not whether it is right or wrong… but if it is fixable” — is the stance Glasshouse mechanizes: a bug here is a visible object with a location, a history, and a chain of causes, and the diagnostics speak one fixable problem at a time in the learner's own words. His metaphor for definition — teaching the computer a new word — is Chalk's to construct and, literally, the “teach this” gesture. And his Mathland — learning mathematics the way one learns French in France, by living where the language is spoken — is the frame in which an environment matters at all: Glasshouse is a small country whose language is deterministic and whose customs are visible.

The reframing. This report defended Chalk's restrictions as the price of guarantees — an engineering argument. Papert supplies an independent, pedagogical defense: his microworlds are incubators for knowledge, deliberately simplified worlds that give a learner something to grab hold of, so that a powerful idea contradicted by everyday intuition can be encountered, manipulated, and owned. Under that reading, a restricted language is not the regrettable cost of verification; restriction is what an incubator is. Two defenses of the same design converging from unrelated premises — one from proof obligations, one from learning theory — is the strongest evidence yet that the stance is right. It also sharpens the second-domain step: a data microworld should be designed around one powerful idea it incubates, with the question asked explicitly of every future world — which idea does this place teach: decomposition, feedback, fixability, look for something like it that you already understand?

The first gap: syntonicity. Papert's explanation of why Turtle geometry is learnable is not visibility but syntonicity — the Turtle circle is body syntonic, firmly related to a child's knowledge of her own body (“play Turtle”: walk the circle yourself, notice you step a little and turn a little, then write exactly that), and ego syntonic, coherent with her intentions and pride. Chalk has no body. Its coordinates are absolute and Cartesian; there is no entity to identify with; one cannot play Chalk. The original design carried sprites and deferred them for the sake of the guarantees. Papert's argument is that this is not an optional flavor but the lever itself — the bridge from body knowledge to formal knowledge is where the learning happens. The directive for the future system is therefore an embodied object whose motion is describable in one's own body's terms, with the guarantees extended to it rather than traded away. And there is a precise technical identity hiding in that sentence: a turtle is a state-accumulating recurrence — its position at frame n is the fold of its steps — which is exactly the cross-frame provenance problem already on the roadmap. Body syntonicity and cross-frame provenance are the same mountain approached from two sides, the learner's and the machine's; climbing it buys both.

The second gap: the samba school. The shipped curriculum is validated but instructionist: numbered steps, find this and change it. Papert's image of a learning society is the samba school — purposeful, socially cohesive, novices and experts learning in one place, teaching deliberate but never syllabus-driven, the activity real rather than rehearsed for. That upgrades the roadmap's corpus-and-remix item from infrastructure to theory: a shared, purpose-laden place to work among people better than you is not a distribution feature, it is where Papert locates the learning. The directive: future lessons become project seeds inside microworlds — a purpose handed to the learner, not a script — with the mechanical validation retained underneath; and sharing is designed as a place with ongoing life, not a gallery of finished artifacts.

The warning that cuts both ways. Papert's QWERTY phenomenon — a technology fossilizing for historical reasons and then acquiring mythical justifications — both vindicates and threatens this project. It vindicates the refusal to teach in an incumbent language merely because it is incumbent; teaching programming in whatever language industry happens to use is his named example of computational QWERTY. And it warns Chalk about itself: once curricula, tooling, and habits invest in v1.1, its restrictions could fossilize with the same irrationality they were designed to avoid. Graded dynamism with honest degradation, described above, is the anti-QWERTY mechanism — the language is permitted to change, but only with the cost of each change made visible, never justified by myth.

What runs the other way. Papert's gears, and later his computer, are transitional objects: material things that mediate the passage from concrete, bodily knowledge to formal knowledge. Glasshouse's one addition to that account is that the transition itself can be made observable — a dragged pixel connected, by an inspectable chain the learner can read, to the arithmetic that produced it. If Mindstorms supplies the levers, a guarantees-first system supplies something Papert did not have: a way to render the lever's action visible while it works.

The order, and why #

Transfer study first, because it is cheap and falsifying. Cross-frame provenance second, because it completes the manipulation story inside the domain that already exists, its hard core is now specified, and the syntonic object the previous section calls for is the same recurrence problem seen from the learner's side. The second domain third, because it decides whether anything here generalizes. Language growth fourth, because only after the second domain does anyone know what the guarantees being spent are worth. Corpus and scale representations last — they are design problems that cannot be specified until the earlier answers are in.

That is a research program, not a backlog, and no single project completes it.

Back to contents


What the first step demonstrated #

The essay closes with a warning that is easy to skim: the features it demonstrates are not the point — the principles are. Read that way, the vision is not a wishlist of scrubbers and hover cards. It is a standard for what a programming system owes the person learning inside it: language and environment designed together, every part answerable to whether it helps the learner see, and to whether something true ends up in her head. That is the vision this project adopted as its requirements document, and the standard by which its one step should be read.

Against that standard, this step did a specific job — the one that sits between the essay and the classroom. It was never an attempt to prove the vision; no single system could, and the essay itself argued the demonstrations better than anyone since. It was the promotion of the vision from illustration to mechanism: each demand taken as an engineering requirement, met with a guarantee, and verified. What the essay showed as interface, this project implemented as machinery and then checked. Three panes never disagree about a value, because there is one formatter. A dragged shape lands under the finger, because an oracle re-runs the program before offering the edit. An abstraction cannot change the picture, because the picture is a byte string that must not change. Nothing in the mockups was left as hope.

Doing that job made the vision's most contested structural claim concrete — that an environment's promises are only as strong as the semantics beneath them — and priced it: every capability was purchased with a named restriction, and the price list is now written down. It also put the system in front of people who had never programmed, and they used it: a narrow result, but pointing exactly where the vision points. What remains — behavior you can grab, domains beyond drawing, a language that grows without lying, and the transfer evidence the essay's own closing question demands — is laid out in the section above, not as a change of direction but as the same vision, carried further by the same method: take a demand, make it a guarantee, verify it, and only then move on.

The essay asked for a programming system in which seeing is the way of knowing. This step establishes that such a system can be built, that its promises can be kept mechanically rather than editorially, and that a person can reshape a program by pointing at it. The steps that realize the rest of the vision are known, ordered, and waiting.

Back to contents


Glasshouse, the environment described here, and Chalk, the language it runs, are open source — a dependency-free engine, a browser studio, and an eight-lesson curriculum — built in six gated phases with 62 recorded design decisions, and every human gate run with real learners. Part of Grasphouse.

Appendix A: The Chalk Language — formal syntax, semantics, and the standard vocabulary
Appendix B: The Eight-Lesson Curriculum — every lesson with a live demo to play with