The 102-line browser port
The three bugs between demo and product
Ratzilla out of the box assumes it owns the whole browser window, and my landing page wanted the terminal embedded in a fixed element, like a reading pane sitting on a desk. That one-line difference surfaced three bugs, and fixing them on a fork was where the actual engineering went.
The first was spectacular: the backend measured the browser window instead of the element it was mounted in, so ratatui’s buffer overran its allocated cell array. It panicked every frame. The second was subtle and mobile-only: phone browsers fire resize events when the address bar slides in and out during scroll, and every one of those tore down and rebuilt the entire character grid, stutter and vanishing text included. The fix is to re-measure first and only rebuild when the grid dimensions actually changed. The third was a classic: the grid element gets destroyed and recreated on resize, taking all its event listeners with it, so keys and mouse had to attach to the stable parent instead.
None of these are exotic. They’re just the bugs you find the first time someone embeds
the thing instead of fullscreening it, which is why they matter. The three patches live
on my fork, branch
fix/dom-backend-embedded-size, one commit per bug, and my web crate pins the last of
them. They’re still sitting there rather than upstream, which isn’t fair to the next
person who tries to put a ratzilla terminal inside a page.
Pre-wrapping, so a scroll offset is a row offset
One design decision from the core carried the whole reading experience: text is pre-wrapped before it ever reaches ratatui, so a scroll offset maps one-to-one to screen rows. Ratatui scrolls by lines, and fighting that with dynamic wrapping is misery. Embracing it made scroll position, verse highlighting, and jump-to-reference all trivially consistent, and a little table maps wrapped lines back to verses. That’s the whole trick.
Shipping 13 megabytes of scripture
The WASM blob is 13.3 MB. All three translations ride inside it, parsed at build time into postcard’s binary format so startup is a deserialize instead of a parse, and the binary goes through Git LFS because a 13 MB artifact in git history is its own sin. For a text application that’s a pretty obscene payload. I know. The alternative was translations fetched on demand, which means a loading spinner between the reader and the text. The page says “nothing leaves your device” and means it; search, bookmarks, everything runs client-side once the blob lands.
The landing page around the terminal went warm parchment and editorial serif, Cormorant Garamond over a faint paper texture, a wordmark and almost no chrome. Terminal-in-browser demos usually read as party tricks. I wanted something quiet, closer to a page than a demo, and quiet is what it turned into.
Where it stands
The web version forgets you on reload.
Position and bookmarks persist natively but the browser build shipped with no
persistence, so every visit starts at Genesis 1 unless you navigate. Genesis 1, every
time. There’s an approved spec from July that fixes this properly, localStorage
persistence plus path-based deep links like /web/1-john/3, and it sits unexecuted in
my specs repo, for reasons I’ve written about separately. The
reasoning is done; the typing hasn’t happened.
The README commit is timestamped 00:52, two nights after the first one, and I declared
victory there. Then I mostly left it alone. I’d like to say it became my daily reader and
it didn’t; most of my actual reading happens in AndBible or on paper, and I’ve opened the
web version more than the binary, by a small margin, because I’ve used neither as much as
I wanted to. On 25 August the core grew a verse range parser, 341 lines and 52 tests for
things like John 3:16-18, and nothing calls it yet: :goto still takes a single
reference and the web build hasn’t been rebuilt since April. That’s what a parked project
looks like, a patch when an idea wants one, wiring later.
I don’t think it stays parked. There are a couple of ideas for the terminal side that would make it something I run rather than something I visit, none of them typed yet. Until then the port is still the part I’d point at, and the reader is still the part I’d hand you.
It’s at bible.hulsman.dev. Type :goto John 3:16 and press
enter.