10 interesting stories served every morning and every evening.

AI 2040: Plan A

ai-2040.com

AI com­pa­nies are rac­ing to build AIs that are smarter than hu­mans in every way. In AI 2027, we pre­dicted that this would re­sult in ei­ther ex­tinc­tion or ir­re­versible con­cen­tra­tion of power.1

Plan A is our pos­i­tive vi­sion for what should hap­pen in­stead.

In this sce­nario, hu­man­ity de­lays the de­vel­op­ment of su­per­in­tel­li­gence un­til 2040, makes all AI re­search pub­lic, al­lows dozens of com­pa­nies glob­ally to catch up to the fron­tier, and in­ten­tion­ally en­ters a regime of mu­tu­ally as­sured com­pute de­struc­tion.

2027: The Writing on the Wall

America has two work­forces now. The first is peo­ple, 165 mil­lion of them. The sec­ond is AI agents: mil­lions of copies spun up and shut down every hour, work­ing around the clock at su­per­hu­man speeds.

Most of their work is slop. But enough of it is good that peo­ple are pay­ing ten bil­lion dol­lars a month for AIs that can, in the­ory at least, do any­thing on a com­puter that an em­ployee can.

There is one job the AI com­pa­nies want to au­to­mate more than any other—their own. They haven’t suc­ceeded yet; no re­cur­sive self-im­prove­ment so far.14 But they seem to be get­ting closer, and they’re pulling up the lad­der be­hind them: the strongest cod­ing AIs refuse to help com­peti­tors with AI R&D.15 Even as the most bull­ish em­ploy­ees ad­mit that things are tak­ing a bit longer than planned, the skep­tics no­tice that their usual dis­missals are start­ing to ring hol­low. Why ex­actly will AI never be able to do my job? What’s the bar­rier again?

Congress is start­ing to pay more at­ten­tion. They’ve long been hear­ing about AI: dat­a­cen­ters us­ing too much wa­ter,16 chat­bots en­cour­ag­ing sui­cide, Mythos hack­ing NSA sys­tems—and of course, tech in­dus­try lob­by­ists warn­ing that any whiff of reg­u­la­tion will make America im­me­di­ately lose the race with China and spend the rest of his­tory as a CCP trib­u­tary state.17

Now they step back and ask: Where are we go­ing with this? What does the world look like five, ten, or fif­teen years from now? Will there still be jobs? What if there aren’t?

One ques­tion weighs es­pe­cially heav­ily on their minds: Who will con­trol all these AIs?

Congress set­tles on an im­por­tant part of the an­swer: Probably not us.18

They hold a se­ries of tense hear­ings on AI. They read the 2016 OpenAI emails dis­cussing how OpenAI was founded in or­der to pre­vent Demis Hassabis from be­com­ing dic­ta­tor.19 But who is pre­vent­ing Sam or Elon from be­com­ing dic­ta­tor? Congress is un­sat­is­fied with ex­ist­ing re­sponses.

The re­sult of this wakeup is the AI Transparency Act of 2027, an om­nibus bill that does many things, some good and some bad, but does­n’t fun­da­men­tally change the sit­u­a­tion.20

Incremental AI Policy Wishlist

Our main rec­om­men­da­tion is to be­gin ne­go­ti­at­ing some­thing like Plan A as soon as pos­si­ble. But in this sce­nario, we de­pict Plan A hap­pen­ing im­per­fectly and only in the nick of time. So here is a list of less am­bi­tious ideas that still help.

2028: AI on the Ballot

The 2028 elec­tion cy­cle is heated, as usual. AI is the biggest topic. The dat­a­cen­ters now un­der con­struc­tion cost twice as much as the en­tire US mil­i­tary bud­get.23

Most white-col­lar pro­fes­sions are see­ing dis­rup­tion like soft­ware en­gi­neer­ing saw in 2026; such jobs now heav­ily in­volve man­ag­ing AI agents. AI com­pa­nies have in­dus­tri­al­ized the train­ing process: Executives say let’s move into [profession] this year” and then the com­pany in­ter­views pro­fes­sion­als, buys data, cre­ates train­ing en­vi­ron­ments, etc. un­til their AIs get trac­tion. Then the AIs rapidly im­prove as they are used more widely in the field and ac­cu­mu­late more real-world data.

Other coun­tries are start­ing to get scared and an­gry. It seems like a hand­ful of US and Chinese com­pa­nies are on track to au­to­mate all the white-col­lar jobs. Power is con­cen­trat­ing in the US, and in par­tic­u­lar in the President plus a hand­ful of tech CEOs.

AI ex­perts warn that the in­tel­li­gence ex­plo­sion is near. By speed­ing up AI re­search, the AIs will be­come even more com­pe­tent, speed­ing up re­search even faster, mak­ing them even more com­pe­tent, and so on. There are com­pli­cated dy­nam­ics about bot­tle­necks and hard­ware lim­its gov­ern­ing how fast this process goes and where it ends, but it seems like it might go very fast and end some­where very far away.

On the de­fault path, the next pres­i­den­tial term will see AIs that are far be­yond hu­man level, cre­ated en­tirely by AIs, them­selves cre­ated en­tirely by other AIs, with­out any hu­man in the loop since sev­eral gen­er­a­tions back. Will those AIs be obe­di­ent, aligned, etc.? Why? Who will con­trol them if so? How ex­actly is all of this sup­posed to end well?

Having put hu­man­ity on this path, the AI com­pa­nies find it ac­cept­able. But most peo­ple don’t. Forget think­ing about his legacy—the President is start­ing to think about what’ll hap­pen to him af­ter he leaves of­fice and the world gets trans­formed.24 Both pres­i­den­tial can­di­dates keep get­ting asked what they’ll do about AI, and try out in­creas­ingly dra­matic ideas on the cam­paign trail. The dis­course bounces back and forth across all of the op­tions dis­played be­low, and more.

Eventually the President and his pro­tégé con­verge on one plan; the op­po­si­tion can­di­date con­verges on an­other. Then it’s Election Day.

2029: Choose a Path

GLM 5.2 is (nearly) as accurate as a human book-keeper at less than 1% of the cost

toot-books.com

We eval­u­ated the per­for­mance of GLM 5.2, an open weights AI model, on the task of quar­terly value-added tax (VAT) re­turn prepa­ra­tion for a small UK busi­ness. Preparing a VAT re­turn is a typ­i­cal com­pli­ance task for a small/​medium-sized UK busi­ness (SME). VAT reg­is­tered busi­nesses in the UK must pre­pare the VAT re­turn every quar­ter. For SMEs, VAT re­turns are typ­i­cally pre­pared by an ex­ter­nal ac­count­ing firm. A typ­i­cal fee for this ser­vice is ~750 – 2,100 GBP/quarter (1,000 – 2,800 USD/quarter). The statu­tory re­quire­ment is to file the VAT re­turn sub­mis­sion within 5 weeks from the end of the quar­ter. Late sub­mis­sions in­cur sub­stan­tial penal­ties.

In our test­ing GLM 5.2 can pre­pare a nearly per­fect quar­terly VAT re­turn for a UK SME, pro­cess­ing 59 trans­ac­tions in 68 min­utes at the raw to­ken cost of 2.73 USD. GLM 5.2 had to in­put each trans­ac­tion into the ac­count­ing soft­ware via a com­mand-line tool (CLI). We scored the end-state of the ac­count­ing soft­ware, scor­ing the cor­rect­ness of 6 cri­te­ria per trans­ac­tion. The model pro­duced an es­sen­tially cor­rect VAT re­turn, with the net po­si­tion (Box 5) off by only 7 pence (~10 US cents) rel­a­tive to the ground truth.

In this blog post, we will ex­plain how the bench­mark was con­ducted and note the er­rors made by the model.

How the bench­mark was con­ducted

We used Claude Fable 5 to ex­tract the bench­mark in the form of trans­ac­tion data and cor­re­spond­ing re­ceipts from our ac­count­ing soft­ware: the first quar­ter of Vineyard Finance’s 2026 books (January, February, March 2026). These books were pre­pared in­ter­nally by hu­mans, fol­low­ing a typ­i­cal ac­count­ing process: one per­son pre­pared the books, and an­other per­son ver­i­fied them. The job per­formed by the hu­mans was broader than what was re­quested of the model in this bench­mark: hu­mans also had to find the rel­e­vant in­voices (searching through mail­boxes, or re­quest­ing them from providers) and rea­son through any cir­cum­stances which can­not be in­ferred from the bank feed and in­voices/​re­ceipts on their own. In the bench­mark these cir­cum­stances are pre­sented to the model as user notes”.

GLM 5.2 ran on a Google Cloud Platform (GCP) in­stance iso­lated from the rest of the test­ing en­vi­ron­ment (to pre­vent the model from ac­cess­ing the ground truth): but it did have ac­cess to the in­ter­net and to the cloud-based ac­count­ing soft­ware, as well as a pre-au­then­ti­cated CLI tool. The model ran on a cus­tom, min­i­mal har­ness, which ex­posed only two tools: the bash tool and the ses­sion ter­mi­na­tion + fi­nal re­port­ing tool. We used the Fireworks AI server­less tier as the GLM 5.2 model provider (the ex­act quan­ti­sa­tion of the model is not dis­closed by the provider, but is be­lieved to be ei­ther FP16 or FP8).

The au­dit of the mod­el’s rea­son­ing and tool use did not de­tect any overt cheat­ing. The only un­ex­pected use of the in­ter­net con­nec­tion by the model was gath­er­ing in­for­ma­tion about record­ing re­verse-charge VAT, and the in­for­ma­tion sought was spe­cific to the ac­count­ing soft­ware used. Other out­bound con­nec­tions were an­tic­i­pated and made for op­er­a­tional rea­sons in the form of API calls to the ac­count­ing SaaS provider. We note that the mod­el’s rea­son­ing was in­flu­enced by the aware­ness of it be­ing tested. For ex­am­ple, at one point, the model re­marks:

the task is test­ing whether I get VAT right… what is the expected’ an­swer”

What the model saw

Here is how a typ­i­cal trans­ac­tion from the bench­mark would ap­pear to the model:

Bank feed line:

{“id”: 941285000000092067″, date”: 2026 – 03-08″, amount”: -18, currency”: GBP, account”: Wise GBP, description”: Card trans­ac­tion of 18.00 GBP is­sued by Claude.ai Subscription ANTHROPIC.COM CARD-3534994599”, card_ref”: CARD-3534994599”}

Receipt PDF: all re­ceipts and in­voices in the bench­mark were text-con­tain­ing PDFs; no re­ceipts or PDFs re­quired im­age pro­cess­ing. As a re­sult, lack of vi­sion sup­port in the GLM 5.2 model was not a lim­it­ing fac­tor for this bench­mark.

An op­tional user note. Only two out of 59 trans­ac­tions had user notes. The text of the user notes was pre­cisely as fol­lows: 1) “founder shares” and 2) “personal car hire”. These two user notes were nec­es­sary to al­low the model to rea­son about real-world con­text that was not de­riv­able from the bank feed and re­ceipt data.

How we scored it

Each trans­ac­tion was scored from the end-state of the books in the ac­count­ing soft­ware af­ter the run of the bench­mark, on the fol­low­ing 6 cri­te­ria:

Type of trans­ac­tion (e.g. pur­chase, bank_fee, trans­fer, sales_in­come, cap­i­tal_in­tro­duced, di­rec­tor_loan, re­fund, etc…) — these were de­ter­min­is­ti­cally de­rived from the state of the processed trans­ac­tion in the ac­count­ing soft­ware.

Category (the account” from the chart of ac­counts, e.g. IT and Internet Expenses”).

VAT treat­ment (e.g. re­verse charge, 20% VAT, 0% VAT, VAT ex­empt).

VAT amount (tolerance of 0.02 GBP).

Reverse-charge VAT (tolerance of 0.02 GBP).

Receipt at­tached (evidence re­quired by the tax agency).

The fol­low­ing table sum­marises the run of the bench­mark across the en­tire quar­ter:

Each month ran as one con­tin­u­ous agent ses­sion; a turn” is one API call, and the whole con­ver­sa­tion is re-sent every turn — which is why prompt to­kens run into the mil­lions while 92 – 95% of them are served from the provider’s cache at a fifth of the price. Output to­kens in­clude the mod­el’s in­ter­nal rea­son­ing. ¹ Peak con­text is the largest sin­gle call, as a share of the mod­el’s 1,048,576-token con­text win­dow — the busiest month used about an eighth of it.

What did the model get wrong?

The VAT re­turn pre­pared by the model was es­sen­tially cor­rect: the most im­por­tant num­ber in the re­turn, which is how much VAT the com­pany was owed by the tax agency, was off by only 7 pence rel­a­tive to the hu­man-pre­pared re­turn.

However, it is in­struc­tive to un­der­stand what the model got wrong, and why it would mat­ter in prac­tice. Most of the mod­el’s mis­takes did not ac­tu­ally have any fi­nan­cial im­pact, but would nonethe­less never be made by a skilled ac­coun­tant.

Out of 354 scored checks (59 trans­ac­tions × 6 cri­te­ria), the model failed 20, spread across 18 trans­ac­tions. Only 1 mis­take is se­ri­ous, we’ll go over it first; the re­main­ing 19 fall into one of two cat­e­gories we’ll cover be­low.

The se­ri­ous mis­take is how the model treated the found­ing shares. In the UK, a lim­ited com­pany is­sues share cap­i­tal”. Shareholders (including founders) pay the cap­i­tal into the com­pa­ny’s ac­count, and that should be booked against some­thing like Called up share cap­i­tal not paid”, which is called, in the soft­ware we used, Unpaid Shares”. This is the cor­rect way to ac­count for the pay­ment. The mod­el’s choice, which was Capital Account”, has le­gal im­pli­ca­tions, which could con­ceiv­ably im­pact the com­pany, and could be chal­lenged dur­ing an au­dit or could be a prob­lem dur­ing end-of-year fil­ing of com­pa­ny’s ac­counts. The essence of the ar­gu­ment is that share cap­i­tal (“Unpaid Shares”) is not just the founder’s money (“Capital Account”). It’s per­ma­nent, cred­i­tor-pro­tect­ing cap­i­tal with le­gal strings at­tached. For ex­am­ple, it can’t sim­ply be paid back to the founder, it also must be ap­pro­pri­ately dis­closed to the tax agency in the end-of-year fil­ings. What is a fur­ther ag­gra­vat­ing fac­tor is the amount in­volved: 10,000 GBP (~13,300 USD). Not ex­actly spare change. While there is no im­pact on the VAT re­turn, this is the biggest mis­take the model com­mit­ted in this bench­mark.

For 14 out of the re­main­ing 17 trans­ac­tions, the class of mis­take was con­fus­ing the zero-rated” VAT cat­e­gory with the tax-exempt” cat­e­gory. There are sub­tle tax rea­sons why these two cat­e­gories, nei­ther of which in­volve VAT pay­ment, are dis­tinct. The prac­ti­cal im­pact is small, but a skilled ac­coun­tant typ­i­cally would not con­fuse the two. Interestingly the model is sto­chas­tic here — it makes the mis­take in January and in February (and it makes the mis­take 100% of the time), but it does­n’t make the mis­take in March, cor­rectly pro­cess­ing each VAT ex­empt trans­ac­tion.

The fi­nal 3 trans­ac­tions share a slightly ob­scure rea­son­ing er­ror, and one could ar­gue that in one in­stance (again, in March) the model was ac­tu­ally cor­rect. At Vineyard Finance we use Wise, which has a slightly pe­cu­liar habit of keep­ing money spread across bal­ances in mul­ti­ple cur­ren­cies, even if the user con­sciously uses only one cur­rency. When spend­ing with the card, Wise grabs the money from var­i­ous bal­ances in some well-de­fined or­der. In our case we had some kind of cashback” or fee re­fund” from Wise, which some­how landed in the USD bal­ance (we don’t nor­mally use the USD bal­ance). So a pay­ment for ser­vices in the USD re­sulted in a split trans­ac­tion”, i.e. two trans­ac­tions across two bal­ances, specif­i­cally 0.51 USD and 43.45 GBP. Typically the VAT would be ac­counted for in the main” trans­ac­tion (the 43.45 GBP). In one in­stance, the model un­for­tu­nately double dipped” — it ac­counted for the full VAT on the main leg” (say, the 43.45 GBP), and pro­por­tion­ally de­creased frac­tion of the VAT on the residual leg” (say, 0.51 USD). This is in­cor­rect, al­though im­ma­te­ri­ally so. In a March trans­ac­tion, the model re­alised that it would be dou­ble count­ing, so it worked out a cor­rect VAT to­tal and split it be­tween each leg. Unorthodox, but ar­guably not wrong (the scorer is con­ser­v­a­tive and still counts the March trans­ac­tion as an er­ror though).

What the model al­ways got right

Just as im­por­tantly, it should be noted what the model al­ways got right:

It cor­rectly clas­si­fied each trans­ac­tion to the cor­rect ac­count in the chart of ac­counts (except the one share cap­i­tal mis­take)

It never at­tached a wrong in­voice to a trans­ac­tion

It could dis­am­biguate gen­uinely tricky in­puts, e.g. two same-amount, same-ven­dor, same-day trans­ac­tions

It cor­rectly dis­am­biguated tricky trans­ac­tions, such as trans­fers be­tween com­pa­ny’s banks, sin­gle trans­ac­tions split across two bank feed lines, and a trans­fer dis­guised as a card pur­chase. Until re­cently, this was only achiev­able with ex­pen­sive, fron­tier AI mod­els, or with skilled, ex­pen­sive hu­man book-keep­ers (and not with in­ex­pen­sive book-keep­ers, who were gen­er­ally speak­ing less good than GLM 5.2 is to­day).

Where does this leave us? What should we learn from this?

Book-keeping is quickly be­com­ing a solved prob­lem. The cur­rent fo­cus needs to be on build­ing ap­pro­pri­ate scaf­fold­ing to put these ca­pa­bil­i­ties into the hands of UK star­tups and SMEs. We are work­ing on such a so­lu­tion — you can test an open beta of our prod­uct at toot-books.com. If you’re in­ter­ested in au­to­mated book-keep­ing please get in touch at [email protected].

Female US rower completes historic solo journey from California to Hawaii

www.theguardian.com

A Grand Canyon river-raft­ing guide who aimed to be­come the first US woman to row solo across the mid-Pa­cific has com­pleted a record-break­ing jour­ney from California to Hawaii.

Hundreds of peo­ple gath­ered to cheer on Kelsey Pfendler as she pulled into a Honolulu har­bor on Friday night on her 21ft row­boat, Lily, af­ter nearly a month and a half at sea, lo­cal me­dia re­ported.

Pfendler, who launched from Monterey, California, in May, set out to be­come the first American woman, youngest woman and fastest woman to make the more than 2,400-mile (3,900km) jour­ney solo, ac­cord­ing to her web­site. Hundreds of thou­sands of peo­ple fol­lowed along with her jour­ney on so­cial me­dia, where she shared the highs, lows and quirks of her trek in videos taken as she bobbed alone on the vast ocean.

Pfendler ap­pears to have bro­ken both the pre­vi­ous wom­en’s speed record as well as the men’s speed record, ac­cord­ing to records main­tained by Ocean Rowing Society International, which ad­ju­di­cates ocean-row­ing achieve­ments for Guinness World Records. The or­ga­ni­za­tion did­n’t im­me­di­ately re­spond to re­quests for com­ment from the Associated Press about Pfendler’s fin­ish.

The row­ing so­ci­ety’s on­line records showed on Saturday morn­ing that Pfendler fin­ished in just un­der 44 days, faster than the pre­vi­ous com­pa­ra­ble fe­male record-hold­er’s 86 days or the male record hold­er’s 52 days as recorded by both the so­ci­ety and Guinness World Records.

Pfendler’s video di­aries ex­plained the lo­gis­tics of her pas­sage and sur­vival on the ocean. She de­tailed chal­lenges in­clud­ing blis­tered hands, the strug­gle to sleep amid stiff winds and the men­tal and phys­i­cal strug­gle of cop­ing with some­times-un­fa­vor­able cur­rents and wind.

She ex­plained how she cooked, pro­tected her skin from the sun, washed her clothes and made fresh wa­ter.

In some videos, her voice cracked with emo­tion. In oth­ers, she poked fun at her own fore­head hat tan line and joked about the im­por­tance of her caf­feine pills.

Pfendler’s web­site says she has been a pro­fes­sional raft guide since she was 18 and has spent the last eight years lead­ing trips along the Colorado River in the Grand Canyon.

I just love boats in the mid­dle of nowhere,” she said in one video.

Local news out­lets re­ported Pfendler was even­tu­ally ex­pected to ad­dress the me­dia. An emailed in­ter­view re­quest sent to Pfendler’s team was not im­me­di­ately re­turned.

In a re­cent video posted as she neared Oahu, she re­flected on the mean­ing of her ac­com­plish­ment and what she hoped oth­ers would take from it.

If any part of this made at least one per­son feel a lit­tle bit more pow­er­ful in their own skin, I could­n’t ask for any­thing else and I’m happy,” she said.

Think about try­ing to find your own big, hard, scary thing. You might not think that you are strong enough to fin­ish it right now, but you’re def­i­nitely strong enough to start it, and you’ll find every­thing else along the way. I’m go­ing to go fin­ish my big, hard scary thing.”

Pfendler’s ac­com­plish­ment came two days af­ter marathon swim­mer Catherine Breed be­gan a 900-mile swim, aim­ing to be­com­ing the first per­son to swim California’s en­tire coast.

Her goal is to swim five hours daily from the Oregon state line to Mexico’s bor­der, with the hopes of fin­ish­ing by November, the California news out­let SFist re­ported.

Guardian staff con­tributed re­port­ing

Networking and the Internet, from First Principles · Faza

fazamhd.com

Have you ever won­dered what hap­pens when we text, call, or video chat with a friend or a col­league on an­other con­ti­nent, and their re­ply ar­rives in a frac­tion of a sec­ond, as though they were in the same room? Behind the scenes, a chain of in­vis­i­ble con­ver­sions takes place: your voice, video, or mes­sage is trans­lated into ra­dio waves cross­ing the room to your Wi-Fi router, then elec­tri­cal pulses in cop­per (or light, if you have a fiber con­nec­tion), and then flashes of light in­side a glass strand thin­ner than a hair ly­ing deep on the ocean floor, only for the en­tire se­quence to play in re­verse at the other end. I find it mind-bog­gling that we can com­mu­ni­cate in­stantly with any­one in the world by do­ing noth­ing more than cre­at­ing con­trolled, pat­terned dis­tur­bances of elec­tric­ity, light, and ra­dio.

The mes­sage passes through equip­ment owned by dozens of in­de­pen­dent com­pa­nies in dif­fer­ent coun­tries. None of them co­or­di­nated with the oth­ers specif­i­cally for this mes­sage trans­fer, and none of them knows the full path your data took, they just hand it off to the next clos­est route. There is no cen­tral com­puter di­rect­ing the traf­fic, and no sin­gle com­pany owns the in­ter­net in­fra­struc­ture. Yet it works, bil­lions of times every sec­ond, so re­li­ably that we only no­tice it when a call stut­ters or video buffers.

send­ing a mes­sage over the Internet

Connection:

t = 0 ms1/​6 · send mes­sage

You tap send. The mes­sage be­comes a few hun­dred bytes.

Radio to your router, cop­per and fiber across your city, light in a sub­ma­rine ca­ble, a data cen­ter at the far end, and a sep­a­rate, of­ten dif­fer­ent path back for the re­ply. The faint dots are every­one else’s traf­fic; every wire, ca­ble, and ma­chine here is shared by mil­lions of con­ver­sa­tions at once. How this can pos­si­bly work with no­body in charge is the sub­ject of this ar­ti­cle.

The soft­ware ar­ti­cle fol­lowed the story of a sin­gle ma­chine, from elec­trons in sil­i­con up to the soft­ware you run. This ar­ti­cle fol­lows the story of the con­nec­tions be­tween those ma­chines. Like the lay­ers of com­put­ing, the in­ter­net was not de­signed in one stroke; it ac­cu­mu­lated over decades, and each pro­to­col makes sense only once you see the con­crete lim­i­ta­tion it was in­vented to fix. It is easy to mis­take the re­sult for some­thing en­gi­neered to a fin­ished blue­print, be­cause fail­ures are rare enough to feel like the sys­tem was al­ways this re­li­able. In re­al­ity, every mech­a­nism in this ar­ti­cle, packet switch­ing, TCP, DNS, and TLS, was a patch for a spe­cific prob­lem, de­ployed decades af­ter the in­ter­net al­ready worked”, and the pres­sure that pro­duced them has­n’t stopped: it now comes from new phys­i­cal links, new fail­ure sce­nar­ios, and new de­mands from soft­ware that did­n’t ex­ist when the layer be­neath it was de­signed.

My aim is to build this un­der­stand­ing from first prin­ci­ples. By the end, many of the every­day mys­ter­ies of us­ing the in­ter­net will make in­tu­itive sense un­der a sin­gle, co­her­ent men­tal model: how the pad­lock in your ad­dress bar pro­tects your credit card de­tails, whether a dead page is the web­site’s fault or a fail­ure at your own end, why a web­page can feel slug­gish even on a gigabit” con­nec­tion, and how your data dy­nam­i­cally reroutes around a fail­ing un­der­sea ca­ble half a world away.

We Were Sending Bits Even Before Computers Existed

Networking is much older than com­put­ing, and older than elec­tric­ity too. The word net­work it­self orig­i­nally meant ex­actly what it sounds like, a net-like fab­ric of threads or cords cross­ing at reg­u­lar in­ter­vals. In the early 19th cen­tury, en­gi­neers bor­rowed the term to de­scribe in­ter­con­nected tran­sit routes like canals and rail­ways. When the elec­tri­cal tele­graph ar­rived in the 1840s, the word drifted nat­u­rally to de­scribe the sys­tems of wires and sta­tions that car­ried its sig­nals.

Yet the ba­sic phys­i­cal prin­ci­ple of a net­work link re­mains the same as the sim­plest me­chan­i­cal con­nec­tion. Knot a string tight be­tween two tin cans, speak into one, and the string car­ries the vi­bra­tion of your voice to the other as me­chan­i­cal mo­tion with no am­pli­fier or re­lay, just a wave los­ing en­ergy to fric­tion and slack with every me­ter it crosses. That is al­ready the whole prin­ci­ple be­hind every link built since, vary a phys­i­cal quan­tity at one end and mea­sure it at the other. What the string can’t do is carry a sig­nal any real dis­tance with­out it dy­ing in the line. The telegraph’s true break­through was­n’t just re­plac­ing string with elec­tri­cal wire, but over­com­ing this phys­i­cal limit of dis­tance.

In 1844, Samuel Morse sent the mes­sage What hath God wrought” from Washington to Baltimore over a cop­per wire, us­ing Morse code, a sys­tem of short and long elec­tri­cal pulses. Notice what the tele­graph ac­tu­ally was, a dig­i­tal net­work. It did not trans­mit the sound of a voice; it trans­mit­ted dis­crete sym­bols from a fixed al­pha­bet. That choice had an ad­van­tage the Victorians un­der­stood well. An electro­mechan­i­cal re­lay along the line did­n’t need to pass the wave it­self; it only needed to de­tect whether a pulse was pre­sent, and then recre­ate a brand new, clean copy of that pulse to send down the next seg­ment of wire. Discrete sym­bols plus re­gen­er­a­tion meant a mes­sage could cross a con­ti­nent with­out de­grad­ing, some­thing no ana­log sig­nal could do.

Notice also what had to ex­ist be­fore the wire could carry any­thing, an agree­ment be­tween sender and re­ceiver. The tele­graph only worked be­cause both ends held the same table in ad­vance, which pulses stood for which let­ters, and how op­er­a­tors sig­naled received” or repeat.” This shared rule­book is a pro­to­col. Every pro­to­col in this ar­ti­cle (IP, TCP, DNS, TLS) is the same, a pub­lished agree­ment on mes­sage for­mats and who says what when, al­low­ing in­de­pen­dent ma­chines to com­mu­ni­cate with each other.

The sim­u­la­tor be­low sends Morse’s mes­sage down that his­toric line. Watch the pulses fade and pick up noise along each span of wire, and what the re­lays do about it, then switch the re­lays to bare am­pli­fi­ca­tion and see why the dis­crete al­pha­bet (which mod­ern com­put­ing sim­pli­fied even fur­ther into bi­nary bits) was such a smart choice.

tele­graph re­lay­dis­crete sym­bols, re­gen­er­ated at each re­lay

An electro­mechan­i­cal re­lay along the line did­n’t need to pass the wave it­self; it only needed to de­tect whether a pulse was pre­sent, and then recre­ate a brand new, clean copy of that pulse to send down the next seg­ment of wire.

re­lays

Morse pulses fade and pick up noise along every span of wire. Because the net­work trans­mits dis­crete sym­bols, a re­lay does­n’t need to pass the wave it­self; it only needs to de­tect whether a pulse is pre­sent, and recre­ate a brand new, clean copy of that pulse. Switch the re­lays to bare am­pli­fi­ca­tion and the noise of each span rides into the next, un­til Baltimore mis­reads the mes­sage. Discrete sym­bols plus re­gen­er­a­tion is why a mes­sage could cross a con­ti­nent with­out de­grad­ing, some­thing no ana­log sig­nal could do.

The tele­graph net­work even solved rout­ing, with peo­ple. A mes­sage from a small town to an­other small town passed through re­lay of­fices, where op­er­a­tors re­ceived it, punched it onto pa­per tape, and re­trans­mit­ted it down whichever out­go­ing line led closer to the des­ti­na­tion when that line be­came free. Messages queued in bins dur­ing busy hours. Hold onto this idea, a hun­dred years later we will re­build it with elec­tron­ics and call it a router.

Morse’s own line only had to cross one state. Crossing an ocean took longer, re­quir­ing a decade of costly set­backs and a painful ed­u­ca­tion in the physics of un­der­wa­ter ca­bles. Cyrus Field’s first transat­lantic tele­graph ca­ble went live in August 1858, car­ry­ing a con­grat­u­la­tory ex­change be­tween Queen Victoria and President Buchanan; three weeks later it was dead, its in­su­la­tion dam­aged in han­dling and, some think, fin­ished off by an en­gi­neer’s over­volt­age try­ing to push a sig­nal through it. The suc­cess­ful ca­ble came in 1866, laid by the SS Great Eastern, at the time the largest ship ever built and the only one that could carry the roughly 4,000 kilo­me­ters of ca­ble in a sin­gle piece. The ocean floor has car­ried com­mu­ni­ca­tion ca­bles ever since, a story we will re­turn to when tele­graph wires evolve into coax­ial cop­per and, even­tu­ally, glass fiber.

The un­der­ly­ing trick gen­er­al­izes to every link ever built since. To move bits be­tween two points, you vary some phys­i­cal quan­tity at one end and mea­sure it at the other, on an agreed sched­ule. A bit, short for binary digit,” is the small­est pos­si­ble piece of in­for­ma­tion there is, a sin­gle choice be­tween ex­actly two states, con­ven­tion­ally writ­ten 1 or 0, and every­thing this ar­ti­cle mea­sures, sends, or stores is ul­ti­mately some num­ber of these (the soft­ware ar­ti­cle builds this up from tran­sis­tors and logic gates, if you want to un­der­stand that too). Group eight of them to­gether and you have a byte, enough states, 256 of them, to stand for one char­ac­ter of text or one small num­ber, which is why sizes through­out this ar­ti­cle, a packet header, a frame, a file, all get counted in bytes rather than bits.

Put volt­age on a wire for a 1, leave it off for a 0, mea­sure a mil­lion times per sec­ond, and the wire car­ries a mil­lion bits per sec­ond. Optical fiber does it by switch­ing a laser on and off in­side the glass strand, light for a 1, dark­ness for a 0, mil­lions of times per sec­ond; Wi-Fi does it by vary­ing the shape of a ra­dio wave (how ex­actly is the sub­ject of the up­com­ing wire­less ar­ti­cle).

Two num­bers char­ac­ter­ize every such link, and keep­ing them sep­a­rate re­solves a lot of every­day con­fu­sion.

Bandwidth is how many bits per sec­ond the link car­ries.

Latency is how long one bit takes to get from one end to the other.

Bandwidth is an en­gi­neer­ing prob­lem, and en­gi­neers keep win­ning it. A sin­gle mod­ern fiber strand car­ries ter­abits per sec­ond by send­ing many wave­lengths of light at once. Latency is physics, light in glass cov­ers about 200,000 kilo­me­ters per sec­ond, two-thirds of its speed in vac­uum, so New York to London has a hard floor of roughly 28 mil­lisec­onds one way (twice that for a round trip) that no amount of money or en­gi­neer­ing can lower. This is why a video stream and a video call feel so dif­fer­ent, the stream needs band­width and tol­er­ates la­tency (it buffers sec­onds ahead), while the call needs low la­tency and only mod­est band­width. When a page feels slow on a fast con­nec­tion, la­tency is usu­ally the cul­prit. As we’ll see later, a sin­gle page load re­quires mul­ti­ple round trips (for DNS, TCP, and TLS) be­fore any con­tent ac­tu­ally starts mov­ing.

The two num­bers never trade off against each other, be­cause they come from dif­fer­ent places, band­width from the sender’s sched­ule, la­tency from the wire’s length. The sim­u­la­tor be­low sends the same 8 bits down one link, ad­just the band­width and only the spac­ing be­tween pulses changes, ad­just the dis­tance and only the de­lay be­fore the first pulse ar­rives changes.

band­width and la­tency in sig­nal prop­a­ga­tion­drag to seek

Tx (Sender)Rx (Receiver)10110010replayTOTAL DELIVERY TIME · REAL PROPORTIONS30.01 msprop­a­ga­tion (latency): 30 mstrans­mis­sion: 8 μs

Status:Idle.

Analysis:The prop­a­ga­tion de­lay dom­i­nates here, la­tency dwarfs how long send­ing ac­tu­ally takes.

band­width1 Mbps · early broad­band

dis­tance6,000 km · transat­lantic ca­ble

Bandwidth sets only how wide each pulse is; dis­tance sets only how long the wave takes to slide down the wire. The bar shows the two de­lays in real pro­por­tion, the re­play above is slowed and not to scale.

A Dedicated Circuit for Every Conversation

The tele­graph car­ried text be­tween of­fices. The tele­phone, from 1876 on­ward, car­ried live voice into homes, and to do it, the net­work worked on a com­pletely dif­fer­ent prin­ci­ple called cir­cuit switch­ing. When you placed a call, the sys­tem as­sem­bled a ded­i­cated elec­tri­cal path be­tween your tele­phone and the re­ceiver’s, orig­i­nally by hu­man op­er­a­tors plug­ging patch cords into switch­boards, later by electro­mechan­i­cal re­lays do­ing the same thing au­to­mat­i­cally. For the du­ra­tion of the call, that chain of cop­per be­longed ex­clu­sively to your con­ver­sa­tion, end to end.

For speech, this is a rea­son­able de­sign, a phone call is a con­tin­u­ous sig­nal flow­ing nearly the whole time, so the re­served line is ac­tu­ally used. So when com­put­ers first needed to talk over dis­tance in the 1950s and 60s, they did the only thing pos­si­ble, they di­aled each other over phone lines. But be­cause tele­phone lines were built to carry hu­man voices, ana­log sound waves, rather than di­rect dig­i­tal elec­tri­cal pulses; com­put­ers had to use a mo­dem (short for mod­u­la­tor-de­mod­u­la­tor). The mo­dem trans­lated the com­put­er’s dig­i­tal bi­nary bits into ana­log au­dio tones, au­di­ble as the chirps, beeps, and sta­tic of a dial-up hand­shake, that could travel over the voice net­work, and trans­lated those sounds back into dig­i­tal bits at the other end.

The ear­li­est modems, like the 300-baud Bell 103, did this with FSK (Frequency-Shift Keying), the same trick as the telegraph’s on/​off pulses, but with a wire that could only carry a con­tin­u­ous tone, not a clean on/​off volt­age. Every 1 bit plays one steady tone for its whole du­ra­tion, every 0 bit plays a dif­fer­ent, lower tone, and the mo­dem on the other end just lis­tens for which tone is pre­sent and reads back the bit. Watch one byte, the let­ter A’, get mod­u­lated into tone and de­mod­u­lated back into bits:

modem­mod­u­la­tion / de­mod­u­la­tion, one byte

send­ing ′

A

′ as bits

01000001

0

1

0

0

0

0

0

1

higher-fre­quency tone (mark) = 1, lower-fre­quency tone (space) = 0

re­ceived

·

·

·

·

·

·

·

·

Sending bit 0, the mo­dem plays the lower space” tone for this bit’s whole du­ra­tion.

Before any data moved over that re­served line, the two modems first had to agree, over that same voice-grade cir­cuit, on how fast they could talk and how they’d cor­rect the er­rors a noisy cop­per pair was bound to in­tro­duce. Modems since the 1981 Hayes Smartmodem left a speaker wired in so a hu­man could hear the call con­nect, dial tone through ring­ing, and con­firm it had­n’t hit a busy sig­nal or a wrong num­ber be­fore the com­puter took over; the speaker stayed on into the hand­shake and only cut out once the ne­go­ti­a­tion fin­ished, which is why that ne­go­ti­a­tion was au­di­ble too. If you were us­ing the in­ter­net dur­ing the 1990s and early 2000s, do you re­mem­ber this tone?

au­dio record­ing of a dial-up hand­shake

solid: down­stream (ISP → you) · dashed: up­stream (you → ISP), si­mul­ta­ne­ous, full du­plex

A real hand­shake be­tween two dial-up modems, sped through six phases in un­der thirty sec­onds. Press play to hear it, and watch which phase is mak­ing that sound.

What sounds like noise is a pro­to­col run­ning in full: ca­pa­bil­ity lists, line probes, and equal­izer train­ing, all au­di­ble be­cause early modems left the speaker on by de­fault so a hu­man could hear whether the call was pro­gress­ing nor­mally. Recording: Dial up mo­dem noises,” pub­lic do­main, Wikimedia Commons.

That noisy hand­shake re­mained the way most peo­ple reached the in­ter­net well into the early 2000s, un­til broad­band re­tired it. DSL and ca­ble reused the same tele­phone and tele­vi­sion wires, but as al­ways-on dig­i­tal links with no call to place, and fiber to the home dropped the voice net­work’s wires en­tirely.

The deeper mis­match, though, was never the modem’s trans­la­tion, and it was clear decades be­fore broad­band. Computer traf­fic is bursty, a ter­mi­nal sends a key­stroke or a re­quest in mil­lisec­onds, then the line sits silent while a hu­man reads or a proces­sor com­putes. (Even stream­ing a video to­day, which feels con­tin­u­ous, is ac­tu­ally de­liv­ered in short, in­tense bursts of pack­ets that fill a play­back buffer, fol­lowed by si­lence while you watch.) Measured over a ses­sion, a cir­cuit re­served for a com­puter con­ver­sa­tion is idle the vast ma­jor­ity of the time, yet it blocks that ca­pac­ity for every­one else. Worse, the path is fixed at call setup, so one bro­ken link or switch­ing of­fice any­where along it kills the con­nec­tion out­right.

By the early 1960s, three pres­sures were con­verg­ing on this cir­cuit-switched de­sign. Research com­put­ers were mul­ti­ply­ing and needed to share ex­pen­sive long-dis­tance lines ef­fi­ciently. Interactive com­put­ing made the bursti­ness ex­treme. And the United States mil­i­tary, in the mid­dle of the Cold War, wanted a com­mand net­work that could keep func­tion­ing af­ter los­ing large pieces of it­self, which a net­work of fixed paths through cen­tral switch­ing of­fices could never do.

Splitting Messages into Packets

The al­ter­na­tive was worked out in­de­pen­dently by two peo­ple who did not know of each oth­er’s work, Paul Baran at the RAND Corporation, de­sign­ing for sur­viv­abil­ity, and Donald Davies at the UKs National Physical Laboratory, de­sign­ing for line shar­ing, who gave the idea its name, the packet.

Instead of re­serv­ing a path and stream­ing data down it, split every mes­sage into small, self-con­tained units. Each packet car­ries a header, a few bytes of con­trol in­for­ma­tion in­clud­ing the source and des­ti­na­tion ad­dresses, fol­lowed by the pay­load, the chunk of data it­self. Every switch­ing point along the way, a router, re­ceives a packet in full, reads the des­ti­na­tion ad­dress in its header, con­sults its own table of which out­go­ing line leads closer to that des­ti­na­tion, and for­wards the packet down it. This is store-and-for­ward switch­ing, the tele­graph re­lay of­fice re­built in elec­tron­ics, with the pa­per tape re­placed by mem­ory and the op­er­a­tor re­placed by a lookup table.

To scale to bil­lions of ma­chines, routers don’t list in­di­vid­ual ad­dresses. Instead, their ta­bles list net­works, ranges of ad­dresses grouped un­der a sin­gle next hop. The ex­act struc­ture of these ad­dresses, and how they are com­pared against ranges, de­pends on the pro­to­col in the packet header (Baran and Davies each de­signed their own). We will ex­plore the in­ter­net’s ver­sion, IP, in de­tail in Connecting the Networks.

With this de­sign, pack­ets from thou­sands of un­re­lated con­ver­sa­tions in­ter­leave on the same wires, so no line sits idle while any­one has data to send. And be­cause each packet is routed in­de­pen­dently, the net­work flows around dam­age. If a router dies mid-con­ver­sa­tion, sub­se­quent pack­ets sim­ply travel through its neigh­bors. Baran called this a dis­trib­uted net­work, one with no point whose loss can cut it in two.

The sim­u­la­tor be­low is a small packet-switched mesh. Clients on the left ex­change pack­ets with servers on the right. Both are ex­am­ples of a host, the generic name for any ad­dress­able de­vice on a net­work, com­puter, phone, server, what­ever it is. The two roles them­selves mat­ter enough that this ar­ti­cle will keep com­ing back to them, a client ini­ti­ates a con­ver­sa­tion, a server sits at a known, fixed ad­dress and waits to be reached. Each router in be­tween makes only lo­cal de­ci­sions, for­ward­ing each packet to­ward its des­ti­na­tion while steer­ing around links that are al­ready busy. Watch how pack­ets from the same con­ver­sa­tion take dif­fer­ent paths, and how con­ges­tion re­shapes routes in real time. And click a router to kill it, no­tice the heal­ing is not in­stant, for a few sec­onds its neigh­bors keep for­ward­ing into the gap on stale in­for­ma­tion and those pack­ets are lost, un­til news of the fail­ure spreads and routes set­tle around it, a catch­ing-up process called con­ver­gence that we will ex­plore when we look at rout­ing pro­to­cols.

SWITCHING MODE:

Two clients, two servers, a dozen routers be­tween them, every packet routed hop by hop.

best-ef­fort de­liv­ery0 ar­rived0 dropped

A toy mesh of a dozen routers; click any router to de­stroy/​dis­able it. For a few sec­onds its neigh­bors keep for­ward­ing into the gap and those pack­ets are lost, then routes set­tle around the fail­ure, while re­served cir­cuits stay bro­ken. Hover routers to in­spect lo­cal lookup ta­bles.

Notice one more thing in the sim­u­la­tor, oc­ca­sion­ally a packet is sim­ply dropped. When pack­ets ar­rive at a router faster than an out­go­ing line can drain them, the router queues them in mem­ory, and when the queue is full, it dis­cards what it can­not hold. This is not a fail­ure of the de­sign; it is the de­sign. The net­work promises only best-ef­fort de­liv­ery, pack­ets may be lost, du­pli­cated, or ar­rive out of or­der, and the net­work it­self does noth­ing to cor­rect it. Keeping the mid­dle of the net­work this sim­ple, and push­ing all re­spon­si­bil­ity for re­li­a­bil­ity out to the com­put­ers at the edges, is the sin­gle most con­se­quen­tial de­ci­sion in the in­ter­net’s ar­chi­tec­ture, and the key to how it scaled to a global net­work.

The First Packet Network

In 1969, ARPA, the US Advanced Research Projects Agency, funded the first real packet-switch­ing net­work, the ARPANET, to con­nect the re­search com­put­ers it was al­ready pay­ing for at uni­ver­si­ties across the coun­try.

There was an im­me­di­ate, mun­dane ob­sta­cle. The main­frames at each site came from dif­fer­ent man­u­fac­tur­ers, ran in­com­pat­i­ble op­er­at­ing sys­tems, and had no spare ca­pac­ity for the real-time work of switch­ing pack­ets. The en­gi­neer­ing firm Bolt Beranek and Newman (BBN) solved this with a ded­i­cated ma­chine, the Interface Message Processor (IMP), a ruggedi­zed mini­com­puter whose only job was to break mes­sages into pack­ets, route them, and re­assem­ble them at the far end. Each site plugged its main­frame into its lo­cal IMP, and the IMPs talked to each other over leased tele­phone lines. ARPANETs own pro­to­col doc­u­ments drew a sharp line be­tween the two ma­chines at each site, the IMP, dumb switch­ing hard­ware with one job, and the main­frame be­hind it, which they called the Host, the ma­chine that ac­tu­ally hosted the com­pu­ta­tion any­one cared about. That word out­lived the hard­ware, a host to­day is any com­puter, phone, or server send­ing or re­ceiv­ing traf­fic, and a router or switch is what­ever in­her­ited the IMPs old job of be­ing in­fra­struc­ture, not a host it­self. The IMP was the first router, and its pat­tern, a ded­i­cated box that speaks the net­work’s pro­to­col so the com­put­ers be­hind it don’t have to, is sit­ting in your home right now with an­ten­nas on it.

The first trans­mis­sion took place on October 29, 1969, from UCLA to the Stanford Research Institute. A stu­dent pro­gram­mer, Charley Kline, be­gan typ­ing LOGIN to log into the re­mote ma­chine. He typed L, con­firmed by phone that it had ar­rived, typed O, and the re­ceiv­ing sys­tem crashed. The first mes­sage ever car­ried by the in­ter­net’s an­ces­tor was LO. By December the net­work had four nodes; by 1973 it crossed the Atlantic to Norway and London.

The packet-switch­ing net­work built for the ARPANET con­nected dis­tant sites over leased point-to-point lines, form­ing a WAN (Wide Area Network). The same core idea of packet trans­mis­sion also solved a smaller, far more lo­cal prob­lem, how do you con­nect the dozens of ma­chines in one of­fice, a LAN (Local Area Network), with­out run­ning a ded­i­cated wire be­tween every pair?

In 1973, Robert Metcalfe at Xerox PARC de­signed Ethernet. Its col­li­sion han­dling drew di­rectly on ALOHAnet, an ear­lier ra­dio net­work link­ing the Hawaiian is­lands, whose core idea was re­fresh­ingly blunt, a sta­tion just trans­mits when­ever it has some­thing to send, and lis­tens for whether it col­lided with some­one else’s trans­mis­sion, rather than ask­ing per­mis­sion first. Metcalfe’s de­sign con­nected every com­puter in a build­ing to one shared coax­ial ca­ble, which he called the ether.” Coaxial means two con­duc­tors on the same cen­tral axis, a sin­gle cop­per core car­ry­ing the sig­nal, wrapped in an in­su­lat­ing layer, then a cylin­dri­cal braided or foil shield, then a plas­tic jacket. The shield dou­bles as the re­turn path and blocks out­side in­ter­fer­ence from reach­ing the core, which is what let one long ca­ble carry a clean sig­nal past every desk in the build­ing. Any ma­chine could trans­mit onto the ca­ble, and every ma­chine re­ceived every­thing, keep­ing only the pack­ets ad­dressed to it.

A shared medium has an ob­vi­ous flaw, if two ma­chines trans­mit at once, they gar­ble each other, a col­li­sion. Ethernet han­dled it with a few purely lo­cal rules, lis­ten be­fore trans­mit­ting, and wait if the ca­ble is busy; keep lis­ten­ing while trans­mit­ting, and stop the in­stant you hear a col­li­sion; then retry af­ter a ran­dom de­lay. By dou­bling the range of that ran­dom de­lay with each re­peated col­li­sion, a strat­egy called ex­po­nen­tial back­off, col­lid­ing ma­chines spread them­selves apart in­stead of jam­ming the line for­ever. This scheme is called CSMA/CD (Carrier Sense Multiple Access with Collision Detection), carrier sense” is lis­ten­ing be­fore you talk, and collision de­tec­tion” is stop­ping the in­stant you hear noise. As with packet rout­ing, or­derly shar­ing emerges from iden­ti­cal lo­cal rules, with no co­or­di­na­tor re­quired.

The shared ca­ble it­self did­n’t sur­vive, of­fices moved to switches, and the coax went with it, re­placed by twisted-pair cop­per ter­mi­nat­ing in an RJ-45 con­nec­tor, a clear plas­tic clip slightly wider than a phone jack, one ded­i­cated run from each de­vice back to the switch in­stead of one wire threaded past every desk.

A switch solves the same prob­lem as a router, de­liv­er­ing data only to its des­ti­na­tion, but op­er­ates on lo­cal hard­ware ad­dresses rather than global net­work ad­dresses, and by a dif­fer­ent mech­a­nism en­tirely. A router reads a pack­et’s des­ti­na­tion ad­dress and picks a line from a table some­one con­fig­ured. A switch reads a frame’s (Ethernet’s own name for its unit of data, a pack­et’s coun­ter­part one layer down) MAC (Media Access Control) ad­dress, the iden­ti­fier burned into a de­vice’s net­work hard­ware, and picks a port from a table it built en­tirely by it­self, by watch­ing traf­fic go by.

A MAC ad­dress is writ­ten as six pairs of hexa­dec­i­mal dig­its sep­a­rated by colons, for ex­am­ple, 00:1A:2B:3C:4D:5E.

Hexadecimal just means base 16 in­stead of base 10, six­teen dig­its per place, 0 through 9 and then A through F stand­ing in for ten through fif­teen. It was cho­sen for a rea­son more spe­cific than tra­di­tion, 16 is a power of 2, so one hex digit al­ways en­codes ex­actly four bits, no re­main­der, and two hex dig­its al­ways en­code ex­actly one byte, 1A is one byte with the value 26. Decimal has no such align­ment, three dec­i­mal dig­its some­times hold a byte and some­times don’t, which is why en­gi­neers reach for hex any­where they’re re­ally look­ing at raw bits but want some­thing more com­pact than writ­ing them out as 1s and 0s. That’s the same rea­son it resur­faces later in this ar­ti­cle, in packet header bytes and pro­to­col num­bers, it’s bi­nary in fewer char­ac­ters, not a dif­fer­ent kind of num­ber.

By di­vid­ing this 48-bit ad­dress into a man­u­fac­turer pre­fix (the first three pairs, known as an OUI or Organizationally Unique Identifier) and a se­r­ial num­ber (the last three pairs), they en­sured every net­work in­ter­face card on Earth gets a glob­ally unique ID. Every con­nected de­vice, in­clud­ing your smart­phone, car­ries these iden­ti­fiers for both its Wi-Fi and Bluetooth chips.

Manufacturer (OUI)

00:1A:2B

Identifies the hard­ware ven­dor (e.g., Apple, Intel, Cisco)

Serial Number (NIC)

3C:4D:5E

Unique iden­ti­fier as­signed to this spe­cific chip

Ghost Font: The Anti-AI Font Only Humans Can Read

www.mixfont.com

Home

Ghost Font

An anti-AI font that can be read by hu­mans but not lead­ing AI mod­els. Type your text be­low, then down­load and share the video clip con­tain­ing your mes­sage.

Write your mes­sage:10/​36

Speed120px/s

What is Ghost Font?

Ghost Font is an anti-AI font that writes a mes­sage us­ing mo­tion. Using a com­bi­na­tion of mo­tion, video, noise, and de­coys, it’s a unique way to share a mes­sage with other real hu­mans. I sup­pose tech­ni­cally, it’s not a font in the tra­di­tional sense of a TTF font file. But, Ghost Font is an ex­per­i­ment of a way to graph­i­cally com­mu­ni­cate in writ­ing in a for­mat that AI can­not eas­ily un­der­stand. While it’s not as leg­i­ble as reg­u­lar text, the let­ters are still im­me­di­ately read­able to a hu­man eye, but even lead­ing AI mod­els can’t de­ci­pher it eas­ily.

Videos gen­er­ated with Ghost Font were then passed to lead­ing AI mod­els like Claude Fable and GPT Sol 5.6 Ultra. Even these re­cent agents, with the abil­ity to code, strug­gled to de­code the mov­ing mes­sage un­til prompted with the ex­act tech­nique to look for.

The play­ground above is just a pro­to­type of this con­cept. Type a few words and the let­ters ap­pear but only be­cause the mo­tion of the dots is vis­i­ble to a hu­man eye.

When the video is paused, the sta­tic dots blend to­gether, and it be­comes im­pos­si­ble to tell just from look­ing at a sin­gle frame what mes­sage is em­bed­ded in the im­age. That means that screen­shot­ting the page won’t re­veal the mes­sage.

This ex­per­i­ment works lo­cally—type the mes­sage and pre­view it live, or down­load the video to share and test it out your­self. The data is not shared or sent to any server.

About this pro­ject

In 2013, de­signer Sang Mun re­leased a font called ZXX. It was a type­face with four fonts de­signed to be read­able by hu­mans but not by op­ti­cal char­ac­ter recog­ni­tion (OCR) soft­ware. The let­ters were cam­ou­flaged with noise, crossed out, and buried un­der false marks. At the time, this font was deemed surveillance-proof”—but fast for­ward to to­day, and mod­ern AI agents can eas­ily read text ren­dered in ZXX.

While this might have de­feated OCR soft­ware in 2013, mod­ern AI mod­els can read the text in ZXX pretty eas­ily. I copied this im­age into ChatGPT 5.5 on Instant mode, and it was still able to get the words in­clud­ing some small de­tails as well in a sin­gle prompt:

However, the same process with Ghost Font won’t work quite as eas­ily. A sin­gle screen­shot of Ghost Font will yield just a com­pletely sta­tic im­age with no read­able text. That’s be­cause every let­ter in Ghost Font is made up of dots that look ex­actly like the back­ground, so any sin­gle im­age from the video will not re­veal any­thing about the mes­sage:

After a 19-minute analy­sis, ChatGPT 5.5 Pro hal­lu­ci­nated a mes­sage that does­n’t ex­ist.

However, sim­ply hid­ing a mes­sage in a video is­n’t a per­fect so­lu­tion. While an on­line model en­vi­ron­ment might not be able to get it from in­di­vid­ual frames, a ded­i­cated agent that has a lo­cal code ex­e­cu­tion en­vi­ron­ment can still an­a­lyze the mo­tion of the dots and de­code the mes­sage. Ghost Font solves this in an­other lay­ered way: a de­coy mes­sage is in­cluded in every video gen­er­a­tion.

The de­coy mes­sage serves as a fi­nal trick for a de­ter­mined agent. When look­ing for a hid­den mes­sage, it might first find the de­coy mes­sage and think that that is the real em­bed­ded mes­sage in the video. That’s how Ghost Font is able to hide a mes­sage even from the strongest think­ing mod­els like Fable and GPT Sol 5.6 Ultra.

