arcaread

ArcaRead

Read the web faster. ArcaRead is a Manifest V3 browser extension that applies bionic reading — bolding the first half of each word — to any webpage, plus ships a full Reader view with on-device AI summaries, a dyslexia toolkit, and a PDF viewer.

Free and open source on browsers. Native desktop / mobile apps are a separate (paid) product.


Table of contents


What you get


Install

Chrome / Edge / Brave / Arc / Opera

  1. Clone this repo or download as a zip.
  2. Open chrome://extensions (edge://extensions, brave://extensions, etc.).
  3. Toggle Developer mode.
  4. Click Load unpacked and select the repo folder.
  5. Pin the extension from the puzzle-piece menu.

A Chrome Web Store listing is on the roadmap; until then, unpacked install is the route.

Firefox

MV3 landed in Firefox 121+. ArcaRead targets the Chromium API surface; Firefox works with minor caveats (service worker semantics differ slightly). Same about:debugging → “Load Temporary Add-on” flow. A web-ext build and AMO submission is planned.

Safari

See Safari packaging below — one xcrun invocation produces an Xcode project.


Usage


Privacy

ArcaRead does not ship any data off your device. Ever. Specifically:

If you’re still uncertain: the full source is right here. content.js is the only file that touches page content. grep "fetch\|XMLHttpRequest\|sendBeacon" shows every network call — you’ll find only the PDFs you explicitly asked to open.


How it works

Bionic transformation

core.js is a ~60-line dependency-free module exposing FocusCore.transform(text, settings) which returns { segments, modified } where each segment is { text, bold }. It handles Unicode letters, punctuation, and the minimum-word-length / intensity settings. It is UMD-exported (CommonJS + global), so the exact same code runs in the content script, in Node (tests), and later in a JavaScriptCore bridge for iOS / macOS.

Content-script walk

content.js walks text nodes under either a detected article root (if smart mode is on and an article exists) or the full document.body. Each text node is replaced with a <span class="focusread-processed" data-focusread-original="..."> containing the transformed HTML. The data-focusread-original attribute preserves the source text so toggling off cleanly restores the DOM.

Skip rules:

A MutationObserver on the chosen root handles SPA navigation and lazy-loaded content. Open shadow roots are traversed explicitly at initial load and when new shadow-hosting elements are added.

Settings model

Two tiers, merged at read time:

Changing a slider in the popup always writes to the current site’s overrides, never to the global. A “Reset” button wipes the overrides for this site. A small indicator dot lights up when overrides exist.

Reader view

When the user clicks Open this page in Reader, the content script clones the detected article root, strips unsafe tags (<script>, <iframe>, <object>, <embed>), absolutizes relative URLs, and hands the sanitized HTML to the popup. The popup stashes it in chrome.storage.session and opens reader.html?id=<key>. The reader page reads the stashed payload, renders it with FocusCore.transform applied, and exposes TTS, chunks, tints, and summarize.

PDF reader

vendor/pdf.min.js + pdf.worker.min.js are the stock Mozilla PDF.js build. When Open PDFs in reader is enabled, the background service worker intercepts navigations to *.pdf URLs and redirects them to viewer.html?file=<url>. The viewer fetches the PDF, extracts text with PDF.js, groups items into paragraphs by vertical gap, and renders each paragraph with bionic applied.


Project layout

core.js                # portable transformation — no DOM, no browser APIs
manifest.json          # MV3 manifest
background.js          # service worker — defaults, keyboard shortcut,
                       # PDF intercept, analytics aggregator, welcome trigger
content.js             # DOM walker + MutationObserver (consumes core.js)
popup.html, popup.js   # toolbar popup — settings UI, Open in Reader button
reader.html, reader.js # clean reader view — TTS, chunks, tints, summarize
viewer.html, viewer.js # PDF reader — wraps PDF.js with bionic rendering
welcome.html           # first-run tour
fonts/                 # Lexend variable font + SIL OFL licence
vendor/                # PDF.js bundled build
icons/                 # app icon 16/32/48/128 + SVG source + design explorations
tests/                 # unit tests for core.js (node --test)
.github/workflows/     # CI: tests, syntax, zip artifact

Develop locally

# Run the unit tests
node --test tests/

# Syntax-check all extension JS
for f in core.js content.js background.js popup.js reader.js viewer.js; do
  node --check "$f"
done

# Validate the manifest
python3 -m json.tool < manifest.json > /dev/null

Changes to extension files are picked up by clicking the reload button on the ArcaRead card at chrome://extensions. Changes to content scripts also require reloading any open tabs you want to test on — Chrome does not retroactively inject into open tabs.

CI runs the same three checks on every push and builds a downloadable zip artifact on the Actions tab for handing to testers.


Safari packaging

The extension is a standard MV3 WebExtension and packages through Apple’s converter. One-time Xcode setup if you haven’t already:

sudo xcodebuild -runFirstLaunch

Then from the repo parent directory:

xcrun safari-web-extension-converter ./focusread \
  --project-location ./focusread/safari \
  --app-name "ArcaRead" \
  --bundle-identifier "com.damienjerry.focusread" \
  --copy-resources --no-open --force

Open the generated safari/ArcaRead/ArcaRead.xcodeproj in Xcode, sign with your Apple ID team, build, and run. In Safari, enable Develop → Allow unsigned extensions or use your signed build, then flip it on under Settings → Extensions.


Roadmap


License

ArcaRead source: MIT — see LICENSE. Lexend font (bundled under fonts/): SIL Open Font License 1.1 — see fonts/OFL.txt. PDF.js (bundled under vendor/): Apache 2.0.