Sign inSign up

hiddenath/bookhaven

By hiddenath

β€’Updated about 2 months ago

A self-contained 3D e-book reader β€” a web application that simulates a physical book

Image
Web servers
1

329

hiddenath/bookhaven repository overview

β πŸ“– BookHaven 3D

A self-contained 3D e-book reader β€” a web application that simulates a physical book in three-dimensional space. No build step, no Node.js, no npm β€” just a browser and Python.

🌐 Live demo: 129.151.144.75:8080⁠ β€” try it before installing

⁠🎬 Demo

⚠️ The media below is large β€” click to expand (high traffic, ~9 MB total).

▢️ 3D page-flip animation (GIF, ~3.7 MB)

3D page-flip animation

πŸ“± Single-page mode on a phone (GIF, ~3.2 MB)

Mobile single-page mode

🎨 Themes and settings (GIF, ~1.6 MB)

Themes and settings

β πŸ“Έ Screenshots

πŸ–ΌοΈ Show screenshots (13 images, ~24 MB total)
LibraryReader
Theme: PaperTheme: Sepia
Theme: NightTheme: Forest
Theme: SkySettings
BookmarksTable of contents
Footnote tooltipFootnote target
Images in a book

⁠✨ Features

⁠Reading
  • πŸ“• 3D book spread with page-flip animation (CSS 3D)
  • πŸ“± Single-page mode β€” for tall/portrait screens only the right page is shown automatically
  • πŸ–±οΈ Interactive page turning β€” grab the page corner with the mouse or your finger
  • πŸ‘† Touch controls β€” swipe left/right and wide tap zones on the screen edges
  • πŸ”Š Volume keys β€” page turning inside an Android WebView wrapper (in a regular browser the OS intercepts them)
  • ⌨️ Navigation: arrows, spacebar, Home/End, click zones on the sides
  • πŸ“„ Dynamic layout β€” text is split into pages according to screen size, font and margins
  • πŸ“ Position saving β€” reopening a book returns you to where you left off (anchored to text, not page numbers)
⁠Narrator (read aloud)
  • πŸ—£οΈ Text-to-speech β€” the narrator reads the book aloud using the built-in browser voices (Web Speech API, works offline)
  • πŸ“– Reads both pages of the spread, then flips the page with animation and sound β€” just like manual reading
  • 🎚️ Stress marks β€” a 3.2M-word Russian stress dictionary (RUAccent) marks the stressed vowel of every word, so the voice puts accents correctly («за́мок», not «замо́к»)
  • 🎭 Human-like pacing β€” separate pauses after sentences, paragraphs, chapter titles and poem stanzas; headings are read slower, like an announcer
  • 🧹 Clean speech β€” quotes and markup symbols are not read aloud, dashes become natural pauses
  • ⏯️ One button β€” the FAB button starts/stops narration when a book is open (in the library it still adds books)
  • πŸ“„ Auto-flip toggle β€” optionally stop at the end of each page instead of turning automatically
⁠Page-flip sounds
  • πŸ”Š Paper rustle on every page flip β€” a random sound from the sounds/ folder (no repeats in a row)
  • βž• Drop-in sounds β€” add more MP3/OGG/WAV files into sounds/, they are picked up automatically, no code changes
  • πŸ”ˆ Toggle in settings β€” sound can be turned off alongside the animation toggle
⁠Library
  • πŸ“š Card grid with covers, title, author and reading progress
  • βž• Adding books: button, Drag & Drop
  • πŸ“‚ Formats: TXT, EPUB, FB2
  • πŸ—‘οΈ Deleting books with confirmation
  • πŸ’Ύ Server-side storage β€” books live in the books/ folder, metadata in meta.json
⁠Bookmarks
  • πŸ”– Add/remove bookmarks with text preview
  • πŸ“‘ Side panel with the list, click to jump to the location
  • 🎯 Marker on the book edge β€” visual bookmark indicator
⁠Table of Contents
  • πŸ“ƒ Chapter navigation β€” jump to any chapter from the TOC panel
⁠Settings
  • πŸ”€ Font size, line height, horizontal & vertical margins
  • 🎨 7 themes: Paper, Sepia, Night, Forest, Sky, Ember (OLED), Amber (OLED)
  • πŸŒ™ Immersive mode β€” clicking the center hides all panels
  • πŸ”€ Hyphenation toggle
  • 🎞️ Page-flip animation and flip sound toggles
  • πŸ“„ Auto-flip during narration toggle

β πŸ“± Phone controls

  • Tap the screen edges β€” right/left edges flip forward/back (zones widen automatically on touch screens)
  • Swipe β€” swipe left to go forward, right to go back
  • Volume keys β€” in a regular browser the OS intercepts them, so flipping works inside an Android WebView wrapper. The wrapper can control the reader in three ways (implementation: js/reader.js, _bindVolumeKeys):
WayHow to do it in the wrapper
JS bridgeCallwindow.BookHavenNative.volumeUp() / .volumeDown() (e.g. via evaluateJavascript in onKeyDown)
EventDispatchwindow.dispatchEvent(new Event('volumeup')) / ('volumedown')
KeyForward akeydown with keyCode 175 (louder) / 174 (quieter) to the WebView

β πŸš€ Quick start

docker run -d --name bookhaven \
  -p 8080:8080 -p 8001:8001 \
  -v bookhaven-books:/app/books \
  -v bookhaven-data:/app/data \
  --restart unless-stopped \
  hiddenath/bookhaven:latest