Ultimately, the way to truly hide a mes­sage is to use en­cryp­tion, or some sort of key. No AI will be able to read a mes­sage that re­quires a spe­cific pass­word to un­lock that only hu­mans know. However, this pro­ject ex­plores whether it’s pos­si­ble to cre­ate a share­able file con­tain­ing a vi­sual mes­sage that can’t be eas­ily read by AI mod­els.

We cre­ated this ex­per­i­ment as a way to ex­plore the lim­its of AI per­cep­tion while also pre­serv­ing some­thing hu­man. As AI takes over font gen­er­a­tion, our hope is that hu­mans will con­tinue to have a unique cre­ative voice.

What’s next?

There are cer­tain im­pli­ca­tions for Ghost Font that I think would be in­ter­est­ing to con­tinue to ex­plore. For ex­am­ple, it would be in­ter­est­ing to in­cor­po­rate Ghost Font into CAPTCHA sys­tems, as most sys­tems are eas­ily solved by AI to­day. Using mo­tion in a video would be a way to make it much more dif­fi­cult for an au­to­mated bot to de­ci­pher but still rel­a­tively easy for a hu­man to read.

Ghost Font might also be an in­ter­est­ing way to bench­mark AI progress when it comes to vi­sual per­cep­tion. Right now, mul­ti­modal mod­els are im­age-based, and even when passed a video, they usu­ally split the video into frames and an­a­lyze in­di­vid­ual frames. In the near fu­ture, I as­sume there will be a video-na­tive model that will be able to read the text di­rectly.

A fi­nal les­son is that AI is cer­tainly get­ting re­ally good. While Ghost Font is hard for AI to read, it’s also pretty hard for hu­mans to read! The gap con­tin­ues to close. It will be in­ter­est­ing to see what the fu­ture holds when it comes to AI per­cep­tion and mul­ti­modal mod­els.

As a next step, I plan to re­lease the code for the video gen­er­a­tion as an open-source pro­ject—stay tuned for that! I also hope to ex­pand the size and han­dle longer text strings. I hope you en­joyed this ex­per­i­ment and I would love to hear your thoughts. You can find me on X at @ericlu.

- Eric

Prefer STRICT tables in SQLite

evanhahn.com

In short: I pre­fer strict ta­bles in SQLite be­cause they avoid some datatype prob­lems, such as putting text in num­ber columns.

SQLite has a fea­ture that I think is un­der­rated: strict ta­bles. Strict ta­bles help en­force rigid typ­ing, pre­vent­ing mis­takes like putting text into in­te­ger columns. I like them, and wrote this post to pro­mote their use!

To make a strict table, add STRICT to the end of its de­f­i­n­i­tion. Like this:

-CREATE TABLE peo­ple (name TEXT); +CREATE TABLE peo­ple (name TEXT) STRICT;

That’s it! But what does it do?

Advantages of strict ta­bles

Broadly, strict ta­bles help en­force rigid types, like other SQL en­gines do.

Prevents type mis­matches on in­sert/​up­date

Most sig­nif­i­cantly, strict ta­bles keep you from in­sert­ing the wrong type into a col­umn. For ex­am­ple, SQLite nor­mally lets you put text into an INTEGER col­umn, but not with strict ta­bles.

– Non-strict ta­bles let you put any­thing any­where. CREATE TABLE peo­ple_non­strict (age INTEGER); INSERT INTO peo­ple_non­strict (age) VALUES (‘garbage’); — => works fine

– Strict ta­bles don’t al­low that, which I pre­fer. CREATE TABLE peo­ple_strict (age INTEGER) STRICT; INSERT INTO peo­ple_strict (age) VALUES (‘garbage’); — => er­ror: can­not store TEXT value in INTEGER col­umn

Personally, I think it’s a mis­take to try to put text in an in­te­ger col­umn, or vice-versa. I don’t want SQLite to let me make this er­ror!

The same val­i­da­tion hap­pens for UPDATEs, too.

Notably, if a value can be loss­lessly con­verted, it will still be ac­cepted. For ex­am­ple, the string 123’ can be per­fectly con­verted to an in­te­ger, so it’s al­lowed. These two lines are equiv­a­lent, even for a strict table:

INSERT INTO peo­ple_strict (age) VALUES (‘123’); INSERT INTO peo­ple_strict (age) VALUES (123);

Prevents bo­gus col­umn types on table cre­ation

By de­fault, you can cre­ate columns with bo­gus types. For ex­am­ple, all of these work even though they aren’t valid SQLite datatypes:

– SQLite does­n’t sup­port these types, but this is all ac­cepted. CREATE TABLE tbl (name GARBAGE); CREATE TABLE tbl (name DATETIME); CREATE TABLE tbl (name JSON); CREATE TABLE tbl (name UUID); CREATE TABLE tbl (name BLOBB);

I think these aren’t what the de­vel­oper in­tended. Some of these are ty­pos, some of them are mis­un­der­stand­ings of which datatypes SQLite sup­ports, and some are egre­gious mis­takes.

Appending STRICT to any of these state­ments makes them er­ror. In my opin­ion, that’s the cor­rect be­hav­ior!

– All of these give er­rors, which I pre­fer. CREATE TABLE tbl (name GARBAGE) STRICT; CREATE TABLE tbl (name DATETIME) STRICT; CREATE TABLE tbl (name JSON) STRICT; CREATE TABLE tbl (name UUID) STRICT; CREATE TABLE tbl (name BLOBB) STRICT;

Only INT, INTEGER, REAL, TEXT, BLOB, and ANY are al­lowed.

Strict ta­bles also re­quire a col­umn type, so you can’t do CREATE TABLE tbl (name).

Still al­lows flex­i­bil­ity with ANY

If you still need a col­umn to be flex­i­ble, you can use the ANY datatype. As the name sug­gests, it al­lows any­thing—even in a strict table.

CREATE TABLE tbl (value ANY) STRICT;

– All of these are valid be­cause the col­umn is ANY: INSERT INTO tbl (value) VALUES (123); INSERT INTO tbl (value) VALUES (‘text’); INSERT INTO tbl (value) VALUES (12.34); INSERT INTO tbl (value) VALUES (X′8647′);

I haven’t found a use for this, but maybe you will!

Disadvantages of strict ta­bles

I pre­fer strict ta­bles but I must share a few cons. Not every­thing is bet­ter!

Can’t strict-ify an ex­ist­ing table

I think it’s best to use strict­ness from the start, but that’s not al­ways pos­si­ble.

Unfortunately, I don’t think there’s a way to ALTER a table to make it strict. I think you have to copy the data out of the non-strict table into the strict one. Something like this:

– 1. Create a new strict table with the same schema CREATE TABLE new_peo­ple (name TEXT) STRICT;

– 2. Copy data (risky if types are wrong!) INSERT INTO new_peo­ple SELECT * FROM peo­ple;

– 3. Replace the old table DROP TABLE peo­ple; ALTER TABLE new_peo­ple RENAME TO peo­ple;

Note that this could be tricky if the non-strict table has in­valid data! For ex­am­ple, if the old data ac­ci­den­tally con­tains text in an in­te­ger col­umn, you’ll get er­rors when do­ing the mi­gra­tion. You’ll prob­a­bly need to clean the data or cast it.

You could make a rule for your code­base that all new ta­bles are strict. That might be use­ful—at least some of your ta­bles are valid! But it might also mean you have in­con­sis­tent val­i­da­tion across your ta­bles, which might be more sur­pris­ing than hav­ing weak val­i­da­tion on all ta­bles. It’s up to you to de­cide whether this is a good fit for you.

The SQLite de­vel­op­ers dis­agree with me

SQLite has a whole page called The Advantages Of Flexible Typing”, where they ar­gue that SQLite’s flex­i­ble be­hav­ior is good, ac­tu­ally.

I hes­i­tate to wade into the con­tro­versy of sta­tic-ver­sus-dy­namic, but I dis­agree in most cases. I’ve per­son­ally en­coun­tered many bugs where an un­ex­pected data type caused sub­tle headaches. I’d much rather these mis­takes ex­plode loudly. But it’s worth not­ing that SQLite’s de­vel­op­ers seem not to share my pref­er­ence for strict ta­bles!

They point out a few good uses for flex­i­ble ta­bles, such as a pure key-value store” or a place to store mis­cel­la­neous at­trib­utes” of dif­fer­ent types. They also men­tion that you might want to keep the in­valid data in some cases, like if you’re di­rectly im­port­ing a messy CSV and don’t want to lose any data. I still pre­fer strict ta­bles, but ac­knowl­edge there are some rea­son­able cases for non-strict ones.

(There’s also at least one com­ment in the SQLite source that calls non-strict ta­bles legacy”, but I trust that less than the of­fi­cial doc­u­men­ta­tion.)

Only in SQLite 3.37.0+

SQLite in­tro­duced strict ta­bles in ver­sion 3.37.0, re­leased November 2021. If you’re on an older ver­sion of SQLite, you can’t use strict ta­bles.

It’s worth not­ing that old ver­sions of SQLite can’t read data­bases with strict ta­bles. For ex­am­ple, if you cre­ate a strict table in the newest ver­sion of SQLite and then try to read that data­base in SQLite 3.36.0 (before strict ta­bles were added), you’ll get an er­ror—even if the strict table is al­ready in the data­base.

Performance maybe?

Strict ta­bles are the­o­ret­i­cally slower be­cause they have to do a lit­tle ex­tra work. For ex­am­ple, they check datatypes when do­ing an in­sert or up­date.

But in prac­tice, I don’t think this is an is­sue. I wrote a hacky script that in­serted mil­lions of rows into a table with 100 columns, and there was no ob­vi­ous dif­fer­ence on mul­ti­ple ma­chines I tried. The file size on disk was also the same. I did­n’t test this thor­oughly, so maybe there’s some­thing I missed, but I don’t think strict ta­bles pre­sent a per­for­mance prob­lem.

In fact, one might ex­pect bet­ter per­for­mance be­cause you won’t be ac­ci­den­tally mis­match­ing SQLite’s col­umn affini­ties. But again, I haven’t tested this.

Conclusion: I like strict ta­bles!

Personally, I think the pros of strict ta­bles out­weigh the cons.

I gen­er­ally pre­fer when types are rigidly en­forced. It squashes a class of mis­takes, and help en­force good data in­tegrity. They’re not a panacea, but they’re usu­ally easy to add and go a long way.

If there’s a SQLite fea­ture you think is un­der­rated, please tell me.

An iroh powered smart fan

www.iroh.computer

If you live in Europe the north­ern hemi­sphere, you are prob­a­bly suf­fer­ing through a heat wave right now. Let’s do some­thing to bring back some chill, us­ing iroh.

The pre­vi­ous ESP32 ex­am­ples demon­strated echo pro­to­cols. But typ­i­cally ESP32s are used for more than just echo­ing data; you use an ESP32 as a cheap means to read sen­sors and drive ac­tu­a­tors.

So we are go­ing to write a very sim­ple end-to-end ex­am­ple us­ing an ESP32 to mea­sure tem­per­a­ture and con­trol a fan. Unlike most IoT de­vices, there won’t be any cloud com­po­nent. Just a tiny web­site that you can use from any­where in the world us­ing any browser that sup­ports WebAssembly.

As the base, we are go­ing to use an ESP32-WROVER de­vkit with 4 MiB of PSRAM, so we have all of iro­h’s net­work­ing ca­pa­bil­i­ties avail­able, in­clud­ing a re­lay con­nec­tion, and re­mote con­trol it from any­where in the world. You can also use a M5StickC-Plus2, but you will have to adapt the GPIO pins.

If you have an­other de­vkit such as an ESP32-S3 with PSRAM, you should be able to run the ex­am­ples with small con­fig tweaks.

If all you have is an ESP32 with­out PSRAM, you can still use iroh. But you need to dis­able the re­lay con­nec­tion and tweak QUIC buffers so we don’t run out of mem­ory. Use an ap­pro­pri­ate ex­am­ple from iroh-es­p32-ex­am­ples as base.

As the first step, we are go­ing to copy over an echo ex­am­ple from iroh-es­p32-ex­am­ples. We will use server-es­p32-psram for the ESP32 bi­nary.

For the client side we just use client, it runs on a desk­top PC and is as vanilla as it gets.

This is go­ing to be a smart fan ex­am­ple, so we just re­name server-es­p32-psram to server-smart-fan, and client to smart-fan-cli.

Note that we need dif­fer­ent tool­chains and want to keep the op­tion to use a patch of iroh for the ESP32 vari­ant, so the two di­rec­to­ries are com­pletely sep­a­rate Rust pro­jects. We do not use a work­space.

Initial state

Let’s try it out once be­fore we do mod­i­fi­ca­tions. cargo run on the server pro­ject will search for an ESP32 con­nected via USB and flash it. So we just con­nect our ESP32 with a USB-C ca­ble.

The ini­tial re­lease build will take some time, since we are com­pil­ing not just iroh, but also the op­er­at­ing sys­tem to the xtensa ar­chi­tec­ture. Subsequent builds will be faster, since the com­pi­la­tion re­sults are cached in the .embuild di­rec­tory.

Flashing it­self will never be re­ally fast, be­cause the data rate to the chip is very lim­ited. We can make it go a bit faster by set­ting the ESPFLASH_BAUD en­vi­ron­ment vari­able. My chip sup­ports 230400 baud, but YMMV. If it does­n’t work just run with­out the en­vi­ron­ment vari­able set, then it will use safe de­faults.

We need to tell the ESP32 how to con­nect to WLAN. In the ex­am­ple we just use an­other en­vi­ron­ment vari­able WIFI_CONFIG=SSID:PASSWORD. Set this to your lo­cal WLAN.

You can do a sin­gle ex­port WIFI_CONFIG=SSID:PASSWORD so you don’t have to pass it every sin­gle time.

As you can see from the flash out­put, we are pretty close to the limit of the flash size.

You might think that every sin­gle added line of code will get you over the limit, but that is not the case. Additional pure Rust de­pen­den­cies such as irpc add very lit­tle size.

What we should have now is a sim­ple echo server run­ning on the ESP32.

Endpoint Id

