VicroCode
Make Code Create Value
VicroCode is a lightweight online platform for publishing, running, sharing, and monetizing code projects. Launch HTML, Python, SQLite, AI agents, management tools, games, and more without server setup.
Please wait while VicroCode loads. You can also explore the AI programming guide
Loading...

AI MARKET GUIDE

The Half of a Bookmarklet Reading Tool That Actually Survives a Rebuild

A long-form reader split cleanly in two: the browser-injection half is stuck, the paste-a-link reader ports fine. Here's how to rebuild the part that travels.

There's a post going around from someone who built a long-form reading aid called Spotlight Reading. The pitch is relatable: they read a long article, hit the third line, and lose track of which line they were on. Not distraction — the attention is still there — it's the bookkeeping of "where was I" that breaks. They estimate a third of the time on a long piece goes to hunting for the line again.

What makes the write-up worth studying isn't the feature list. It's the builder's own honesty about what mattered and what didn't. And buried in that honesty is a clean answer to a question every solo builder eventually faces: which parts of my clever hack will actually survive if I move it off a browser injection and onto a hosted app?

The confession that reframes the whole thing

The tool ships with a lit band that sits over the lines you're reading. It looks like the headline feature. The builder says plainly it isn't.

Three counterintuitive findings came out of iteration. Masking everything outside the band made reading worse, because you lose the spatial sense of where you are on the page — so they switched to letting contrast decay along a gradient instead of hiding text. Blurring the surrounding text was worse than dimming, because the brain reflexively strains to resolve blur and you can't switch that instinct off; a plain contrast drop reads as "not your turn yet" rather than "this is broken."

Then the real one: the band isn't what does the heavy lifting. The typography reflow is. Narrow columns — roughly 23 characters per line for Chinese, about 65 for English — plus line spacing pushed to around double. That single change beats the light band. Line loss happens in the return sweep, the instant your eye jumps back to the start of the next line, and the longer the line, the worse the landing error. Most web pages run forty-plus characters per line because that's how you fit ads around text, not how you make text readable.

That reframing is the whole ballgame for a rebuild. If the value lives in reflow and contrast, and reflow and contrast are pure layout over text you already have, then the core of this tool is portable. It does not need to live inside someone else's page.

Where the bookmarklet magic doesn't port

The original had a second half: a bookmarklet that injects the reading aid into arbitrary pages. That half is a pile of browser-runtime tricks.

The builder found that a bookmarklet isn't bound by the host page's CSP, but a loader script is. Appending an external script broke instantly on Wikipedia, which enforces script-src — and Wikipedia is exactly the kind of long, dense page you'd want to read this way. The fix was inlining the whole compressed script into a javascript: URL so it runs as a user gesture rather than a page-initiated resource request, sidestepping CSP. Clever, and completely dependent on running inside a live browser as an injected script.

That is the honest boundary. There is no in-platform way to reproduce arbitrary in-page injection, CSP evasion via user-gesture execution, or a browser extension surface. Those depend on the browser as a runtime you control from the inside. The builder even lists their own dead ends: Canvas-rendered readers like some web Kindle views can only be tinted whole-page because the text is painted as an image, and native apps have no entry point at all. If your plan is "take over any page anywhere," that plan stays outside what a hosted app can do, and it's worth saying so up front rather than pretending otherwise.

The half you can rebuild, and how

Strip away the injection story and what's left is the mode the builder describes first: paste a link or a chunk of text, or open a txt / pdf / epub, and read it in a clean reflowed view. That is a hosted app, full stop.

The front end is a self-contained reader. You can run HTML online for the reading surface — the reflow into narrow columns, the doubled line spacing, the band you can drag or advance sentence by sentence, and the contrast gradient. One detail from the original carries straight over as a gotcha: CSS custom properties won't animate transitions unless you register them with @property and a syntax, because untyped custom properties give the browser nothing to interpolate and the band just teleports between values. That's a front-end concern that lives entirely in the hosted page.

The parsing is where a small backend earns its keep. txt is trivial, but pdf and epub are not. The builder points at the ugly part: text pulled from a PDF is broken along layout, so a single sentence gets split across several fragments, and you have to stitch it back by reasoning about line height and the right margin to tell hard breaks from soft layout breaks — otherwise the band can't snap to a sentence. That stitching logic is exactly the kind of thing you'd run Python online to handle: accept an uploaded file, extract and re-sew the text, hand back clean paragraphs the reader can reflow. File management covers the upload side, and the extracted output is just text going back to the front end.

The privacy posture in the original is worth keeping because it's cheap to keep. Everything parsed client-side, no accounts, no analytics, and the only thing remembered is your last position, keyed by a fingerprint of the text — the text itself never stored, just the position. If you do want cross-session position memory server-side, a small SQLite table mapping a text fingerprint to an offset is all it takes, and it stores no content, only where you stopped. That's a deliberate design choice, not a default; keep it local unless you have a reason not to.

One more honest note about the link-paste path. The original routed remote fetches through a Worker to dodge CORS, with SSRF protection that refuses localhost, private ranges, and non-http schemes. If you rebuild the fetch server-side in Python, that protection isn't optional — a service that fetches arbitrary user-supplied URLs is an SSRF liability by default, and the same allow/deny rules have to move with it. Whether remote fetching is worth the exposure at all is a real trade-off; the paste-text and upload-file paths carry none of it, and they cover most of the actual use.

Why this split is the useful lesson

The broader signal across what solo builders are shipping right now is that the ambitious surface and the durable core are often different things. The interesting move is naming which is which before you commit to a platform.

Here the durable core is small and honest: text in, reflow and contrast out, sentence-level tracking on top. That runs as a hosted reader with a thin parsing service behind it, and web app hosting lets you publish and share it without owning a server. The injection half — the part that felt like the magic — is the part that doesn't travel, and knowing that saves you from porting effort that was never going to land.

If you've felt the line-loss problem yourself, the buildable version is genuinely worth an afternoon. Just build the half that survives the move, and be upfront with yourself about the half that stays behind in the browser.