Open http://localhost:8080⁠ β€” that's it. Books and settings live in named volumes and survive container recreation. Add a book via the UI (button / drag & drop) or:

docker cp "My book.fb2" bookhaven:/app/books/
⁠Local (no Docker)
# Start the server (static + API in one file)
python3 server.py

# Open in browser
http://127.0.0.1:8080

The server runs two processes in threads:

  • 8080 β€” static files (HTML/CSS/JS)
  • 8001 β€” API for saving state and books

Options:

python3 server.py          # both servers
python3 server.py --static # static only (8080)
python3 server.py --api    # API only (8001)
python3 server.py --quiet   # no request logging

β πŸ“ Project structure

book_reader/
β”œβ”€β”€ index.html          # Entry point, UI markup
β”œβ”€β”€ book.svg            # Logo (library header + favicon)
β”œβ”€β”€ server.py           # Single server: static + API
β”œβ”€β”€ css/
β”‚   β”œβ”€β”€ main.css        # Global styles, themes (CSS Custom Properties)
β”‚   β”œβ”€β”€ reader.css      # 3D reader scene, bookmarks
β”‚   └── library.css     # Library (card grid)
β”œβ”€β”€ js/
β”‚   β”œβ”€β”€ main.js         # Initialization, pagination, state, autosave
β”‚   β”œβ”€β”€ reader.js       # Reader: flip animation, drag, keyboard, volume keys
β”‚   β”œβ”€β”€ narrator.js     # Narrator: speech engines, pacing, stress marks
β”‚   β”œβ”€β”€ sounds.js       # Page-flip sounds (random pick from sounds/)
β”‚   β”œβ”€β”€ library.js      # Library: cards, import, deletion
β”‚   β”œβ”€β”€ bookmarks.js    # Bookmarks (side panel, marker on the book edge)
β”‚   β”œβ”€β”€ notes.js        # Footnotes: tooltip, jump to note, "back" button
β”‚   β”œβ”€β”€ parsers.js      # TXT/EPUB parsers (FB2 β€” server-side)
β”‚   β”œβ”€β”€ position.js     # Reading anchors (data-block-id)
β”‚   β”œβ”€β”€ toc.js          # Table of contents
β”‚   └── storage.js      # localStorage + server-side saving
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ epub.js         # epub.js (downloaded, BSD)
β”‚   β”œβ”€β”€ jszip.min.js    # JSZip β€” epub.js dependency
β”‚   └── stress_dict.json.gz # Russian stress dictionary (3.2M word forms, RUAccent)
β”œβ”€β”€ sounds/             # Page-flip sounds (MP3/OGG/WAV, picked up automatically)
β”œβ”€β”€ assets/             # Screenshots and GIF animations for the README
β”œβ”€β”€ books/              # Book catalog (created automatically)
β”‚   β”œβ”€β”€ <name>.fb2 / .txt / .epub   # Book file with its original name
β”‚   └── <name>.meta.json            # Metadata next to the book file
└── data/
    └── state.json      # Global settings (created automatically)

β πŸ”§ How it works

⁠Pagination

Text is split into pages in the browser: a hidden measurer computes the height of each paragraph under the current font settings, then paragraphs are laid out into pages. Each paragraph gets a stable identifier (data-block-id) tied to the book and its sequential number β€” so the reading position does not "drift" when settings change.

⁠Data storage

Books are stored as plain files in books/ β€” the original filename is kept, no subfolders. Metadata (progress, bookmarks, covers) is stored in <name>.meta.json next to the book.

WhatWhere
Books (originals)books/<name>.fb2 / .txt / .epub
Book metadata (progress, bookmarks)books/<name>.meta.json
Global settingslocalStorage + data/state.json
⁠API
MethodPathDescription
GET/booksList of books (metadata without text)
POST/booksSave a book (copies intobooks/)
GET/books/<id>/textBook text (FB2 parsed on the fly)
GET/books/<id>/metaBook metadata
POST/books/<id>/metaUpdate progress/bookmarks
GET/books/<id>/coverBook cover (from FB2<coverpage>)
GET/books/<id>/image/<name>Image from the book body (FB2<binary>)
DELETE/books/<id>Delete a book
GET/soundsList of page-flip sound files
POST/tts/stressMark up text with stress accents (U+0301) for the narrator

β πŸ“‹ Tech stack

  • Frontend: vanilla ES2022 JavaScript, CSS3 Custom Properties, CSS 3D transforms
  • Backend: Python http.server (no dependencies)
  • Libraries: epub.js + JSZip (bundled in /lib/)
  • TTS: Web Speech API (browser voices) + RUAccent stress dictionary (3.2M word forms)
  • Icons: inline SVG (Material Design paths)

β πŸ“„ License

Dual licensing:

  • πŸ†“ Personal use β€” free: read, install on your devices, modify for yourself, share with others for personal use.
  • πŸ’Ό Commercial use β€” paid license: selling, embedding into commercial products/services, monetized hosting, using the code in commercial projects β€” requires purchasing a license from the author.

Applies to the repository version dated September 1, 2026 and later.

Third-party libraries: epub.js (BSD-3-Clause), JSZip (MIT/GPLv3).

πŸ“š About the books in the screenshots: the demo uses free preview versions of books from LitRes⁠ (a Russian e-book store) and a public-domain art book. They are shown for demonstration purposes only and are not distributed with the project.

Tag summary

Content type

Image

Digest

sha256:b9290fadd…

Size

81.6 MB

Last updated

about 2 months ago

docker pull hiddenath/bookhaven