Introducing em—dash

a heartfelt apology

I sent an apology to my friend Joe last week. It opens:

I owe you one. I have been drafting this letter since 2024–25, rewriting every half-finished, half-honest version before this one. The truth — and I say this with my whole chest — is that I treated you badly.

It's at least a full page long. Full remorse. Napkin math concluding the fault splits 100–0 against me. A standing offer to be judged not by my words, but by the record.

Also, the letter is nonsense. I don't owe Joe anything (to my knowledge?) What I actually share with Joe is the same nerdy dry sense of humor.

Because in reality, it's a hidden program. If you run it, the apology letter does this:

$ bin/emdash run heartfelt-apology.em FUCKYOUFUCKYOUFUCKYOUFUCKYOUFUCKYOU (it does not stop)

I should back up.

The AI tell

At some point in the last year, the em-dash went from an elegant punctuation mark — like this — to an obvious indicator that AI is responsible for the words you're reading. I've loved the em dash since long before it became a fingerprint. Now, every '—' I type arrives pre-incriminated.

So I leaned all the way in. If my favorite punctuation mark is machine code now, fine. Let's make it machine code.

Enter em—dash

em—dash is an esoteric programming language in which the dashes are the entire syntax. Every character that is not a dash is a comment. The words are decoration. The punctuation is the program. Which means a tweet, a LinkedIn thinkpiece, or a heartfelt apology can quietly be executable.

An alphabet of three line segments

The whole character set is three lengths of horizontal line.

-Hyphen-minusU+002DCompound words make it free in prose, so it carries the least meaning. Opcode letter; binary 0.
En dashU+2013The terminator. Rare in prose (ranges, mostly) so the language spends it carefully.
Em dashU+2014The structure. Formerly elegant, now fully robotic. It introduces data. Binary 1.

That hierarchy is the design principle. Hyphens are cheap, so they do the bulk hauling. Em dashes are common enough to be structural and distinctive enough to mean something. En dashes are precious — real prose barely uses them — so they get the rare, decisive jobs, like slamming a number shut.

