Tome
Features

Reading stats

Tome's stats screens are built from real session data — every minute logged by the web reader and every session the TomeSync KOReader plugin reports. No estimates from page counts, no fudge factors, no guessing. If a chart is empty, it's because the data isn't there yet.

Reading time

Where the data comes from

Two writers, one table. The web reader and the TomeSync plugin both POST to reading_sessions: start time, end time, pages turned, device. The stats endpoints aggregate from there. The UserBookStatus table provides finished-book counts (status = "read") and the TomeSyncPosition table provides current-progress percentages.

Overview tab

The "where am I right now?" dashboard. Designed to be glanceable.

Totals + streak

Six top-level metric cards

Six counters across the top, scoped to the period selector (default 30d, options 7d / 30d / 90d / 365d / all). Sum of duration_seconds, count of session rows, finished books, current streak, pages turned, completion rate. Average session length is shown under Reading Time.

Streak is in local-with-rollover days, with a one-day grace: if you haven't read yet today, the streak still shows yesterday's value instead of dropping to zero. Once midnight (plus rollover) passes without a session, the streak breaks.

Currently reading

Currently reading books with progress bars

Books with UserBookStatus.status = "reading", ordered by most-recently-read. Progress comes from TomeSyncPosition.percentage when KOReader has reported in, else UserBookStatus.progress_pct from the web reader.

Reading time per day

Bar chart: reading time per day

One bar per day in the selected period. Tallest spike is your biggest session day. Useful to spot binges (single tall bar) vs steady consistency (many short bars).

Top books

Top 10 books by reading time

Top 10 books by total reading time in the period. Joins reading_sessions against books and groups by book_id. The big numbers usually surface a book you've been chewing on for weeks.

365-day activity grid

GitHub-style 365-day reading activity grid

GitHub-style annual heatmap of daily reading minutes. Cells go from empty (no session) to full accent (1h+ in a day). Useful to spot patterns — vacation blocks, work-week droughts, weekend regulars.

Habits tab

The "when and how do I read?" dashboard.

Hour × day-of-week heatmap

Hour-by-day-of-week reading intensity heatmap

A 7×24 grid. Each cell is "minutes read in this hour of this weekday, summed across the selected period." The colour scale is per-user: the darkest cell is your peak, everything else is relative to it.

The interpretation: find your reading windows. If Monday 21:00 lights up but Monday 09:00 is blank, you read at night, not before work. If Saturday morning blooms after a few months of use, you read more on weekends than you thought. The point isn't the absolute numbers, it's the shape.

Session timeline

Recent session timeline on a 24h axis per day

Recent sessions plotted as horizontal bars on a time axis — start time on the x-axis, duration as bar length, one row per date. Lets you see binge sessions (long bars) vs nibble sessions (short bars), and at a glance whether you read in one big block or many small ones.

Reading pace

Pages-per-minute over recent sessions

Pages per minute, computed per-session from sessions with duration_seconds > 60 and pages_turned > 0.

Two things this catches: format effects (manga clocks 5+ ppm, dense literary fiction sometimes 1 ppm) and tiredness (your evening pace usually drops below your morning pace). Don't compare your number to anyone else's — pace is a fingerprint, not a benchmark.

Monthly comparison

Bar chart of reading hours and books finished per month

Reading hours + books finished by month, last 12 months. Smooths out the day-to-day noise — month-on-month is more legible than week-on-week.

Library tab

The "what have I been reading?" dashboard. Focused on books, not on time.

Series completion ladder

Series completion cards with per-series progress bars

For every series you have at least one volume in, a card showing volumes you own vs volumes you've finished. Standalones are skipped — they're not a ladder.

Author affinity

Top authors by total reading time

Authors ranked by total minutes spent in their books. Distinct from "most books read" — an author you've read three short novels by ranks lower than one whose 1500-page tome you've finished.

Completion by type

Finish rate per book type

For each BookType (Novels, Manga, Comics, etc.) — total books started vs finished. Quick way to see "I'm a manga reader who pretends to read novels" or vice versa.

Category breakdown

Pie chart of reading time per book type

Reading time split by book type — pie chart format. Pairs with completion-by-type to show not just how much you finish but how much time you sink in each format.

Library growth

Cumulative books added over time, split by book type

A cumulative line chart of books.added_at grouped by month and split by book type. Not about reading — about hoarding. Goes up and to the right. Useful for noticing acquisition sprees you'd otherwise forget about.

Charts that aren't here yet

Things explicitly considered and not built:

  • Per-chapter pace within a book — too noisy for the current data shape; sessions cross chapters and chapter boundaries aren't tracked.
  • Sleep-quality correlation — we have the data (late-night session detection) but no second data source to correlate against.
  • Multi-user leaderboards — stats are per-user only by design. Tome isn't trying to gamify reading across a household.

API access

All the data behind these charts is reachable via the API. Useful for one-off scripts:

  • GET /api/stats?days=30&tz_offset=-120 — the main bundle (everything on this page).
  • GET /api/stats/completion-estimates — projected finish dates for in-progress books.
  • GET /api/stats/sessions — paged session list, oldest first.
  • DELETE /api/stats/sessions/{id} — delete a misfire.

Authenticate with an API token or a JWT. Times are UTC in the payload; bucketing happens server-side using the tz_offset you pass.