← Learnings

Every article draws its own line

Every article on this site has a thin line drawn above it. It runs flat for a stretch, takes a slanted step up or down, runs flat again — with a few small marks sitting on it. No two articles share one, nobody draws them, and this article's line is plotted from the exact words you're reading.

The whole trick, before any detail: the machine reads every letter of the article and mixes them into one number, called the seed. Then it scrambles the seed over and over, and each scramble answers one question about the drawing — where to start, how far to run flat, which way to step and how far. Same words in, same line out, forever. The rest of this article is just those two moves, shown with real numbers small enough to check by hand.

Neither move is invented here, and it's worth saying so before any of it looks clever. Mixing text into one number is a polynomial string hash — the same three lines Java runs on every String. Scrambling that number into an endless supply of new ones is a linear congruential generator, proposed by D. H. Lehmer in 1949 and older than every programming language still in use, running here on the constants published in Numerical Recipes in 1992. Both are textbook. The only fun part is pointing them at a drawing instead of a hash table.

First move: an article becomes one number

Four steps:

  1. Start a running total at zero.
  2. Read the next letter. To the computer it already is a number — its character code.
  3. New total = old total × 31 + that number.
  4. Repeat to the end of the text. The finished total is the article's seed.

Watch it run on nothing but the four-letter word line:

HASHING "LINE", STEP BY STEPBY HAND
// every letter already IS a number to a computer:
// 'a'=97, 'b'=98 … 'l'=108, 'i'=105, 'n'=110, 'e'=101

start                                    total = 0
'l' (code 108)        0 × 31 + 108  →    total = 108
'i' (code 105)      108 × 31 + 105  →    total = 3,453
'n' (code 110)    3,453 × 31 + 110  →    total = 107,153
'e' (code 101)  107,153 × 31 + 101  →    total = 3,321,844

// change one letter and every total after it lands elsewhere.
// the finished total is the SEED: 3,321,844.
FIG 1 · FOUR LETTERS IN, ONE NUMBER OUT — REAL ARITHMETIC, NOT AN ILLUSTRATION

Two articles that differ by one comma land on wildly different seeds; the same article always lands on the same one. That's the entire first move.

The drawing is a short list of numbers

These drawings are made the way a pen plotter makes one: a pen touches down at one edge of a sheet, is dragged across without ever lifting, and stops at the far edge. Two words carry the rest of this article.

The paper is the strip itself — a fixed rectangle, 620 px wide and 96 px tall, the same sheet for every article on this site. The pen is what crosses it. It has exactly one job: enter at the left edge and reach the right edge, never lifting on the way. Nothing else is ever drawn — and the paper settles more than you would think.

The pen only ever does two things.

A stretch is a flat run. The pen keeps whatever height it's at and moves to the right.

A step is a diagonal. The pen moves right and changes height at the same time. Every step is at 45°, which just means it moves right by exactly as much as it moves up or down.

Each step comes with a number — 25, say. That number is how much the height changes. Whether the change is upward or downward is decided separately, one step at a time, with no pattern to it. Where the 25 comes from, and where the up-or-down comes from, is the next section.

The pen alternates the two: stretch, step, stretch, step. The last stretch runs into the right edge, and the drawing is done. Here is what the word line from FIG 1 turns into — the paper and both moves labelled:

THE WORD 'LINE' ON THE PAPER — 620 × 96 PX, AND THE PEN'S ONLY TWO MOVES STEP · TWO ROLLS · UP OR DOWN, 16–50 PX OF HEIGHT ALWAYS 45° — HEIGHT CHANGE EQUALS RIGHTWARD TRAVEL 12 84 ENTERS ANYWHERE IN HERE ROLL #1 → 57 STRETCH · ONE ROLL · 58–170 PX HOLDS ITS HEIGHT, SO IT DRAWS FLAT THE PEN STOPS AT 620 — WHICH IS WHERE THE LIST ENDS
FIG 2 · THE WORD LINE ON THE PAPER — THE TWO MOVES, AND THE RANGES EVERY NUMBER IS PICKED FROM