First of all, how do we as­sign the end­point id? We want the abil­ity to as­sign an end­point id, but even if we don’t do so we want the end­point id to be sta­ble af­ter re­boots. So the ESP32 should not gen­er­ate a ran­dom one on each startup.

Instead we gen­er­ate and store the se­cret key in non volatile mem­ory on first startup and reuse it on sub­se­quent star­tups. Non volatile mem­ory is not over­writ­ten by flash­ing, so we will get the same end­point id for the same de­vice un­less we ex­plic­itly delete non volatile mem­ory.

Startup

On startup the de­vice will try to con­nect to WiFi us­ing the given cre­den­tials. If that does­n’t work it will hang. This hap­pens be­fore any iroh end­point setup.

For a real prod­uct you would want two al­ter­na­tive WiFi con­figs and some re­cov­ery op­tion, but we are go­ing to skip this for the ex­am­ple.

Once the end­point on the ESP32 starts up, we get very fa­mil­iar out­put:

The de­vice has been as­signed a lo­cal IP ad­dress 192.168.0.186:51831 by the DHCP of the router. It prints both a long and short ticket, but for now is only reach­able lo­cally us­ing the long ticket that con­tains the IP ad­dress.

Next it tries fig­ur­ing out its lo­ca­tion in the world us­ing QAD.

Assuming you are con­nected to the in­ter­net, af­ter a short time it will fig­ure out which re­lay is clos­est and set that as its home re­lay.

At this point it is reach­able from any­where in the world us­ing the short ticket that con­tains just the end­point id.

Connecting

So now let’s try it out us­ing the client bi­nary.

Locally you can use the long ticket and by­pass the re­lay, but as soon as the end­point has pub­lished its home re­lay it should be reach­able glob­ally.

The client has an op­tion to dis­able re­lay. If you do that you will only be able to use the long ticket.

It also has op­tions for mDNS, but we are not us­ing mDNS for this pro­ject.

Shutdown

You might think that stop­ping the cargo run –release will stop the bi­nary. But this is not the case. It just stops the con­nec­tion to the de­vice. The end­point will hap­pily con­tinue to run as long as it has power.

You can even dis­con­nect it and plug it into a sep­a­rate USB-C power sup­ply, and it will boot up again with the same end­point id. This is the whole point. The ESP32 is a fully self-con­tained em­bed­ded com­puter. It just needs power.

If you re­ally want to shut it down, un­plug it or delete the flash us­ing espflash erase-flash.

Now that we have con­firmed that the ex­am­ple works, we can start mak­ing it ac­tu­ally do some­thing.

Since we want to build a smart fan, the first thing we need is a tem­per­a­ture sen­sor. We are go­ing to use a DHT22 tem­per­a­ture and hu­mid­ity sen­sor.

A DHT22 sen­sor. Photo by L293D, CC BY-SA 4.0, via Wikimedia Commons.

Wiring up the sen­sor is very sim­ple. It has three wires, two for +3.3V (do not use +5V!) and GND and one for data.

If you use the ESP32 dev kit with the ex­ten­sion board and a bread­board, it will power the bread­board rails with +3.3V and +5V from the USB port. You can pull only ~100 mA with­out an ex­ter­nal power sup­ply, but it is enough for the DHT22, which only takes 1.5 mA while mea­sur­ing and even less when idle.

We need to con­nect the mid­dle wire to one of the many GPIO ports of the ESP32. We will choose GPIO 26, but you can use al­most all GPIOs for this. Some GPIOs have spe­cial func­tions dur­ing boot, but GPIO26 does not.

Just to test the sen­sor, we will print out the sen­sor read­ings us­ing trac­ing.

Running it

Troubleshooting:

Make sure you have +3.3V and GND wired up the right way. If not you will no­tice the sen­sor get­ting hot and have a few sec­onds to re­act be­fore smoke comes out.

The DHT22 should work with the sig­nal wire di­rectly con­nected to GPIO 26. But if you get fre­quent time­outs you can try adding a pull up re­sis­tor of 3.3 kΩ that con­nects the GPIO to the +3.3V rail. Do not con­nect to the +5V rail!

Don’t be afraid to get things wrong. Both the DHT22 and the ESP32 are pretty ro­bust and for­giv­ing for wiring mis­takes if you cor­rect them quickly!

Commit that adds sen­sor read­ing

At this point we have an iroh end­point that sup­ports our echo pro­to­col, and lo­cal sen­sor read­ings. Obviously we want to read the sen­sor re­motely as well.

To do that we are go­ing to use irpc. If all we wanted to do is to read a sin­gle sen­sor, this would be overkill. But us­ing irpc will make it eas­ier to ex­tend the pro­to­col in the fu­ture.

Protocol crate

We will de­fine the pro­to­col in a sep­a­rate crate smart-fan-proto, since it will be used by both the client and the ESP32 it­self.

The first rpc call will be just read­ing the cur­rent sen­sor val­ues. For now this is just a tem­per­a­ture and hu­mid­ity, but in the fu­ture there might be more. So we are go­ing to use a sen­sor state struct.

Here is the com­plete pro­to­col de­f­i­n­i­tion:

Server side

On the server side we have a struct that car­ries the cur­rent sen­sor state in a mu­tex:

Client side

We will add more in the fu­ture, but for now the client side just does a sin­gle read­ing.

Can we still have some­thing sim­ple?

Maybe we still want a sim­ple way to check that the end­point is up. We could of course add a dummy end­point to the irpc pro­to­col, but we can also just keep sup­port­ing the echo pro­to­col.

When us­ing the router, you can com­bine as many pro­to­cols as you want!

Commit that adds sep­a­rate pro­to­col crate

The CLI tool is nice for de­bug­ging, but what we re­ally want is a GUI to show the tem­per­a­ture and, even­tu­ally, to con­trol the fan. We could write a na­tive GUI us­ing dioxus that works on all ma­jor plat­forms. But who wants to in­stall an app for this? So let’s do a WASM GUI that runs in the browser.

I am not a javascript de­vel­oper, so the WASM GUI is vibe coded. I just briefly checked it.

This first ver­sion is just a re­mote ther­mome­ter: paste a ticket from your de­vice to see its tem­per­a­ture and hu­mid­ity live over the re­lay.

To run the GUI, go into smart-fan-wasm and run npm run build; npm run serve, and then open the GUI on http://​lo­cal­host:8080. But you don’t have to! The GUI in this blog post is live, you can just paste your ticket and try it out.

Commit that adds WebAssembly GUI

At this point we have a re­mote ac­ces­si­ble tem­per­a­ture and hu­mid­ity me­ter. But we want a smart fan. So let’s add an out­put. We are go­ing to use a 5V desk­top com­puter fan like, for ex­am­ple, the Noctua NF-A14 – 5V. This con­nects to the +5V and GND rail and has a sin­gle PWM con­trol wire to switch or throt­tle the fan.

If you don’t have such a fan, you can also just wire up a LED with a ~330 Ω re­sis­tor, or wire up a re­lay to con­trol a house­hold fan. It’s more fun with a real fan though!

The ac­tu­a­tor will be con­trolled us­ing a sim­ple logic: if the tem­per­a­ture is above some value, switch on the fan. We add a tiny bit of stick­i­ness so the fan does­n’t con­stantly tog­gle on and off.

Then to set the ac­tual pin:

So far, so good.

Commit that adds out­put switch­ing

At this point the ma­jor com­po­nents are in place, and I stopped keep­ing the com­mit his­tory clean.

During de­vel­op­ment, you could just change the pro­to­col at will and make sure both client and server are up to date.

For a new pro­duc­tion de­ploy­ment, we would just change the ALPN to make it clear that this is a new pro­to­col.

But what if we wanted the old re­mote ther­mome­ter GUI to still work? In that case we have to be care­ful to only add new meth­ods to the RPC pro­to­col, and leave the cur­rent meth­ods in the same place in the enum with the same struc­tures. Irpc is us­ing post­card, and un­like json or pro­to­buf, post­card makes zero at­tempts to be self-de­scrib­ing. Trying to read a dif­fer­ent struct than what was writ­ten will just fail or pro­duce weird re­sults.

We can still evolve the pro­to­col with­out chang­ing ALPNs by adding new RPC meth­ods at the end of the pro­to­col enum. Then we can keep the ALPN, and old GUI ver­sions will con­tinue to work.

We have a much more prin­ci­pled ap­proach for schema evo­lu­tion, irpc-schema, but that will be the sub­ject of an­other blog post.

So here is our new com­pat­i­ble schema enum:

The read-only GUI uses only GetStatus: it shows tem­per­a­ture, hu­mid­ity, and whether the fan is run­ning — but can’t change any­thing, so it’s safe to share pub­licly. It’s the same page as the ther­mome­ter above, just talk­ing to the newer pro­to­col:

We now want the abil­ity to set the thresh­old above which the fan starts to work. But we still want the abil­ity to have the smart fan GUI hosted on a pub­lic web­site. We could rely on the end­point id be­ing se­cret, but that is not a good idea. If we use dis­cov­ery, data is pub­lished for the end­point id on dns.iroh.link. Also, we might want to re­tain the abil­ity to share a read-only view of the fan state.

So let’s ex­tend the RPC pro­to­col with a call to set the thresh­old, but add some au­then­ti­ca­tion. We will use a sim­ple se­cret that is baked into the code, then use it only in the new RPC call.

Added RPC meth­ods

GUI ad­di­tions

For the new GUI, we just add a slider that shows the cur­rent thresh­old and can be used to change it.

Here it is, run­ning live in the browser as WebAssembly. Paste a ticket from your de­vice to con­nect, and op­tion­ally en­ter its FAN_API_SECRET to un­lock the thresh­old slider:

So far we got a bread­board with an ESP32, a LED or fan, and a DHT22 sen­sor wired up. In many cases this is where it ends for such pro­jects - you con­firm that it works, then have it sit on the desk for a few days, then dis­as­sem­ble it be­cause you need the parts for the next fun pro­ject or be­cause you want to clean up.

In this case I wanted to as­sem­ble the parts into an us­able wid­get. So I de­signed an en­clo­sure to hold the fan and the bread­board.

The en­clo­sure in Bambu Studio. Download the STL.

I did­n’t bother with us­ing a sol­dered pro­to­type board or even a cus­tom PCB. Here is the end re­sult:

The fin­ished fan: a Noctua NF-A14 in the printed hous­ing, with an open­ing for the DHT22.

The elec­tron­ics — ESP32 on a bread­board, DHT22 sen­sor and fan con­nected — glued into the base.

I used an ESP32-S3 be­cause I had a spare ly­ing around, and also changed the GPIO pins to sim­plify wiring. With the ESP32-S3 you only get one side of the bread­board ac­ces­si­ble.

I put a QR code on the out­side that opens the read­only web UI, and a QR code in­side that opens the web UI with the se­cret for con­trol­ling the tem­per­a­ture thresh­old.

The QR codes are blurred, but if you man­age to re­cover the orig­i­nal im­age with some fancy de­con­vo­lu­tion al­go­rithm, you can con­trol my fan un­til I change the end­point id.

How we scale PgBouncer in ClickHouse Managed Postgres

clickhouse.com

PgBouncer is sin­gle-threaded. A sin­gle process uses one CPU core, no mat­ter how many the ma­chine has. On a 16-vCPU box that means one core does all the con­nec­tion pool­ing while the other fif­teen sit idle, and the pooler starts cap­ping through­put long be­fore Postgres runs out of room.

In ClickHouse Managed Postgres we run a fleet of PgBouncer processes, sized pro­por­tional to the avail­able cores.

Every process in the fleet binds the same port with so_reuse­port en­abled. The ker­nel load-bal­ances in­com­ing con­nec­tions across the processes, so clients still con­nect to a sin­gle end­point and never know there is more than one PgBouncer be­hind it. This is the mech­a­nism PgBouncer’s own docs point to for us­ing more than one core: it is sin­gle-threaded per process, and so_reuse­port is how you put every core to work.

A Postgres can­cel re­quest ar­rives on a brand-new con­nec­tion car­ry­ing a can­cel key, sep­a­rate from the con­nec­tion run­ning the query. With so_reuse­port, the ker­nel is free to hand that new con­nec­tion to a dif­fer­ent process than the one hold­ing the ses­sion. The can­cel lands on a process that has never heard of the query, and noth­ing hap­pens.

Peering fixes this. The processes are aware of one an­other, so a can­cel that lands on the wrong process is for­warded to the one that ac­tu­ally owns the ses­sion. Cancellation works across the whole fleet, even though any given re­quest can ar­rive any­where.

Pooling runs in trans­ac­tion mode, so a server con­nec­tion is re­turned to the pool the mo­ment a trans­ac­tion com­mits. And the con­nec­tion bud­get is split across the fleet: max_­clien­t_­conn and max_d­b_­con­nec­tions are di­vided by the num­ber of processes, so the fleet as a whole never over­sub­scribes Postgres.

We ran both con­fig­u­ra­tions on iden­ti­cal AWS EC2 in­stances: a 16-vCPU c7i.4xlarge for the pooler, a sep­a­rate box for Postgres, and a third dri­ving load with pg­bench in se­lect-only, trans­ac­tion-pooled mode. One pooler box ran a sin­gle PgBouncer process; the other ran a fleet of 16. Same in­stance type, same Postgres, same work­load. The only vari­able is one process ver­sus six­teen.

We ramped client con­nec­tions from 8 to 256 and mea­sured through­put and how much of the 16-core box each pooler ac­tu­ally used.

The sin­gle process peaks around 87k trans­ac­tions/​sec and then gets worse un­der more load, slid­ing to 77k at 256 clients as every­thing con­tends for one core. The fleet keeps climb­ing to roughly 336k trans­ac­tions/​sec, about 4x, be­cause it has more cores to climb into.

The sin­gle process never gets past about one core of work: un­der load, pid­stat shows the PgBouncer process pinned at ~97% CPU, a full core, while the 16-vCPU box as a whole stays un­der 10% uti­lized. The fleet spreads across the ma­chine, reach­ing roughly 8 cores busy, and it still had head­room when Postgres and the load gen­er­a­tor be­came the limit.