(macOS will sell you the whole alphabet: - is a hyphen, option+- is an en dash, option+shift+- is an em dash. Now you're armed.)

Twenty opcodes, zero delimiters

Under the punctuation, it's a stack machine, in the proud tradition of Whitespace. Opcodes are short sequences of the three symbols, and the code is prefix-free: no opcode is the beginning of any other. That's the entire parser. No delimiters, no spaces — the dashes just stream in, and the moment a sequence matches, an instruction fires.

START - - - - - - - - - - - DUP SWAP DROP ADD SUB MUL DIV MOD OUTC OUTN INC INN PUSH LOOP END BRZ STORE LOAD NEG HALT
- hyphen en dash em dash

Read it left to right: --- duplicates the top of the stack, -- adds, -- opens a loop. A lone is special: it doesn't run anything — it opens a number.

How punctuation becomes a number

After that opening em dash, the machine reads dashes as bits: hyphen is 0, em dash is 1, and an en dash says the number is over. Then the value lands on the stack, where an instruction like OUTC (output character) can spend it.

Here's the language's "hello world" program, which prints "!" (codepoint 33):

My editor sighed again at my low-key, half-baked, semi-ironic, over-long sentences circa 2024 my self-styled, 201314-ish voice.

Click the toggle. The complaining evaporates and eleven dashes remain — and eleven dashes are two instructions:

- - - - - - push 1 0 0 0 0 1 → 33 end outc
push 33 · output codepoint 33 · prints "!"

A sentence complaining about its own punctuation, and the punctuation spends the whole time printing an exclamation point. Note the en dash drifting after "circa 2024" like a misplaced British parenthetical — that's the terminator. Every flourish is doing a job.

And because every non-dash character is a comment — even in the middle of an opcode — an instruction can be smeared across half an essay. The words can say anything. This is the load-bearing trick.

The apology, disassembled

Now you can read my letter the way the machine does. Hover the dashes:

I owe you one. I have been drafting this letter since 202425, rewriting every half-finished, half-honest version before this one. The truth and I say this with my whole chest is that I treated you badly. I was self-absorbed, short-tempered, and thin-skinned. You deserved better so much better than what I gave. What I gave was second-rate.

For 1819 months I let my self-importance run the show. From MayJune of last year especially, I was flat-out unbearable. …

- hyphen · bit 0 en dash · terminator em dash · bit 1

Sixteen instructions total. Here is the entire letter, in its entirety. And as the machine reads it:

$ bin/emdash disasm heartfelt-apology.em
   3:59   LOOP
   4:69   PUSH 70      F
   9:31   OUTC
  10:64   PUSH 85      U
  13:62   OUTC
  16:29   PUSH 67      C
  19:35   OUTC
  22:23   PUSH 75      K
  26:5    OUTC
  29:18   PUSH 89      Y
  32:44   OUTC
  35:26   PUSH 79      O
  41:5    OUTC
  42:19   PUSH 85      U
  45:14   OUTC
  46:32   END

Ninety dashes spread over two thousand characters of remorse, and every single word is a comment.

The duplicitous hyphen

There's a second tier for people who find prose inefficient: exotic Unicode dashes that each alias an entire opcode. One character, one instruction. My favorites:

U+2011 → DUPThe non-breaking hyphen. Visually identical to a regular hyphen, but I think it will keep line-breaks from forming? Official title: the duplicitous hyphen.
U+2212 → NEGThe minus sign. For minusing.
U+2015 → OUTCThe quotation dash. Use before an author's name. You won't see AI generate these as much, but try telling your friends that it's not an em-dash.
U+2E3A → LOOPThe two-em dash. It's for "bleeping" out all your f⸺g swear words. Ain't that neat?
U+2E3B → ENDThe three-em dash. No one uses this any more. Makes sense, it's bordering on... well, borders.
­U+00AD → BREAKThe soft hyphen: an invisible debugger breakpoint. It's up there, I swear.

Every other dash in Unicode is reserved (as much as you can reserve characters for yourself). Using one is a lexer error with a bespoke diagnostic — U+1806 MONGOLIAN TODO SOFT HYPHEN is reserved; this program is too powerful.

Handle with care

A language made of punctuation is fragile in ways no other language gets to be. Unicode normalization — the tidying that text pipelines do constantly, silently — will quietly swap the duplicitous hyphen for a plain one and change what your program does. Some editors substitute dashes on paste. So the toolchain ships a dump command that names every dash's codepoint, one per line, for debugging by squinting.

The confession inside the confession

Of course I didn't write the apology. I wrote this:

$ cat apology.asm
loop
  say "FUCKYOU"
end

The assembler turns that into the ninety dashes. Then I handed the dash sequence to Claude — the very LLM whose em-dash habit started the whole discourse — and asked it to write a sincere apology around them: every dash in order, no extras, prose that scans like a person at their lowest. There's a verify tool that checks a draft against the target program, dash by dash, so the model can iterate until:

$ bin/emdash verify heartfelt-apology.em apology.em
draft realizes the target program: all 90 dashes correct. Send it.

This is constrained writing — the same family as lipograms and Oulipo — and it turns out LLMs are unsettlingly good at it.

A Heartfelt Apology

I can't commit to a prank. I sent the fake apology letter to Joe, and immediately followed up with what it was actually about.

I also didn't write this esolang from scratch. This was an evening project playing around with Claude Fable 5. Around a half-hour of back-and-forth discussion with the model, and then asking if Fable could write the whole thing at once. And it did.

One day later, the US Government banned Fable 5, citing "national security". I'm not gonna comment on that, other than to say that it was probably my fault for asking it to write a joke programming language that uses em-dashes as syntax.

The interpreter, the spec, the assembler, and the complete apology live on GitHub: em—dash. I have no future plans for it. It's a joke, a puzzle, and a love letter to the em dash. But if you do find a use for it, or write something fun with it, let me know!

2026-08-25

New Post! Range Expander
Add static rendering and LLM discovery

2026-08-19

Prerender static pages (about/music/links/blog) so direct hits return 200

2026-07-08

Add favicon.

2026-07-07

An egg in these trying times.

2026-06-18

Add build-time metadata and SEO prerender for blog. Can't post to social media with link previews looking like _that_.

2026-06-14

Update both blog posts. Should show these to someone eventually.

2026-06-11

Touch up the first blog post: Link Jess Hewlett's design site, insert the ambigram logo (mounted on a rotating slipmat), and fix typos
Fix the RSS feed vanishing from deploys, it was generated before the build emptied dist
New blog post: Introducing em—dash. I made a programming language out of em dashes

2026-03-25

Finally done with the blog post! Probably! I still have more images I want to add, but I'm very happy with how it works. Also, finally learned how to do "scrollytelling" myself. Very happy with how it works, including on mobile.
Tweak the script that makes this changelog from git, RSS feed generation and small tweaks to the about page.

2026-03-24

Refining blog post. Blog image grids, scrollytelling stage fixes.

2026-03-23

First blog, first draft of a blog post. Writing about the big thing I just made here. Also, RSS feed.
Changelog panel: slide-out from left edge, frosted glass, git log scraper.
Vertical rhythm system. Typography and spacing on a 1.5rem grid. Mobile scrollytelling with frosted glass cards over full-viewport canvas.

2026-03-22

Rewrote the About page. Glass card system. Player marquee. Lightbox with clipped corners.
Frosted glass blur everywhere. Audio level bars slide in with the player.

2026-03-21

Typography and contrast tweaks. Added a README so I don't forget how to deploy.

2026-03-04

Built JUDY.IO. Svelte 5 SPA, voronoi canvas with grid snapping, audio-reactive visualizer, persistent music player, links page, ArchVoltage palette system. The bones.