So the entire picture is this list, and nothing else:

  1. The height the pen enters at.
  2. How long the first stretch runs.
  3. Which way the first step goes, and how far.
  4. How long the second stretch runs.
  5. Which way the second step goes, and how far.

…and so on to the right edge.

Every item on that list is one number, and each is picked from a range that's fixed in the code — the three ranges labelled in FIG 2. They were chosen by hand once. They are the same for every article on this site; the only thing that changes from one article to the next is the seed.

Direction is the exception. It has no range, because there are only two answers. It's a coin, not a dial.

One thing to keep straight: heights are measured from the top of the paper downward. Height 12 is near the top edge, height 84 near the bottom.

The pen also has a hard ceiling and a hard floor, at 7 and 89. A step that would carry it past either one gets flattened against the edge instead. That matters later.

Where those ranges live in the code
ASSETS/JS/STRIP.MJS — stripModel()JAVASCRIPT
// No config file, no per-article settings, no knobs. The ranges
// are literals typed once inside stripModel(), the one function
// that draws every strip on this site. H is the paper's height.

let y    = 12 + rnd() * (H - 24);   // entry height   12 … 84
const len  = 58 + rnd() * 112;      // stretch        58 … 170 px
const dir  = rnd() < 0.5 ? -1 : 1;  // direction      the coin
const jump = 16 + rnd() * 34;       // step           16 … 50 px of height

ny = Math.max(7, Math.min(H - 7, y + dir * jump));   // ceiling 7, floor 89

// Five lines lifted from the walk loop; the loop itself is left
// out. Change any number here and every drawing on the site moves.

That list is the drawing. Hand those numbers to the code that pushes the pen and there's nothing left for it to decide. The list has no fixed length — the machine keeps extending it until the pen crosses 620, then stops. For the word line it comes out fourteen numbers long.

Which is the tension this machine has to resolve: fourteen numbers needed, one number available.

One number, an unlimited supply of answers

The seed is 3,321,844. The first thing the list wants is a height between 12 and 84. There's no sensible way to read 3,321,844 as a height — and even if there were, the list needs fourteen numbers and the seed is one.

So the seed is never used as an answer. It's used as a starting position. Scramble it and you get a different number; scramble that one and you get another; the supply never runs out, and it's the same supply every time you start from 3,321,844.

A scramble is four operations, and one scramble-and-read is a roll — like rolling a die, except the same seed always rolls the same sequence. Roll #1 in full, arithmetic and all:

ROLL #1, STEP BY STEPBY HAND
// the seed from FIG 1 — a starting position, not an answer
seed                        =   3,321,844

// 1 · multiply by a big fixed constant
× 1,664,525                 =   5,529,292,384,100

// 2 · add another fixed constant
+ 1,013,904,223             =   5,530,306,288,323

// 3 · keep only what still fits in 32 bits. this is where the
//     scrambling happens: the multiply overflowed enormously,
//     and what survives the trim resembles nothing that went in
keep remainder of ÷ 2³²     =   2,683,378,371

// 4 · divide by the largest 32-bit value, 4,294,967,296
read as a fraction of 2³²   =   .6248…      ← roll #1

// the ledger rounds that to .625. and the remainder from step 3,
// not the fraction, is what gets scrambled into roll #2 — forever.

// nobody here chose 1,664,525. it is the pair published in
// Numerical Recipes in 1992, copied into a thousand programs since.
// the two numbers are not arbitrary and only work together: nudge
// 1,664,525 up by one and the sequence repeats after 32 values
// instead of all 4,294,967,296.
FIG 3 · ONE ROLL, BY HAND — WHERE .625 COMES FROM

Now the hinge — the only piece of arithmetic that connects the seed to the picture. A fraction between 0 and 1 is a position inside any range. Written out: answer = low + roll × (high − low). That one line produces every number on the list. Direction is the same idea with two slots instead of a range: under .5 the step climbs, .5 or over it drops.

Now put that together with the shape of the walk:

Earlier

“The pen alternates the two: stretch, step, stretch, step. The last stretch runs into the right edge, and the drawing is done.” — The drawing is a short list of numbers