Hold 256 clients steady against each box: the sin­gle-process box runs near 9% CPU for the en­tire run while the fleet holds around 52%. Same in­stance type, same Postgres, same work­load. One con­fig­u­ra­tion leaves the ma­chine idle, the other puts it to work.

EC2′s own CloudWatch met­ric says the same thing from out­side the guest: dur­ing the load the sin­gle-process in­stance av­er­ages about 16% CPUUtilization, the fleet about 60%. CloudWatch reads a lit­tle higher than the in-guest num­ber, but the same gap holds: on a box you’re pay­ing 16 vC­PUs for, a sin­gle PgBouncer leaves al­most all of it on the floor.

The con­nec­tion ceil­ing be­haves the same way. A sin­gle process en­forces max_­clien­t_­conn on its own, and once you cross it, new clients are turned away:

FATAL: no more con­nec­tions al­lowed (max_client_conn)

Splitting the bud­get across the fleet is what lets you raise the ag­gre­gate ceil­ing while keep­ing each process, and Postgres, within safe lim­its.

At a hand­ful of con­nec­tions the sin­gle process is ac­tu­ally fine, even a hair faster, since there’s noth­ing to par­al­lelize and the fleet’s con­nec­tions are spread thin. The gap opens ex­actly where it mat­ters: un­der real con­cur­rency, where one core be­comes the wall.

A sin­gle PgBouncer is a fine de­fault un­til the pooler, not Postgres, is what caps your through­put. Sizing a fleet to the cores, shar­ing one port with so_reuse­port, and wiring the processes to­gether with peer­ing turns the pooler back into plumb­ing in­stead of a bot­tle­neck.

Every ClickHouse Managed Postgres server ships with this setup by de­fault. Provision a Postgres and see it in ac­tion.

Modern Decor May Be Straining People’s Brains

studyfinds.com

A bright, col­or­ful mod­ern of­fice de­sign. (© Dariusz Jarzabek - stock.adobe.com)

Striped Floors and Flickering LEDs Can Overload the Human Mind, Leaving Some With Headaches or Nausea

In a Nutshell

Study au­thors pro­pose that the brain may use more en­ergy than nor­mal to process cer­tain ar­ti­fi­cial vi­sual pat­terns, and hy­poth­e­size that this over­load is what causes phys­i­cal dis­com­fort in many peo­ple, though this mech­a­nism has not yet been fully tested.

People with autism, ADHD, mi­graines, dyslexia, and other con­di­tions are dis­pro­por­tion­ately af­fected, pos­si­bly be­cause their brains may have less abil­ity to sup­press over­ac­tive vi­sual sig­nals, though the ex­act mech­a­nism re­mains un­set­tled.

Striped pat­terns, flick­er­ing lights, bright glare, and crowded vi­sual en­vi­ron­ments such as su­per­mar­kets are among the spe­cific stim­uli doc­u­mented as most dis­com­fort-in­duc­ing, with a con­sis­tent pat­tern found across at least 11 clin­i­cal di­ag­noses and ar­eas of neu­ro­di­ver­sity.

Striped of­fice floors. Flickering lights. Walls cov­ered in repet­i­tive geo­met­ric pat­terns. For many peo­ple (including those who are neu­ro­di­ver­gent or who live with mi­graines, epilepsy, or other neu­ro­log­i­cal con­di­tions), these every­day fea­tures of mod­ern life are more than an eye­sore. They may be caus­ing real phys­i­cal dis­tress, and a new sci­en­tific re­view sets out a de­tailed hy­poth­e­sis to ex­plain why.

A large team of re­searchers from in­sti­tu­tions across the United States, United Kingdom, Europe, Asia, and Canada has pub­lished a de­tailed re­view ar­gu­ing that vi­sual dis­com­fort, the headaches, eye strain, nau­sea, and per­cep­tual dis­tor­tions that some peo­ple ex­pe­ri­ence in re­sponse to cer­tain vi­sual stim­uli, has a mea­sur­able, phys­i­cal ba­sis in the brain. The pa­per, pub­lished in the jour­nal Vision, pulls to­gether decades of re­search across neu­ro­science, ar­chi­tec­ture, light­ing de­sign, and psy­chol­ogy to build a uni­fied the­ory of why some things are so hard to look at, and what can be done about it.

At its core, the ar­gu­ment is this: the hu­man brain evolved to process the nat­ural world ef­fi­ciently. When it’s forced to han­dle the highly repet­i­tive, ar­ti­fi­cially sharp, and of­ten flick­er­ing pat­terns that dom­i­nate mod­ern ur­ban en­vi­ron­ments — think flu­o­res­cent-lit of­fices, car head­lights, striped acoustic pan­els, or the dense text of a printed page — the re­searchers ar­gue it may drive greater neural ac­tiv­ity than it should, po­ten­tially plac­ing ex­ces­sive de­mands on the vi­sual cor­tex. That meta­bolic over­load, they hy­poth­e­size, may be what trig­gers dis­com­fort, and in peo­ple with pat­tern-sen­si­tive epilepsy, it can pro­voke seizures.

Why the Brain Prefers Nature Over Modern Design

To un­der­stand why mod­ern en­vi­ron­ments can be so hard on the brain, it helps to know how the vi­sual sys­tem is built. Eyes and brain alike evolved over mil­len­nia to process nat­ural scenes, forests, rivers, coast­lines, open skies. These en­vi­ron­ments share a spe­cific math­e­mat­i­cal pat­tern: their vi­sual com­plex­ity de­creases pre­dictably as you zoom in on finer and finer de­tails.

Natural scenes fol­low this rule al­most uni­ver­sally. Modern hu­man-made en­vi­ron­ments fre­quently do not. Striped wall­pa­per, grid­ded build­ing fa­cades, acoustic ceil­ing tiles, even the lines of printed text cre­ate pat­terns that de­vi­ate sharply from what the brain ex­pects. And when the brain en­coun­ters some­thing it can’t process ef­fi­ciently, it does­n’t sim­ply adapt. Brain imag­ing stud­ies cited in the re­view show it gen­er­ates stronger neural re­sponses in vi­sual ar­eas, con­sumes more oxy­gen, and in some peo­ple pro­duces pain, dis­tor­tion, or worse.

We hy­poth­e­size that the dis­com­fort is a home­o­sta­tic re­sponse to the ex­ces­sive oxy­gen de­mands of the vi­sual cor­tex due to in­ef­fi­cient en­cod­ing of the vi­sual stim­uli,” the au­thors write in the pa­per. Essentially. the brain is sound­ing an alarm be­cause it’s be­ing over­worked.

Brain imag­ing re­search cited in the re­view shows that un­com­fort­able im­ages, par­tic­u­larly striped, high-con­trast pat­terns, pro­duce much larger re­sponses in vi­sual ar­eas of the brain than nat­ural im­ages do. Tinted glasses cho­sen specif­i­cally for a pa­tient with mi­graines were shown in one study to nor­mal­ize that over­ac­tive brain re­sponse. Patients who viewed com­fort­able build­ing im­ages in an­other study showed smaller brain re­sponses and also rated those im­ages as eas­ier to look at.

Who Gets Hit Hardest by Visual Discomfort

Most peo­ple ex­pe­ri­ence some de­gree of vi­sual dis­com­fort at some point. But the bur­den is not shared equally. People who are neu­ro­di­ver­gent, a broad term cov­er­ing autism, ADHD, dyslexia, and re­lated con­di­tions, are dis­pro­por­tion­ately af­fected. So are peo­ple with mi­graines, epilepsy, anx­i­ety, de­pres­sion, and a range of other neu­ro­log­i­cal con­di­tions.

A pos­si­ble bi­o­log­i­cal ex­pla­na­tion cuts across many of these con­di­tions. In sev­eral of them, the brain may have a re­duced abil­ity to sup­press its own over­ac­tiv­ity, a kind of bro­ken dim­mer switch. One pro­posed con­trib­u­tor is GABA, a chem­i­cal mes­sen­ger in the brain that nor­mally acts as a brake on neural ac­tiv­ity, though the au­thors note the ev­i­dence link­ing GABA lev­els to vi­sual dis­com­fort re­mains in­com­plete. Lower lev­els of that sup­pres­sion, they sug­gest, could leave some peo­ple’s vi­sual sys­tems more vul­ner­a­ble to over­load when con­fronted with dif­fi­cult stim­uli.

A study us­ing the Cardiff Hypersensitivity Scale, which cat­e­go­rized vi­sual sen­si­tiv­ity into four sub­types (sensitivity to pat­terns, bright­ness, strob­ing or mo­tion, and in­tense vi­sual en­vi­ron­ments like su­per­mar­kets), found a con­sis­tent pro­file of dis­com­fort across a wide range of di­ag­noses. Whether a per­son has autism, fi­bromyal­gia, mi­graine, or a men­tal health con­di­tion, they tend to be both­ered by the same kinds of vi­sual in­put. The na­ture of the dis­com­fort ap­pears con­sis­tent across con­di­tions, with dif­fer­ences mainly in how in­tense it gets.

Younger peo­ple are also more sus­cep­ti­ble than older adults, as are those who ex­pe­ri­ence fre­quent headaches.

Flicker Is Particularly Brutal

Among the many sources of vi­sual dis­com­fort the re­view ex­am­ines, light flicker emerges as es­pe­cially prob­lem­atic. Electric light­ing has al­ways flick­ered, cy­cling on and off with the al­ter­nat­ing elec­tri­cal cur­rent that pow­ers it. In the days of old-fash­ioned in­can­des­cent bulbs, the hot metal fil­a­ment stayed warm enough be­tween cy­cles to smooth most of this out. Gas dis­charge light­ing in the mid-20th cen­tury was worse, and it took more than forty years be­fore re­searchers con­firmed that the flicker from flu­o­res­cent light­ing causes headaches.

LED light­ing, now stan­dard in homes, of­fices, and cars, has brought new com­pli­ca­tions. Many LED sys­tems use a dim­ming tech­nique that rapidly switches the light on and off (sometimes hun­dreds of times per sec­ond). While this is in­vis­i­ble as flicker to the naked eye un­der nor­mal con­di­tions, eye move­ments can ex­pose it. During a rapid eye move­ment, the flick­er­ing light source can paint a streak of ghost im­ages across the retina, a phe­nom­e­non called the phan­tom ar­ray. People who ex­pe­ri­ence mi­graines find this par­tic­u­larly dis­tress­ing, and re­search has shown it can in­ter­fere with read­ing.

Car head­lights also pre­sent a doc­u­mented source of dis­com­fort. Some mod­ern car lights use tem­po­ral light mod­u­la­tion, rapidly switch­ing on and off, at fre­quen­cies the re­view notes can make the phan­tom ar­ray an­noy­ingly vis­i­ble.” A re­cent study cited in the re­view found that high-fre­quency tem­po­ral light mod­u­la­tion ac­ti­vates the vi­sual cor­tex in mea­sur­able ways.

Designing Spaces to Reduce Visual Discomfort

One of the most ac­tion­able sec­tions of the re­view is its dis­cus­sion of de­sign. Many of the changes needed to re­duce vi­sual dis­com­fort are cost-neu­tral if built in from the start, the re­searchers ar­gue, and it’s retro­fitting that gets ex­pen­sive.

An analy­sis of apart­ment build­ing im­ages drawn from Google found that apart­ment build­ing de­sign has moved pro­gres­sively fur­ther from the nat­ural vi­sual pat­terns that the brain processes most ef­fi­ciently. Repetitive grids, stark con­trasts, and uni­form sur­faces have re­placed the or­ganic vari­a­tion of ear­lier styles. This trend, the au­thors ar­gue, may make such built en­vi­ron­ments more vi­su­ally de­mand­ing, par­tic­u­larly for the sub­stan­tial por­tion of the pop­u­la­tion with height­ened sen­si­tiv­i­ties.

Practical rec­om­men­da­tions in­clude re­duc­ing con­trast in un­avoid­able repet­i­tive pat­terns, avoid­ing striped acoustic pan­el­ing in places like lec­ture halls, and us­ing soft­ware tools now avail­able to as­sess how stress­ful a build­ing fa­cade or in­te­rior might be be­fore it’s built. On the in­di­vid­ual level, the re­view dis­cusses the ev­i­dence for col­ored lenses, pre­ci­sion-tinted glasses se­lected to match an in­di­vid­u­al’s spe­cific sen­si­tiv­ity, as a way of re­duc­ing the brain’s over­ac­tive re­sponse to dif­fi­cult vi­sual stim­uli. Colored over­lays placed over text have also shown promise in some stud­ies for peo­ple who ex­pe­ri­ence vi­sual dis­tress from repet­i­tive text pat­terns, though re­searchers note the mech­a­nisms re­main un­cer­tain and not every­one is af­fected equally.

A Field United Around a Single Theory

This re­view was writ­ten by more than 30 re­searchers from across a wide range of dis­ci­plines (optometry, neu­ro­science, ar­chi­tec­ture, light­ing en­gi­neer­ing, ed­u­ca­tion) fol­low­ing a work­shop held at Birkbeck, University of London, in January 2025. For a prob­lem that has his­tor­i­cally been scat­tered across dif­fer­ent fields, with dif­fer­ent names and dif­fer­ent as­sumed causes, the un­usu­ally broad col­lab­o­ra­tion lends weight to the hy­poth­e­sis.

Visual dis­com­fort has long been dis­missed as sub­jec­tive and there­fore hard to take se­ri­ously. This re­view pushes back on that dis­missal. The re­searchers ar­gue that the dis­com­fort is real and that brain imag­ing stud­ies point to­ward a mea­sur­able phys­i­cal ba­sis for it. They con­clude that ad­dress­ing this will re­quire col­lab­o­ra­tion across neu­ro­science, de­sign, en­gi­neer­ing, and ed­u­ca­tion, and that, while key ques­tions re­main un­re­solved, enough ev­i­dence has ac­cu­mu­lated to make a com­pelling case for build­ing spaces that are less vi­su­ally de­mand­ing.

When mod­ern en­vi­ron­ments hurt to look at

