Skip to content

a-citizen-of-the-united-states/inlinereader

Repository files navigation

InlineReader

InlineReader helps readers move through books and web articles faster by reducing the need for constant eye movement across the page. The project includes demos that show how a web developer can embed a small control at the bottom of an article with a simple reference. When opened, it expands into a focused reading mode that lets the user control the speed of the text, making reading quicker and easier on the eyes.

It is a small RSVP-style word rendering control (core engine + host/runtime + dock UI).

It presents text one token at a time at a fixed cadence. The goal is to isolate and experiment with timing, buffering/backpressure, forward/rewind navigation, byte-accurate progress, and canvas rendering.

The project is meant to be embedded, modified, or used as a reference.

Scope

InlineReader focuses on presentation mechanics and efficient local-file streaming.

Intentionally out of scope:

  • content extraction (web/article parsing), libraries, sync, accounts
  • EPUB/PDF ingestion pipelines
  • “page” layout, highlighting systems, themes, or UI frameworks (runtime-only territory)
  • “speed-reading tricks” that rely on flicker/animation illusions at high WPM

Characteristics

  • Word-by-word RSVP presentation
  • Optional ORP alignment (runtime/workbench)
  • Canvas-based rendering (runtime/workbench)
  • Deterministic engine timing (core)
  • Forward + reverse playback (workbench wires rewind controls)
  • Streaming local-file ingestion with bounded queues (no full-file reads)
  • Byte-accurate progress (playedBytes / totalBytes)
  • Minimal dependencies

Recommended entry points

  • Dock UI (default): examples/dock/ — practical page reading UI (seek scrubber, repetition cues, settings panel).
  • Embed (minimal): examples/embed/embed.html — smallest self-contained example for embedding in a page.
  • Workbench (low-level): workbench/ — reference host + local-file loader + byte-accurate progress.

Repo layout

  • src/core/ — engine-only (timing, buffering semantics, token sources, emitted render facts)
  • src/runtime/ — host/runtime layer (RAF scheduling + canvas renderer + input policy). Exported as InlineReaderRuntime.
  • workbench/ — workbench page (TS + CSS) for local-file ingestion, seek, and byte-accurate progress
  • samples/texts/ — optional sample texts + provenance notes (if present)
  • dist/ — generated build output

Running locally

Requirements

  • Node.js 18+ (includes npm)
  • A local static web server:
    • macOS/Linux: python3
    • Windows: py

Install + build

From the repo root (the folder containing package.json):

npm ci        # or: npm install
npm run clean
npm run build

Serve

Serve the repo root over HTTP (needed for ES modules):

macOS/Linux:

python3 -m http.server 8000

Windows:

py -m http.server 8000

Open:

Don’t double-click the HTML files from the filesystem. Browsers will block module imports without a server.

Using the Workbench

  • Click Load File and choose a .txt file (UTF-8 recommended).
  • The Workbench shows:
    • a read-only Progress indicator (percent) driven by byte metadata
    • a separate Seek control (scrubbable) that jumps on token boundaries
  • Optional sample texts (if included) live under samples/texts/ (select via file picker).

Using the Dock

  • The Dock reads the page content under a selector (default #content).
  • It can skip non-readable blocks (lists/figures/tables/code) by default.
  • Seek is scrubby but stays byte-accurate via the underlying core.

Production bundle (Dock Widget)

If you want a deployable Dock Widget with minimal network requests, generate the production bundle (1 JS + 1 CSS).

Build

npm install
npm run build:prod:dock

Outputs:

  • dist-bundle/inlinereader-dock.min.js
  • dist-bundle/inlinereader-dock.min.css

Test locally

Serve the repo and open:

  • http://localhost:8000/examples/dock/index.bundle.html

Web Admin Install (Bundled Dock Widget)

If you want the Dock UI on hundreds of pages (without copy/pasting markup), deploy the bundled Dock Widget.

1) Build the bundle (on a dev machine)

npm install
npm run build:prod:dock

2) Copy files to your web root

Copy this folder to your server (example path shown):

  • dist-bundle/

Target layout:

/var/www/html/inlinereader/
  dist-bundle/
    inlinereader-dock.min.js
    inlinereader-dock.min.css

You do not need Node.js on the web server—just serve the static files.

3) Add the one-line snippet to your site template

Add this to a shared header/footer template so it loads on every page:

<script type="module"
        src="/inlinereader/dist-bundle/inlinereader-dock.min.js"
        data-ir-root="#content"></script>

The widget mounts the Dock at the bottom of the page and loads its CSS automatically.

4) Optional configuration

For strict CSP (no inline scripts), configure the widget via data-* attributes on the script tag:

<script
  type="module"
  src="/inlinereader/dist-bundle/inlinereader-dock.min.js"
  data-ir-root="main"
  data-ir-enable-load-url="false"
></script>

Supported attributes:

  • data-ir-root (or data-ir-root-selector) — CSS selector for the main content root (optional).
  • data-ir-enable-load-url — reserved for future URL-loading UI; default is false.