That alternation is what spends the rolls. One roll for the entry height. Then one roll for every stretch, and two for every step — one to pick the direction, one to pick the size — until the pen runs into the right edge. That's where the fourteen numbers come from.

The first four rolls, then, turned into the first two segments of the drawing:

ROLLS #1–#4 → THE FIRST TWO SEGMENTSBY HAND
// every answer comes from one line: low + roll × (high − low)

// 1 · entry height — the band runs 12 … 84, so 72 px wide
roll #1 = .6248  →  12 + .6248 × 72       =  57

// 2 · stretch length — the range runs 58 … 170, so 112 px wide
roll #2 = .0367  →  58 + .0367 × 112      =  62 px

// 3 · direction — no range, just a coin: under .5 climbs
roll #3 = .8858  →  .8858 is over .5      =  down

// 4 · step size — the range runs 16 … 50, so 34 px wide
roll #4 = .2609  →  16 + .2609 × 34       =  25 px of height

// the pen holds height 57 for 62 px — that is why a stretch draws
// flat — then drops 25 px while travelling 25 px rightward, to
// land at height 82 — the solid piece in the drawing below.
// roll #5 sets the next stretch, and the machine is walking.
ROLLS #1–#4 ROLLS #5–#24 DRAW THE REST #1 #2 #3–4 FOUR ROLLS BUY 87 OF THE 620 PX
FIG 4 · FOUR ROLLS, FOUR ANSWERS — AND THE 87 PX OF LINE THEY BUY

Some rolls get thrown away

Rolls are free, so the machine is picky. Once it has computed a step but before it draws anything, two rules apply. The step must move the pen at least 12 px — anything smaller reads as a wobble, not a step. And it must not land within 12 px of the height the pen left two segments ago — otherwise the line bounces between the same two rails and turns into a heartbeat monitor.

A step that fails either rule is discarded, both its rolls with it, and the machine rolls a fresh pair. Four tries is the limit; if the fourth try also fails, it gets drawn anyway.

That's where the extra rolls go, and on this drawing you can see it happen. line's first step cost two rolls. Its second cost eight. The first try climbed 20 px, which put it 5 px from the height the pen had left two segments earlier — rejected. The next two both rolled downward, and the pen was already sitting 7 px above the floor, so each got flattened against it into a 7 px nudge — too small, rejected, rejected. The fourth try produced the same nudge, but four tries is the limit, so it was drawn. That's the little notch left of centre in FIG 2: the one step that doesn't look like a step. It isn't one.

STEP 2, CLOSE UP — WHY IT COST EIGHT ROLLS INSTEAD OF TWO HEIGHT 57 — WHERE THE PEN WAS TWO SEGMENTS AGO RULE 2 · ANY LANDING INSIDE THIS ±12 PX BAND IS REJECTED FLOOR 89 — BELOW HERE IS OFF THE PAPER; EVERY STEP GETS CLAMPED TO IT TRY 1 · UP 20 TRY 2 · DOWN 44 TRY 3 · DOWN 37 TRY 4 · DOWN 25 TRIES 2, 3 AND 4 ALL CLAMP TO THIS ONE POINT — A 7 PX NUDGE TOO SMALL BY RULE 1, BUT TRY 4 IS THE LIMIT, SO IT IS DRAWN PEN HERE · HEIGHT 82
FIG 5 · STEP 2 IN CLOSE-UP — FOUR TRIES, THREE REJECTIONS, AND A FORCED 7 PX NUDGE

Fourteen numbers made this drawing; getting them took twenty-four rolls. Here is the whole ledger, and the line it paid for — the same drawing as FIG 2, now with every segment tagged by the rolls that bought it:

ALL 24 ROLLS FROM SEED 3,321,844 — IN ORDER START — where the pen enters · STRETCH — a flat run, that many px long UP / DOWN — a 45° step between heights, that many px · REJECT — thrown out, both rolls with it #1 .625 → start at height 57 #2 .037 → stretch, 62 px #3#4 .886 .261 → down, 25 px #5 .324 → stretch, 94 px #6–#13 3 rejects, then a forced down 7 px #14 .544 → stretch, 119 px #15–#18 1 reject, then up 29 px #19 .560 → stretch, 121 px #20–#23 1 reject, then up 45 px #24 .744 → stretch, into the right edge — done #1 #2 #3–4 #5 #6–13 #14 #15–18 #19 #20–23 #24 EVERY SEGMENT PAID FOR BY A NUMBERED ROLL — NOTHING ELSE DECIDES ANYTHING SAME SEED → SAME ROLLS → SAME LINE, EVERY TIME
FIG 6 · ALL 24 ROLLS FROM SEED 3,321,844 — AND THE LINE THEY DREW

The marks are the only part not decided by rolls

Everything above has been the line. The marks sitting on it are a separate system that knows nothing about seeds or rolls: they are the article's table of contents. One rule decides what gets one — the block needs an id in the HTML, and that's all. No id, no mark. Several blocks here are deliberately unmarked, because a strip with a mark on every block is a comb, not a map.

Laying one out takes four steps:

  1. Find every block carrying an id.
  2. Work out how far into the article it starts, as a fraction. A heading a quarter of the way down scores 0.25. Nothing is allowed past 0.05 or 0.95, so no mark ends up jammed against an edge.
  3. Multiply by the strip's width. A quarter of 620 px puts the mark at x = 155.
  4. If x lands on one of the diagonals, slide the mark sideways to the nearest flat run instead. Its height is then simply that run's height.

Step 4 is why a mark's height tells you nothing, and why the marks sit at odd heights that have nothing to do with what they point at.

WHAT A MARK IS — SHAPE, PLACE, AND TARGET SECTION CODE PANEL FIGURE 18 PX TARGET · 5 PX DOT NEVER ON A SLOPE — MOVED TO THE NEAREST FLAT LEFT TO RIGHT = HOW FAR INTO THE ARTICLE. NOTHING ELSE ABOUT A MARK MEANS ANYTHING. HEIGHT CARRIES NOTHING
FIG 7 · THE MARK VOCABULARY — THREE SHAPES, ONE MEANINGFUL AXIS

Now the real thing: this article's own line

Everything above ran on four letters. The strip at the top of this page is the same machine run on this article's entire text — thousands of characters mixed into one seed, the seed rolled until the pen crossed the paper. Here is that line again, with its actual ledger and every decision tagged, exactly as FIG 6 did for the word line:

THIS ARTICLE'S SEED, ROLLED — LEDGER FROZEN AT PUBLISH
FIG 8 · THIS ARTICLE'S OWN LINE, DECISION BY DECISION

A longer text doesn't buy a busier line — this drawing has the same handful of stretches as the four-letter one, just in different places. Any text, four letters or four thousand, mixes down to one seed, and every seed rolls the same couple dozen answers onto the same paper. Different words, different line; never a denser one.

Aside

However long the article, the seed lives in 32 bits — about 4.3 billion possible lines. Infinitely many possible articles, finitely many drawings: somewhere out there, two articles could share one. Fine for art; never do this for passwords.

All the computing happens before you arrive

None of this runs when you load the page. At publish time the engine hashes the text, rolls the rolls, and writes the finished drawing into the article itself — the geometry, the marks, all of it. The script in your browser parses that record and draws it, nothing more. The Learnings grid goes one step further: each card carries this same drawing, amplified to fill the card and inlined as plain markup, so the grid ships no drawing code at all.

For the technically curious, the production pipeline has one extra link: the full text is first fingerprinted with djb2, the fingerprint is stitched to the article's name, and that string is what the ×31 move turns into the seed. The rolls come from a linear-congruential generator — the FIG 3 arithmetic has a name. The receipts:

PUBLISH-CORE.MJSJAVASCRIPT
// the article's identity, frozen at publish time
export function contentSeed(slug, bodyHtml) {
  const text = bodyHtml.replace(/<[^>]+>/g, '');
  let h = 5381 >>> 0;
  for (let i = 0; i < text.length; i++)
    h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;
  return slug + ':' + h.toString(36);
}
FIG 9 · TWENTY YEARS OF HASH RESEARCH, IGNORED RESPONSIBLY

That's the machine: two moves, both checkable by hand, run once at publish and never again. The line above this article is the word-count-heavy sibling of FIG 1's four letters.