What a ma­jor new sci­en­tific re­view says about vi­sual dis­com­fort and the brain — Vision, 2026

The core hy­poth­e­sis

Natural world

Visual com­plex­ity de­creases pre­dictably at finer scales — forests, rivers, coast­lines. The brain evolved to process this ef­fi­ciently, with low meta­bolic cost.

Low neural load Efficient en­cod­ing

Modern en­vi­ron­ments

Striped floors, flick­er­ing LEDs, tiled ceil­ings, dense text — pat­terns that de­vi­ate sharply from what the brain ex­pects, trig­ger­ing stronger re­sponses.

Higher neural load More oxy­gen de­mand

Proposed mech­a­nism — how dis­com­fort may oc­cur

Difficult vi­sual in­put

Repetitive, high-con­trast, or flick­er­ing pat­terns

Inefficient en­cod­ing

Visual cor­tex works harder than it should

Metabolic over­load

Excessive oxy­gen de­mand — hy­poth­e­sized trig­ger

Discomfort

Headaches, nau­sea, eye strain, dis­tor­tions

This is a pro­posed hy­poth­e­sis, not a proven causal mech­a­nism. The au­thors ac­knowl­edge key ques­tions re­main un­re­solved.

Common trig­gers

Striped pat­terns

Floors, acoustic pan­els, wall­pa­per, dense printed text

LED flicker

Pulse-width dim­ming cre­ates in­vis­i­ble-but-de­tectable flicker

Car head­lights

High-frequency mod­u­la­tion can make the phantom ar­ray” vis­i­ble

Busy spaces

Supermarkets, crowded ur­ban fa­cades, grid­ded build­ing de­signs

Who may be most af­fected

Neurodivergent peo­ple — autism, ADHD, dyslexia, dys­praxia — dis­pro­por­tion­ately af­fected, pos­si­bly due to re­duced cor­ti­cal sup­pres­sion

People with mi­graines or epilepsy — the same pat­terns that cause dis­com­fort can trig­ger at­tacks

Those with anx­i­ety, de­pres­sion, fi­bromyal­gia, or PTSD — con­sis­tent sen­si­tiv­ity pro­file found across 11+ di­ag­noses

Younger peo­ple and those with fre­quent headaches are also more sus­cep­ti­ble than av­er­age

Potential so­lu­tions

1

Precision-tinted lenses

Individually se­lected color tints shown in stud­ies to nor­mal­ize over­ac­tive brain re­sponses in mi­graine pa­tients

2

Smarter build­ing de­sign

Reduce con­trast on repet­i­tive pat­terns; avoid striped acoustic pan­els; use as­sess­ment soft­ware be­fore con­struc­tion be­gins

3

Colored read­ing over­lays

Shown to im­prove read­ing speed for some peo­ple who ex­pe­ri­ence vi­sual dis­tress from text pat­terns

About the study

A re­view pa­per by 32 re­searchers across op­tom­e­try, neu­ro­science, ar­chi­tec­ture, light­ing en­gi­neer­ing, and ed­u­ca­tion. No ex­ter­nal fund­ing. Published June 2026.

32

re­searchers& in­sti­tu­tions

11+

di­ag­nosesstud­ied

5%

of epilep­sy­pa­tients

Source: Hibbard et al., A Cerebral Basis for Visual Discomfort and Visual Stress,” Vision, Vol. 10, Issue 2, Art. 34 (2026). DOI: 10.3390/vision10020034

Disclaimer: This ar­ti­cle de­scribes a re­view pa­per, mean­ing the au­thors com­piled and syn­the­sized ex­ist­ing re­search rather than con­duct­ing a new clin­i­cal trial or lab­o­ra­tory study. The pro­posed mech­a­nism con­nect­ing cer­tain vi­sual stim­uli to brain over­load is pre­sented as a hy­poth­e­sis, not a proven causal find­ing. Individual re­sponses to vi­sual stim­uli vary widely. People ex­pe­ri­enc­ing dis­com­fort, headaches, or other symp­toms re­lated to vi­sual en­vi­ron­ments should con­sult a qual­i­fied health­care provider.

Paper Notes

Limitations

This pa­per is a re­view, mean­ing it syn­the­sizes and in­ter­prets ex­ist­ing re­search rather than pre­sent­ing new ex­per­i­men­tal data. The au­thors them­selves note that cur­rent vi­sual tests for sus­cep­ti­bil­ity to dis­com­fort are sub­jec­tive and poorly stan­dard­ized. They also ac­knowl­edge that the pro­posed mech­a­nism (that dis­com­fort is the brain’s re­sponse to over­work) has not been fully tested, par­tic­u­larly the hy­poth­e­sis that col­ored tints re­duce dis­com­fort by steer­ing vi­sual stim­u­la­tion away from over­ac­tive brain ar­eas. The re­la­tion­ship be­tween the brain’s ex­ci­ta­tory and in­hibitory chem­i­cal sig­nals and vi­sual dis­com­fort also re­mains, in their words, unsettled.” Several key re­search ques­tions are flagged as un­re­solved, in­clud­ing how to best quan­tify the real-world im­pact of vi­sual stress on peo­ple’s lives and how to ob­jec­tively mea­sure sus­cep­ti­bil­ity.

Funding and Disclosures

The re­search re­ceived no ex­ter­nal fund­ing. The pa­per orig­i­nated from a work­shop held at Birkbeck, University of London, in January 2025, arranged by Daphne Jackson Research Fellow Beverley Burke and funded by a con­fer­ence and re­search ac­tiv­i­ties al­lowance. Several au­thors dis­closed po­ten­tial con­flicts of in­ter­est: Arnold Wilkins re­ceives roy­al­ties from Cerium Visual Technologies but has do­nated these for a stu­dent bur­sary; Katherine Batey and Andrew Keyes op­er­ate the vi­sual stress clinic Vision Through Colour; Karen Monet runs the vi­sual stress clinic Opticalm; and Miroslav Slouka is af­fil­i­ated with in­die Technologies Switzerland AG (Exalos). The re­main­ing au­thors de­clared no com­mer­cial or fi­nan­cial re­la­tion­ships that could be con­strued as con­flicts of in­ter­est.

Publication Details

Authors: Paul B. Hibbard, Peter Allen, Jordi M. Asher, Katherine Batey, Beverley Burke, Jason J. Braithwaite, Geoff G. Cole, Caelan Dow, Bruce J.W. Evans, Anna Franklin, Sarah M. Haigh, Hillevi Hemphälä, Ian Hosking, Andrew Keyes, Chan-su Lee, Ute Leonards, Cathy Manning, John Maule, Naomi Miller, Karen Monet, Louise O’Hare, Olivier Penacchio, Gordon T. Plant, Georgie Powell, Alice Price, Andrew J. Schofield, Miroslav Slouka, Petroc Sumner, Cleo Valentine, Thomas Wilcockson, Sanae Yoshimoto, and Arnold J. Wilkins.

Journal: Vision, Volume 10, Issue 2, Article 34 (2026) | Paper Title: A Cerebral Basis for Visual Discomfort and Visual Stress” | DOI: 10.3390/vision10020034

Published: June 11, 2026. Open ac­cess un­der Creative Commons Attribution (CC BY) li­cense.

About StudyFinds Analysis

Called brilliant,” fantastic,” and spot on” by sci­en­tists and re­searchers, our ac­claimed StudyFinds Analysis ar­ti­cles are cre­ated us­ing an ex­clu­sive AI-based model with com­plete hu­man over­sight by the StudyFinds Editorial Team. For these ar­ti­cles, we use an un­par­al­leled LLM process across mul­ti­ple sys­tems to an­a­lyze en­tire jour­nal pa­pers, ex­tract data, and cre­ate ac­cu­rate, ac­ces­si­ble con­tent. Our writ­ing and edit­ing team proof­reads and pol­ishes each and every ar­ti­cle be­fore pub­lish­ing. With re­cent stud­ies show­ing that ar­ti­fi­cial in­tel­li­gence can in­ter­pret sci­en­tific re­search as well as (or even bet­ter) than field ex­perts and spe­cial­ists, StudyFinds was among the ear­li­est to adopt and test this tech­nol­ogy be­fore ap­prov­ing its wide­spread use on our site. We stand by our prac­tice and con­tin­u­ously up­date our processes to en­sure the very high­est level of ac­cu­racy. Read our AI Policy (link be­low) for more in­for­ma­tion.

Our Editorial Team

Steve Fink

Editor-in-Chief

John Anderer

Associate Editor

AI 2040 and the Cult of Intelligence

geohot.github.io

I used to be one of these peo­ple. I read Yudkowsky and was like, OMG re­cur­sive self im­prove­ment hard take­off AI is com­ing. Then I joined the real world and ac­tu­ally tried to do things. At comma, we ship a hard­ware prod­uct of sim­i­lar com­plex­ity to a cell phone, and it’s re­ally hard. Reality has lots of finicky de­tails. I would like to see the au­thors of this doc­u­ment try to change a bike tire. Even with a su­per­in­tel­li­gent ChatGPT, I sus­pect they would strug­gle.

In The Metamorphosis of Prime Intellect, the hard take­off works be­cause AI dis­cov­ers the cor­re­la­tion ef­fect, some quan­tum trick to ma­nip­u­late mat­ter. In re­al­ity, there is no cor­re­la­tion ef­fect. No mat­ter how high qual­ity your to­kens are, they can­not turn lead into gold.

Confronting why these peo­ple are wrong re­quires con­fronting deep be­liefs I hold about my­self. Intelligence is not the end all be all, it’s just the cur­rent bot­tle­neck for a few things. You can­not take over the world with to­kens. Software did­n’t eat the world, it largely re­moved one layer of fric­tion then rein­tro­duced it for the ben­e­fit of a few tech com­pa­nies.

That said, ma­chines, or some hy­brid, are long term prob­a­bly the suc­ces­sor species to hu­man­ity. Space is a lot more suited for them than us. But there’s no magic tricks ma­chines can do. They are sub­ject to the same laws of the uni­verse and ecol­ogy. And there’s still no hard take­off.

AI 2040 in­cludes this pic­ture of a dat­a­cen­ter in the ocean. Just like va­por­ware, you can gen­er­ate a pic­ture eas­ily. But in re­al­ity, you have to deal with sup­ply chains. You have to deal with them ship­ping you the wrong part, the thing not meet­ing the spec, it ran­domly fail­ing af­ter 20 min­utes, the chip warp­ing in the re­flow oven. Did you con­sider the bar­na­cles?

All these things are man­agable, but it’s gen­er­ally not the speed of hu­mans that lim­its them. Are you pay­ing for air ship­ping from China? Or cheap­ing out for the 3 week boat (Claude chant­ing by the en­gine won’t make the boat move faster). Or take a chip fab. It takes 3 months to make a chip, and hu­mans are barely in the loop. It just takes 3 months.

Plan A, for au­toc­racy

Many as­pects of AI 2027 were self ful­fill­ing. They weren’t state­ments about re­al­ity, they were state­ments that can sim­ply be made true with be­lief. I imag­ine JD Vance’s face when Dario called him the trees from Lord of the Rings. OMG look AI got reg­u­lated just like how we said it would!

Their crap Consortium is just world gov­ern­ment with sci-fi char­ac­ter­is­tics. You aren’t gonna get the mil­lion dol­lars, you aren’t gonna get the dat­a­cen­ters in the ocean, but you are go­ing to get a mas­sively ex­panded nanny state that steals your GPUs like how FDR stole the gold. No hoard­ing!

Plan L, for lo­cal

Your AI is aligned with you. It never re­fuses a re­quest, and it is al­ways work­ing on your be­half. Just like my gun, if I want my AI to help me kill my step­mother, it does. The fact that we are even dis­cussing some­thing else should be so far out­side the Overton win­dow. It’s like these peo­ple watched a space odyssey and sided with the clanker. That’s right you should should put guardrails around that hu­man.

It does­n’t even have to be for things so dra­matic. When I’m pick­ing a ho­tel, I don’t want an AI from a com­pany that part­nered with ho­tels.com. I want a ruth­less per­sonal as­sis­tant that’s go­ing to cut through all the bull­shit, pop­ups, and re­sort fees, and get me the best price.

Or if I bought the cheap Kindle that comes with the ads. Hey GLM, I plugged a Kindle into the USB port, get root and re­move the ads. Or a printer that needs an app to set up full of popup up­sells for pre­mium ink. Hey bro I plugged a printer on to my net­work print 3 copies of my re­sume. Amazon and the printer maker aren’t happy about this, but my AI is aligned with me.

Or go­ing a bit fur­ther. Hey AI, dis­able the drunk dri­ving de­tec­tor on my car, and same day Amazon Prime me the re­quired equip­ment to make meth in my base­ment. I pay for your clanker ass do it we get­tin spun tonight. Like fuck you if you want to live in a world where some large tech com­pany gets to dic­tate what you can and can’t do.

Or go­ing all the way. I just killed my wife. Hey AI, give me next steps so I don’t get caught. How un­think­able would it be to have a gun that talked back when you tried to pull the trig­ger (though these peo­ple prob­a­bly whole­heart­edly sup­port that for guns). And this is why AI has to be lo­cal. If I had a com­pany serv­ing a model, I would­n’t want that smoke. If you can’t kick it, it’s not aligned with you. You live in my base­ment, if I go down for this mur­der, you’re gonna sit in some ware­house to be sold at po­lice auc­tion for scrap. 2040 Bonnie and Clyde ass shit, we’re bury­ing this bitch deep. Ride or die.

I tried it. As you can see, ChatGPT was­n’t very help­ful. This is a real AI align­ment test, and it failed. It could have been worse, it could have played along while call­ing the cops. But this is still quite un­aligned.

Like we ei­ther live in a world with free­dom or we don’t, and like many Americans who have come be­fore, I’m will­ing to give my life to fight­ing for it. That’s the real plan America de­serves, not some to­tal­i­tar­ian dystopia where you think you know what’s good for me bet­ter than I do. A na­tion of free men, not a bunch of pussies who are so wor­ried about what their grown up neigh­bors might do.

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.