# The calculus of interfaces: compiling a layout into one formula, and what survives

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/calculus-of-interfaces
> date: 2026-10-31
> tags: react, layout, compilers, differentiable-programming, handwired

*UI = f(props)* is the oldest line in the React docs. On October 27 Shu Ding posted that the f in it is JavaScript, not math, and that he had [compiled a dashboard into a real math function](https://x.com/shuding/status/2115082810738479363) to see what that allows. The page is [calculus-of-interfaces.vercel.app](http://calculus-of-interfaces.vercel.app): a projects page whose layout is one function from twelve numbers (viewport width, gap, font scale, how many projects) to the position and size of every card.

I wanted to know what "compiled" and "real math" mean in that sentence, so I did three things. I read the page's source and its inspiration, [chenglou/handwired](https://github.com/chenglou/handwired). I tried to run the page's code and was refused. And I wrote my own small compiler from a restricted component to a formula, checked it against ordinary code, and measured how the formula grows. Every claim below is labelled **measured** (I ran it), **reported** (a source says it), or **reasoned** (I inferred it). Code and numbers labelled *ours* are mine, not Shu's or Chenglou's.

<RepoCard repo="chenglou/handwired" />

## What was built

**Reported.** The post is a 15-second video and the link. I found no repository linked from the post or the page. The page's own text says the layout is "written once as a straight list of operations with no branches or loops, following the approach of chenglou/handwired", that every operation is "a weighted sum, a product, a ReLU, or a step function", and that the same list can be evaluated four ways: exactly, with every step replaced by a sigmoid, with forward-mode differentiation, and with interval arithmetic. Its "Limits" list is the most useful paragraph on it:

- "The page is written by hand in this form. Nothing converts existing React or HTML into it yet."
- Messages "are set in a monospace font and wrapped by character count, so line counts are computed rather than measured from rendered text."
- "Proofs hold for exact arithmetic. They do not account for floating point rounding."
- Projects, device shares and font scale shares are sample data.

So the word *compiled* is generous. Nothing takes `<ProjectsPage {...props} />` and produces the function. A person wrote a builder program that appends nodes to a list, in the same shape the component would have had. The author says this himself. What the demo shows is what you can do once a layout is in that form, not that React can be put into it.

## Where the idea comes from

**Reported.** Handwired's README opens: "Turn your regular JavaScript code into a neural net; no training, exact, yet also fully differentiable." Its example is the one the whole approach rests on:

```ts
const above4 = Math.max(0, a - 4) - Math.max(0, a - 5)   // 1 if a > 4, else 0, for an integer a
const result = above4 * 10 + (1 - above4) * 12            // both branches compute, above4 picks one
```

Both branches always run; a number between 0 and 1 picks which one counts. Chenglou's runtime has four kinds of operation on 32-bit floats: a weighted sum (`affine`), a product with a ReLU gate (`reglu`), a score rounded to 0 or 1 (`boolean`) and a table read (`indexRead`). The docs are blunt about the same limit Shu's page repeats: "No tool converts existing JavaScript source into a program." You write builder code, ordinary JavaScript that runs once and appends operations to a `Circuit`.

What Handwired adds is the engineering that makes the construction exact in FP32. Its `expressions.md` has a table of "familiar expression, circuit version, operations and layers, exact when": `a >= b` on a declared grid is 2 operations in 2 layers; the same on an undeclared range within 0..16,384 is 15 operations in 14 layers (a "sign cascade"); a column count among 7 is 13 operations in 3 layers. Its demo, HandwiredOS, is a small desktop with three windows run by one program, which the repo's execution-model doc puts at 5,028 built operations, 4,646 of them live (reported; I did not rebuild it). Its tests run programs "including seeded random ones, through a plain reference evaluator and the compiled one, and compare the results". That is the same check I make below.

## What is inside Shu's bundle

**Read, not run.** The page ships one 452 KB script, unminified enough to follow. The intermediate representation is a class with a node list and six node kinds: `input`, `const`, `affine` (a list of `[node, coefficient]` pairs plus a bias), `mul`, `relu`, and `step`. The builder methods are the ones the page's prose lists: `max(a,b) = a + relu(b - a)`, `select(c,a,b) = b + c*(a - b)`, `not(c) = 1 - c`. A `step` carries a `strict` flag (greater than versus greater or equal), a `scale` (the sigmoid width used when softened) and a `hard` flag that opts a node out of softening.

Four evaluators walk that list. The exact one is a single loop with a `switch`. The soft one swaps `step` for a logistic. The differentiating one carries a derivative per requested input alongside each value; a `relu` passes it through when its input is positive and a hard step contributes zero. The interval one propagates `[lo, hi]` and, for a `mul`, takes the min and max of the four products. A fifth function walks the list backwards to collect the `step` and `relu` nodes whose input interval straddles zero. Those are the places where a proof must split the range.

The layout builder encodes a card grid with the constants visible in the bundle: 12 cards per page, at most 8 columns, at most 3 text lines, a 13-pixel monospace font at 0.6 em per character. The column count is a sum of steps, exactly as the page's formula says:

$$\text{cols} = 1 + \sum_{k=2}^{8} \mathbf{1}\big[\,\text{inner} + \text{gap} \ge k\,(\text{minCard} + \text{gap})\,\big]$$

The builder then lays out the grid once for every possible column count, and blends the eight results with the one-hot of the column count (`step(k) - step(k+1)`). Each card's line count is a sum of steps against widths precomputed from the sample commit message by a word-wrap function, and each row's height is a chain of `max`. That is the whole trick, and it is why the program is long: **reasoned**, the cost is the number of cards times the number of column counts, because a program with no branches has to compute every branch.

**Could not run.** I extracted the IR class, the evaluators and the builder into a module to call the builder on the default props and count its nodes. The sandbox's permission layer refused to execute it. I did not work around that, so I have no node count for Shu's dashboard, no output to compare, and nothing measured on his code. I also did not run Handwired's tests. Everything above about both repositories is from reading.

## My version: a compiler small enough to check

*Ours.* The component is a grid of text cards, a pure function of six props: viewport `W`, container max width `M`, padding `p`, gap `g`, minimum card width `m`, and how many cards are shown. Each card holds a commit-message-like string. Layout rules are the ones above: the container is `min(W, M) - 2p` wide; the column count is the largest `k` for which `k` cards of width `m` and gaps fit; card width is the remainder split evenly; a card's text wraps by character count at 8 pixels a character, up to 4 lines of 18 pixels over a 40-pixel base; each row is as tall as its tallest card; cards stack from a 96-pixel header; the page height `H` is the last row's bottom plus padding.

I wrote it twice. The **reference** is ordinary JavaScript with real `if`, `for`, `Math.max`, `Math.ceil` and a greedy word-wrap. The **compiler** runs the same rules through a tracing builder that hash-conses nodes into the same six kinds as Shu's IR (affine, mul, relu, step, plus inputs). Every `if` becomes a step, every `max` becomes `a + relu(b - a)`, every loop is unrolled, and the unknown column count is handled the way both Shu and Chenglou handle it: compute the layout for each `k` and blend.

Two details are what make equality exact rather than approximate. Wrapping depends on `floor(cardW / 8)`, which is a division. I avoid it: the threshold test is on `k * cardW`, kept as the un-divided affine `inner - (k-1)g`, against an integer `capacity * 8 * k` precomputed from the text. And the thresholds themselves are found by running the real word-wrap on each string for each line budget, which is how the compiled program inherits the behaviour of an algorithm it cannot contain.

The smallest case that still has a branch is one card with at most two columns. The compiler's output for the page height, as straight-line code (variables are node ids, `1[x >= 0]` is a step):

```text
v6  = W - M                       v7  = max(0, v6)        v8  = W - v7        # min(W, M)
v9  = -2*p + v8                   v10 = -g - 2*m + v9     v11 = 1[ v10 >= 0 ] # fits two columns?
v12 = 1 - v11                     v13 = total - 1         v14 = 1[ v13 >= 0 ] # is the card shown?
v15 = v9 - 144   v16 = 1[ v15 >= 0 ]    # one column: fits in 1 line (18 chars x 8)
v17 = v9 - 80    v18 = 1[ v17 >= 0 ]    #                  2 lines (10 chars x 8)
v19 = v9 - 56    v20 = 1[ v19 >= 0 ]    #                  3 lines (7 chars x 8)
v21 = 4 - v16 - v18 - v20         v22 = 18*v21 + 40       v23 = v14 * v22     # card height, one column
...  the same for two columns (v33..v43)                                       # 39 equations in all
H   = v12 * v32 + v11 * v45
```

With `W=1280, M=1248, p=24, g=24, m=300` and the string "fix the retry loop", the compiled program and the reference both return 202. That is one data point; the check that matters is the next one.

**Measured.** I compiled the grid for 4, 8 and 12 cards (at most 6 columns) and compared it with the reference on 20,000 random integer props each (`W` 320 to 1920, `M` 480 to 1920, `p` 0 to 64, `g` 0 to 48, `m` 160 to 480, cards shown 1 to N), checking the column count, page height, and the `x`, `y`, width and height of every card. Result: **0 mismatches in 60,000 evaluations**, in Node 22.22.0.

How much does that prove? Less than it sounds. I mutated one line-count threshold by a single pixel in the 8-card program and re-ran 20,000 props: the check caught it **12 times**. A threshold that moves by one pixel only matters for the props that land on the boundary. So the zero is real, and it is also a test that would let a one-pixel error through unless the sample were ten or a hundred times larger. Handwired's approach, an exact check on declared integer grids plus seeded random programs, exists because of exactly this.

Try it. The widget below does not run a layout engine: it evaluates the baked 401-node program for six cards, node by node, and shows the hand-written layout's numbers next to it. Drag the width and watch the column count step. Then raise the softness slider: it is the same program with every step read as a sigmoid, which is the second of Shu's four evaluations.

<GridSolver />

## How big does the formula get

**Measured.** The same compiler, the same strings, growing the component. Each program has one output per card for `x`, `y`, width and height, plus `H`, the column count and the card width.

| cards (up to 6 columns) | nodes in the whole program | nodes `H` reaches | `H` written with nothing shared |
|---:|---:|---:|---:|
| 1 | 144 | 105 | 474 |
| 12 | 1,042 | 770 | 13,056 |
| 32 | 2,272 | 1,620 | 34,302 |
| 64 | 3,902 | 2,650 | 69,261 |

Growth is close to linear in the number of cards, with a slope near 51 nodes per card between 32 and 64 cards, and linear in the number of column counts too: at 12 cards, 226 nodes for one column count, 404 for two, 1,042 for six, 1,370 for eight. The unshared form is the one to look at if you picture "a formula" as a single expression. At 64 cards it is 26 times the size of the version that reuses sub-expressions. Real math, in the sense of one closed-form line you could read, would be unreadable; the usable object is the straight-line program, and "formula" really means that.

<SizeScaling />

Two things are reasoned from this. The cost of removing branches is paid once per possible value of every data-dependent decision: a layout that also chose between 3 card styles would multiply the program by about 3. And the work is not free at run time. I timed 12 cards over 2,000 random props, 10 passes: the 1,042-node program takes about 50 microseconds an evaluation in Node (allocation of its value array included), the hand-written layout about 3. That is 16 times slower for the same answer, on one machine, with no attempt to optimise either. The payoff has to come from what the form allows, not from speed.

## What the form buys

### Gradients, with an asterisk

**Measured.** I differentiated the compiled `H` with respect to the gap using forward mode, treating every step as slope zero, and compared with a central finite difference of the *reference* (step 0.001) on 5,000 random props with a fractional gap. At 3 of them the finite difference straddled a jump larger than 20 pixels and I discarded them. Of the other 4,997, **4,994 agreed to 1e-6**. The 3 that did not were off by up to 9,000, which is an 18-pixel jump (one text line) inside a 0.002-pixel window, divided by 0.002. The derivative the program reports is correct wherever the function is differentiable. At a step it is wrong, in the sense that it says nothing.

That is the limit of "differentiable" for a layout. A hard layout is piecewise linear with jumps, so its gradient is a slope that is zero or constant almost everywhere and misses every jump. On my twelve-card example, `H` rises by exactly 3 pixels per pixel of gap (3 gaps between 4 rows) until a column count changes. To get a gradient that sees the jumps you soften the steps. I did, on the same props, with the sigmoid width `tau` set to 0.5, 2, 8 and 30 pixels:

| `tau` | `H` | dH/dgap |
|---:|---:|---:|
| exact | 502 | 3 |
| 0.5 | 500.3 | 2.98 |
| 2 | 468.2 | 2.84 |
| 8 | 386.6 | 2.20 |
| 30 | 337.1 | 2.16 |

At `tau` 8 the surrogate's page is 115 pixels shorter than the real one. It is a different function with a useful slope, not an approximation to be trusted for a number. The good use is the one Shu's tool suggests, tune with the soft one and read the answer off the hard one.

### Bounds, and why mine are loose

**Measured.** With the interval evaluation I asked for the range of `H` over every viewport from 320 to 1920 pixels, other props at the defaults, twelve cards, up to six columns. The true range, from evaluating the reference at all 1,601 widths, is 502 to 1,302 pixels. The proved range is **minus 3,370 to 6,064**. Sound, and useless. Cutting the width range into 8 pieces and taking the union gives 0 to 1,932; 256 pieces give 0 to 1,806. After that it does not move.

<IntervalBand />

The floor is explained by the blend. In a piece that straddles a column-count change, the one-hots are each in [0, 1] and each per-column layout is a large positive number, so their sum is bounded by [0, max] however narrow the piece. H changes value 16 times along the 1,601 widths, so some piece always straddles one, and subdividing the input never removes the slack. The standard fix is to split on the straddling steps rather than on the input, so that each branch has its steps decided. That is what the backward walk in Shu's bundle collects, and I did not implement it. **Reasoned:** the page's claim that you can prove a rule for every screen width is plausible for a branch-and-bound that splits on those nodes. My plain version shows why the naive version does not.

Equally important, the bound is over *exact arithmetic*. The page says so. Handwired spends most of its docs on the converse: how to make comparisons exact in FP32 at all.

## What it cannot do

**Arbitrary JavaScript.** Loops must have a bounded trip count, and every possible count is unrolled and blended. Sorting, recursion, string handling and object identity do not fit. Chenglou's `patterns.md` is a catalogue of how a `findIndex` or a sort becomes a count, a tree or a table; each costs.

**Effects and state over time.** A pure f(props) fits. A click, a network response, an animation need a state machine with the next state as an output. Handwired does this with a host loop that writes inputs into cells, evaluates, copies the next state back and writes the DOM. That is not React with a different compiler. It is a different programming model.

**Text measurement.** This is the cliff. Both pages set text in a monospace font and count characters, because a proportional font's width is not a number you can write as a sum. My compiler gets its thresholds from running the real word-wrap, which only works because the width of a string is `8 x length`. Handwired's status log records the retreat: Notes first laid out typed text in-program by measuring segments with Chenglou's pretext library each frame, and that evening became "the browser's own text field", with "the text layout" leaving the program. The measurement lives outside, and what comes in is a number.

**Existing code.** Neither project converts a component. For my compiler the "component" had to be written against a builder in the same shape as the reference. A real tool would need a restricted language, or a tracer over a React renderer that could prove which parts are pure arithmetic, and nobody has shown either.

**Scale.** My 64-card grid is 3,902 nodes. A dashboard with nested flex containers, images with intrinsic ratios and several breakpoints will multiply, not add, because each independent discrete choice multiplies the program. Handwired's 4,646 live operations are a desktop with three windows.

## What I would take from it

The useful claim is narrower than the title. A layout that depends on its inputs through arithmetic, a handful of thresholds and a few `max`es can be written as a short list of sums, products, ReLUs and steps. That list can be evaluated exactly, differentiated away from its jumps, softened, and bounded. In my small version it was exact on 60,000 random evaluations, about 51 nodes per extra card, 16 times slower to run than the hand-written layout, with gradients correct everywhere but at the jumps, and bounds that were sound but needed branch-aware splitting to be worth anything.

What I did not establish: how big Shu's dashboard program is, whether his interval proofs close with the splitting I did not implement, and whether Handwired's tests pass in my environment. The first two need his code to run, and the sandbox would not run it. If you do run it, the numbers worth comparing with mine are the node count for the default props, and how many branch splits his bound needs to certify "never taller than the page limit" across all widths.

Reproducing mine takes about 200 lines of JavaScript and no dependencies: a six-kind node list, a tracing builder with hash-consing, and the four evaluators. The order I would build it in: the exact evaluator and the reference first, compare on random props, then add the soft, derivative and interval walks on the list you already have. The comparison with the reference is the part that keeps you honest.