Caching note

For best performance, serve the bundle with long-lived caching (e.g., Cache-Control: public, max-age=31536000) and version filenames when you start publishing releases.

Website security notes

InlineReader (Dock Widget) is meant to be safe to host as a static asset, but the usual web rules still apply:

  • No cross-site requests by default. The Dock widget reads text from the current page DOM and does not fetch remote URLs.
  • No inline scripts required. Use data- attributes for configuration to play nicely with strict CSP.
  • Keep it same-origin. Serve /inlinereader/ from your own site. If you later add any URL-loading feature, keep it same-origin and behind your normal access controls.
  • Don’t pass untrusted HTML into it. It extracts text (not markup) from the page it’s running on; avoid injecting user-supplied HTML into your pages without sanitization.
  • CSP suggestion (typical): allow scripts/styles from your own origin and block inline scripts. Example policies vary by site, so treat this as guidance—not a one-size-fits-all.

Workbench config (optional)

The workbench optionally loads workbench/core.config.json (if present) to override defaults.

Recommended repo practice:

  • commit: workbench/core.config.example.json
  • ignore: workbench/core.config.json (local overrides)

Keys

speed.*

  • speed.default (number) — initial playback speed (WPM).
  • speed.minWpm (number) — lower clamp for the speed slider.
  • speed.maxWpm (number) — upper clamp for the speed slider.

buffer.*

  • buffer.secondsAhead (number) — forward prebuffer target (seconds of tokens).
  • buffer.historySeconds (number) — rewind history window (seconds).

file.*

  • file.chunkBytes (integer) — streaming read chunk size in bytes.
  • file.maxQueueTokens (integer) — upper bound for queued tokens (backpressure on producers).
  • file.resumeQueueTokens (integer) — queue depth to resume production after backpressure.

decorators.duplicateRuns.* (optional)

A transient overlay to mitigate RSVP repetition blindness for consecutive duplicate runs. It activates only during runs (and an optional 1-frame post-run carry), then disappears.

  • keyMode (string enum) — how “same word” is detected:
    • "raw": exact token match
    • "edgePunct": strip leading/trailing punctuation
    • "edgePunctLower": edgePunct + lowercase
  • superscriptMode (string enum) — when to draw the ^n overlay:
    • "off": never
    • "runs": only during duplicate runs (and the 1-frame post-run carry)
    • "alwaysAboveWpm": only during duplicate runs and only when WPM ≥ superscriptMinWpm
  • superscriptMinWpm (number, >= 0) — threshold used only when superscriptMode="alwaysAboveWpm".
  • lookaheadTokens (integer, >= 0) — how many upcoming tokens to scan to detect runs early (so ^1 can appear immediately).
  • showAfterWord (boolean) — on the last duplicate frame, show the first non-duplicate token on the right.
  • rewindContext (boolean) — while press-and-hold rewinding, show subdued left/right neighbor words for navigation (superscripts remain reserved for true duplicate runs).

Example

{
  "speed": { "default": 300, "minWpm": 30, "maxWpm": 520 },
  "buffer": { "secondsAhead": 60, "historySeconds": 60 },
  "file": { "chunkBytes": 4096, "maxQueueTokens": 80, "resumeQueueTokens": 10 },
  "decorators": {
    "duplicateRuns": {
      "keyMode": "edgePunct",
      "superscriptMode": "runs",
      "superscriptMinWpm": 380,
      "lookaheadTokens": 32,
      "showAfterWord": true,
      "rewindContext": true
    }
  }
}

See workbench/core.config.example.json for an example.

Core facts and guarantees (compact)

Core is engine-only: it emits minimal render facts/state and performs no UI work.

  • Determinism: With the same token stream, config, and control inputs (play/pause/rewind/setSpeed/tick), Core produces the same emitted states. Core does not read wall-clock time; time advances via tick(dtMs).
  • Progress: playedBytes is an absolute byte cursor that increases on forward consumption and decreases on rewind. totalBytes is the source size (when known). progressRatio = playedBytes / totalBytes in [0, 1] and can reach 1.0 at end-of-stream.
  • Buffering: Streaming token sources are supported without loading full files into memory. Bounded queues apply backpressure using maxQueueTokens / resumeQueueTokens.
  • Rewind: Rewind operates on played tokens and updates playedBytes accordingly. Trimming rewind history does not affect playedBytes or progressRatio.
  • Boundary: Core emits facts; Runtime (or other consumers) own rendering, interaction, and presentation choices.

License

MIT. See LICENSE.

Status

Foundational code. Interfaces and names may change. The goal is to remain small, fast, and easy to embed.

Disclaimer

All trademarks and titles are the property of their respective owners and are used for identification/example purposes only. No endorsement is implied.

“Llama Llama Red Pajama” is the title of a children’s book by Anna Dewdney; referenced here as an example of repeated words.

About

RSVP/ORP reader control with a clean core/runtime split and a docked widget for websites.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors